简体   繁体   中英

Changing function parameters in javascript?

Say I have a javascript function like below:

myFunction: function(data, callback){
  //Do stuff
}

Say I now want to add another parameter to this function. Lets call if flag . I also don't want to change function calls all over the place. It seems to me that most javascript functions include their callback as the last parameter. Does this mean that I should alter the function like so:

myFunction: function(data, flag, callback){
  //Do stuff
}

or is it better to alter it like so:

myFunction: function(data, callback, flag){
  //Do stuff
}

The first method allows the callback to be at the end, but if I go this route I will need to add something to handle all old calls that pass the call back as the second parameter. Something like:

if (_.isFunction(flag)) {
  onComplete = flag;
  return retAll = false;
}

The second method looks a little strange and would require me to do something like this in order to handle old calls that do not include a third parameter:

if (flag == null) {
  flag = false;
}

Is there a generally accepted way to handle the order of parameters in situations like this? Also, should I just track down all calls to the function and change what parameters are being passed in instead of handling it in the function itself?

You could use the arguments Array, but I recommend putting optional parameters last. Additionally you can use typeof to determine your argument type.

You could use arguments .

This is a simple, naive example:

myFunction: function() {
  var data = arguments[0],
      flag = (_.isBoolean(arguments[1])) ? arguments[1] : arguments[2],
      cb   = (_.isFunction(arguments[2])) ? arguments[2] : arguments[1];

  // stuff
}
  1. You can null for 'no value for this parameter' if you're not sure which parameters will be needed.
  2. You can rename the original function to myFunctionFlag(data, flag, callback) and create a new function myFunction(data, callback) which just calls myFunctionFlag with a default value for flag . This is especially useful when taking into account backwards compatibility but code can get messy over time.
  3. You can use arguments like shown in other answers.

Just an option to perhaps get the best of both worlds. Leave all your existing code that calls the function alone, but still add the third parameter as flag to the "myFunction" function:

myFunction: function(data, callback, flag){
  //Do stuff
}

Define a new function that simply calls the original function for the places where the flag IS going to be passed:

myFunctionSane: function(data, flag, callback) {
  myFunction(data, callback, flag);
}

You still need to add the check for the flag in the original "myFunction", but any NEW places where you want to call with all three parameters, you can call "myFunctionSane" with the callback as the third argument.

EDIT: Throwing another idea out would be to leave the original signature completely in tact and pass the original "data" element for the old calls (no changes needed), but pass a json object as the data element for the new calls such as:

myFunction( { "data": myData, "flag": myFlag }, myCallback );

In your function, you would just have to test if the element "data.data" is defined to know whether or not a legacy function call is being made or a new call that passes JSON.

To give code for the decorater function I suggested in the op post comment

//http://jsfiddle.net/NsyQT/
var routeEnd = function(fn, len, def) {
    len = len || fn.length;
    return function() {
        var args = Array.prototype.slice.call(arguments);
        if(args.length < len) {
            var last = args.pop();//keep last val in place
            for (var i = 0, l = len-args.length-1; i < l; i++) {
                args.push(def);//default value (undefined)
            }
            args.push(last);
        }
        return fn.apply(this, args);
    }
}

Usage

var myFunction = routeEnd(function (data, flag, callback) {
    console.log("flag: %s", flag);
    console.log("cb: %s", callback);
});

myFunction({data: true}, "flagged", function () {});
myFunction({data: true}, function () {});

If going through all of your code and changing the call signature is possible, that is not a bad idea. Sometimes there is code that is outside of your control, though, and you want to preserve the old signature but support a new one as well.

Methods with multiple signatures/optional arguments are quite common in libraries like jQuery, for example. Here is a minimal implementation of an argument parser that switches just based on argument types.

This is combined with a defaults object to ensure all values get initialized. If not an error is thrown. This is preferable to requiring the method to always be called using object syntax, so you can do myFun(a, b, c) instead of myFun({foo: a, bar: b, baz: c})

var myFunSig = typeSignature('data flag callback', '* boolean function');
function myFun() {
    var args = myFunSig(arguments, {
            flag: false // set default
        });

    // all arguments/defaults are properties of 'args'
}

function typeSignature(sigStr, nameStr) {
    var types = sigStr.split(' ');
    var names = nameStr.split(' ');
    return function(args, defaults) {
        var result = $.extend({}, defaults),
            argIdx = 0;
        for(var i = 0, len = types.length; i < len; i++) {
            if(types[i] === '*' || typeof(args[argIdx]) === types[i]) {
                result[names[i]] = args[argIdx++];
            }
        }
        if(argIdx < args.length) { throw 'invalid call'; }
        for(var i = 0, len = names.length; i < len; i++) {
            if(!(names[i] in result)) { throw 'invalid call'; }
        }
        return result;
    };
}

Obviously this enables combinations of certain types of method signatures, specifically ones with optional arguments of identifiable types. As things get more complicated I guess you might have to fall-back on custom code for each method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM