tags:

views:

302

answers:

9

I have a static javascript function that can take 1, 2 or 3 parameters:

function getData(id, parameters, callback) //parameters (associative array) and callback (function) are optional

I know I can always test if a given parameter is undefined, but how would I know if what was passed was the parameter or the callback?

What's the best way of doing this?


Examples of what could be passed in:

1:

getData('offers');

2:

var array = new Array();
array['type']='lalal';
getData('offers',array);

3:

var foo = function (){...}
getData('offers',foo);

4:

getData('offers',array,foo);
A: 

You can use the arguments object property inside the function.

Ryu
+4  A: 

Er - that would imply that you are invoking your function with arguments which aren't in the proper order... which I would not recommend.

I would recommend instead feeding an object to your function like so:

function getData( props ) {
    props = props || {};
    props.params = props.params || {};
    props.id = props.id || 1;
    props.callback = props.callback || function(){};
    alert( props.callback )
};

getData( {
    id: 3,
    callback: function(){ alert('hi'); }
} );

Benefits:

  • you don't have to account for argument order
  • you don't have to do type checking
  • it's easier to define default values because no type checking is necessary
  • less headaches. imagine if you added a fourth argument, you'd have to update your type checking every single time, and what if the fourth or consecutive are also functions?

Drawbacks:

  • time to refactor code

If you have no choice, you could use a function to detect whether an object is indeed a function ( see last example ).

Note: This is the proper way to detect a function:

function isFunction(obj) {
    return Object.prototype.toString.call(obj) === "[object Function]";
}

isFunction( function(){} )
meder
"this would work 99% of the time due to some ES bugs."Can you explain more? Why could it go wrong?
jd
I added the correct code to detect a function. I believe the bug is here: http://bugs.ecmascript.org/ticket/251
meder
I would highly suggest you feed just one object. If not, use CMS's method.
meder
Oh... damn... Just posted the same idea.
Arnis L.
+2  A: 

So use the typeof operator to determine if the second parameter is an Array or function.

This can give some suggestions: http://www.planetpdf.com/developer/article.asp?ContentID=testing_for_object_types_in_ja

I am not certain if this is work or homework, so I don't want to give you the answer at the moment, but the typeof will help you determine it.

James Black
+2  A: 

You should check type of received parameters. Maybe you should use arguments array since second parameter can sometimes be 'parameters' and sometimes 'callback' and naming it parameters might be misleading.

Kamil Szot
A: 

I think you want to use typeof() here:

function f(id, parameters, callback) {
  console.log(typeof(parameters)+" "+typeof(callback));
}

f("hi", {"a":"boo"}, f); //prints "object function"
f("hi", f, {"a":"boo"}); //prints "function object"
Mark Bessey
A: 

Are you saying you can have calls like these: getData(id, parameters); getData(id, callback)?

In this case you can't obviously rely on position and you have to rely on analysing the type: getType() and then if necessary getTypeName()

Check if the parameter in question is an array or a function.

DmitryK
+2  A: 

You can know how many arguments were passed to your function and you can check if your second argument is a function or not:

function getData (id, parameters, callback) {
  if (arguments.length == 2) { // if only two arguments were supplied
    if (Object.prototype.toString.call(parameters) == "[object Function]") {
      callback = parameters; 
    }
  }
  //...
}

You can also use the arguments object in this way:

function getData (/*id, parameters, callback*/) {
  var id = arguments[0], parameters, callback;

  if (arguments.length == 2) { // only two arguments supplied
    if (Object.prototype.toString.call(arguments[1]) == "[object Function]") {
      callback = arguments[1]; // if is a function, set as 'callback'
    } else {
      parameters = arguments[1]; // if not a function, set as 'parameters'
    }
  } else if (arguments.length == 3) { // three arguments supplied
      callback = arguments[1];
      parameters = arguments[2];
  }
  //...
}

If you are interested, give a look to this article by John Resig, about a technique to simulate method overloading on JavaScript.

CMS
A: 

Can you override the function? Will this not work:

function doSomething(id){}
function doSomething(id,parameters){}
function doSomething(id,parameters,callback){}
J.Hendrix
No, this won't work. You won't get any errors but Javascript will always use the latest function that you defined.
jd
Wow. I thought you were crazy. I just tested it. You are right. My world just changed a little bit for me. I think I have a lot of JavaScript to look at today to make sure I don't have anything like this in production. Thanks for the comment. I gave you a +1.
J.Hendrix
A: 

If your problem is only with function overloading (you need to check if 'parameters' parameter is 'parameters' and not 'callback'), i would recommend you don't bother about argument type and
use this approach. The idea is simple - use literal objects to combine your parameters:

function getData(id, opt){
    var data = voodooMagic(id, opt.parameters);
    if (opt.callback!=undefined)
      opt.callback.call(data);
    return data;         
}

getData(5, {parameters: "1,2,3", callback: 
    function(){for (i=0;i<=1;i--)alert("FAIL!");}
});
Arnis L.