Sending objects as parameters - good or bad?
One of the differences I keep seeing in functions and methods lately is that people seem to go away from the strict pattern of expecting parameters in a certain form. If you look back some years you might remember functions like the following:
function doLayerFloat(id,start,end,direction,speed,fade,whatelse,Iforgot){
...
}
This, at least to me is rather annoying as I am terrible at remembering the order the parameters need to be sent in (conditioning in PHP confusion probably). The other issue I kept seeing with this was that if you didn’t want to provide some of the parameters but one of the last ones you had to send empty strings or even null values:
doLayerFloat('myDIV',0,30,null,null,true,null,null){
...
}
// or
doLayerFloat('myDIV',0,30,'','','','',''){
...
}
This is both confusing and convoluted. Other scripts I have seen work around the issue by using the arguments array, which at least allows a flexible amount of arguments to be sent.
function doLayerFloat(id){
var args = arguments;
for(var i = 1; i < args.length; i++) {
...
}
}
However, my favourite these days is functions that actually take a few defined parameters (or a single one), allow for it to be several things and allow you to send an object as the properties:
function doLayerFloat(id, props){
var elm = typeof id !== 'string' ? id : document.getElementById(id);
for (var i in props){
...
}
}
This allows the id to be either an element reference or a string and takes an object as the second parameter in which I can define the order and amount any which way I like (provided the method then tests for each of them and their correct values):
doLayerFloat('x',{start:20,end:30,fade:true});
// or
doLayerFloat(myElm,{fade:true,direction:'right'});
This also allows you to define default values should the properties not be set, something you can do in PHP but not in JavaScript. In PHP, this works:
function foo($bar=2,$baz='foo'){
}
In JavaScript that can’t be done, but if you use an object you can predefine if the properties are not set:
function doLayerFloat(id, props){
var elm = typeof id !== 'string' ? id : document.getElementById(id);
props = props || {};
props.start = props.start || 100;
props.fade = props.fade || false;
}
Do you agree? Or is the object literal syntax still too tricky?
Technorati Tags: javascript, syntax, parameters, object literal, objectliteral, arguments, codestyle, webdevtrick
November 7th, 2007 at 12:41 am
I agree, but would add that any required parameter, should never be in the param object. For example you might change the addListener implementation in YUI to:
addListener: function(el, sType, fn, params);
and in the params you could define whether to pass through an object, override the scope, or use the capture phase. However, since the element, event type, and callback function are required (capture will be assumed false if not provided), then they should be explicitly required by the function definition.
also, if you like default values for the missing elements in the params object, you might try:
params.item1 = params.item1 || defaultValue1;
params.item2 = params.item2 || defaultValue2;
params.item3 = params.item3 || defaultValue3;
November 7th, 2007 at 12:54 am
Agreed.
I find that sending a mega list of parameters is a quick and dirty way of doing it 'for the time being' in a small scale, however, anything further then that is certainly a maintenance headache.
November 7th, 2007 at 1:06 am
Agree. It was one of those "ooooh" moments when I found this in YUI at release. In terms of "just jumping in", throwing a DOM id, an object reference or even an array of either makes things easier and faster to use.
Not thought of passing an object literal as another argument though. Nice idea.
November 7th, 2007 at 1:37 am
I actually tend to mix and match.
A function/method usually has certain required variables, I always make those actual parameters.
Then when the function starts to grow and need optional pieces of information, I usually just add them on at the end. That is until I reach 2, 3 or more optionals. Then I usually make all the optionals an object.
Also I sometimes ask myself when needing a tenth option to a function if maybe my code should be a bit different, maybe broken up into more functions or something.
I just do what feels natural and things tend to work out well in the end :)
Of course this is all when I develop code that is mostly for myself.
When developing code that others will be calling, I tend to be much more strict to passing optionals as an object or breaking things into different functions.
November 7th, 2007 at 1:57 am
oOops “invalid assignment left-hand side”, typeof id === "string"
Even if it looks tricky because of the OR syntax, it's pretty clear and easy to understand. The strength of that kind of object params is that you can more easily save them as global config and reuse them as often as you need, the alternative that involves Function.apply *is* tricky.
November 7th, 2007 at 3:04 am
I completely agree. Couldn't live without it. I find it especialy nice for passing in callback functions to override certain behaviours, e.g. for map applications: have a standard behavior defined inside your function, but override it from anywhere if you need to. The only thing im still missing is a decent way to document it, so my Eclipse autocompletion recognizes the possible properties for the literal, as it does with normal parameters.
November 7th, 2007 at 8:14 am
@Yoah, darn this is Wordpress changing the code when rendering it :-(
November 7th, 2007 at 10:40 am
Named parameters (as opposed to positional parameters) have been in use in many other languages (perl and python come to mind) for a long time. It helps a lot when you have many optional parameters. The downside, of course, is that you end up having to throw exceptions if you don't get a parameter that you wanted. That's basically moving to run time the kind of error checking that can be done at compile time.
Javascript isn't a compiled language, so this probably isn't much of an issue.
November 7th, 2007 at 2:09 pm
Another option is to use query strings which are parsed into variables. I'm not sure it really has any real advantages but they can be easier to work with in some situations.
November 7th, 2007 at 7:58 pm
Definitely agree. Theres a way to support both kinds as well, or to be backward compatible, for example to replace a multi-arg function with one that takes a single object literal:
function foo() {
if (arguments.length > 1) {
doFoo.apply(this, arguments);
} else {
var args = arguments[0]
doFoo(args.a1, args.a2, args.a3, args.a4, args.a5);
}
}
function doFoo(a1, a2, a3, a4, a5) {
}
November 9th, 2007 at 12:31 pm
Let us be honest, now: this is basically a bodge to get around JS’s lack of named parameters and the awkwardness of dealing with a variable length parameter list. However, since we need a bodge in this area, this is the one I normally use…
November 13th, 2007 at 11:08 pm
Absolutely agreed, with the caveat (mentioned by others) that any required attribute should be an actual parameter. The “cfg” object (as I almost always name it) is for optional arguments.