If you don’t know this already, JavaScript functions which accept a single object (hash) which contains each of the properties are far better than functions which accept a number of individual parameters. The reason is that using a hash no longer requires your function’s API to depend on the presence of certain arguments or on their order, and it also means that updating the functions to support new arguments will have no negative impact on all other uses (and users) of it.
So, instead of functions like:
function concat(str1, str2){ return str1 + str2; } concat("This API", " is bad.");
You should create functions that work like this:
function concat(props){ return props.str1 + props.str2; } concat({str1:"This API", str2:" is better.});
Obviously, you’d want to make sure an object’s property exists before referencing it.
Why is this better?
Lets say that you want to modify concat() to support concatenating 3 or more strings. In a different scenario you may decide that a given function would work better with a different order of arguments since the last one may be optional. Your options for backwards support are limited and mostly bad. Plus, any further changes to the function require going back and updating every place where its called. You could create a new function which does almost the same thing but is slightly different, but that approach is even worse. Instead, if your function accepts a single object parameter, you just change the code to do this:
function concat(props){ var newStr = ""; newStr = props.str1 + props.str2; if (props.str3){ newStr += props.str3; } if (props.specialArg){ // do something else if this argument is present } } concat({str1:"This API", str2:" is better.", str3:" Indeed.", specialArg:"Blah blah."}); concat({str1:"This API", str2:" is better.}); // still works!
Taking this idea further
Defining default variables is usually handy since you don’t want to always pass the same things. Lets say you have a constructor which creates and returns instances of your custom objects:
function Dude(customProps){ var props = { firstName: "Jeffrey", lastName : "Lebowski" age : 50 } $.extend(props, customProps); return props; } var someOtherDude = Dude({age:60, lastName:"Sobchak", firstName:"Walter"});
We now created another dude with each of the properties reset to new values, but we could have also done this to create a younger, but original dude:
var youngerDude = Dude({age:20}); alert(youngerDude.age); // "20" alert(youngerDude.firstName); // "Jeffrey"
You may have noticed that I used jQuery’s $.extend() method to extend the default object with the custom one (passed as the argument to the constructor). This handy method can easily be written if you’d prefer not to use jQuery. A basic form of it is:
function extend(origObj, newObj){ for (prop in newObj){ origObj[prop] = newObj[prop]; } }
Applying this technique to other languages
While JSON notation (object literals) is specific to JavaScript, this technique can be applied to other languages as well, though it might appear slightly less elegant. For example, in PHP, you could just create functions which accept associated arrays (hashes):
<?php function concat($hash){ $str = ""; if (isset($hash['str1']){ $str += $hash['str1']; } if (isset($hash['str2']){ $str += $hash['str2']; } if (isset($hash['str3']){ $str += $hash['str3']; } return $str; } echo concat(array("str1"=>"First", "str2"=>"Second")); ?>
By the way, I’m not suggesting that you should create a concatenation function that looks or works like this. I’m using it here only to demonstrate the general idea.
More later.