DRYing up JavaScript functions taking optional arguments and a callback

In Node.js it is, for several reasons, customary/recomended to pass a callback to a function as the last argument. There may also be one or more optional arguments, which we would like to pass before the callback. You end up with seeing a lot of very repetitive code, such as

// receiveMessages([options], [callback])
function receiveMessages(options, callback) {
   if(typeof options === 'function'){
      callback = options;
      options = {}; // or some other sensible default
   }
   //...
 }

Adding additional optional arguments means adding an additional check, of course:

 // through([dest], [options], [callback])
 function through(dest, options, callback) {
     if(typeof dest === 'function'){
        callback = dest;
        dest = noop();
        options = {};
     }else if(typeof options === 'function'){
        callback = options;
        options = {};
     }
     // ...
 }

Edit This pattern appears all over the standard library as well.

I can think of a few hacky ways of DRYing this up, but I'm wondering if anyone has an especially elegant or generic solution to get the arguments bound to the correct positional parameters.


One thing that comes to my mind is method overloading. Technically this is not supported in JavaScript but there are some way to realize something like this. John Resig has an article about it in his blog: http://ejohn.org/blog/javascript-method-overloading/

In addition, here is my implementation, that is very similar to John Resig's and highly inspiried by it:

var createFunction = (function(){
    var store = {};

    return function(that, name, func) {
        var scope, funcs, match;

        scope = store[that] || (store[that] = {});
        funcs = scope[name] || (scope[name] = {});

        funcs[func.length] = func;

        that[name] = function(){
            match = funcs[arguments.length];
            if (match !== undefined) {
                return match.apply(that, arguments);
            } else {
                throw "no function with " + arguments.length + " arguments defined";
            }
        };
    };
}());

This enables you to define the same function several times with a different number of arguments each time:

createFunction(window, "doSomething", function (arg1, arg2, callback) {
    console.log(arg1 + " / " + arg2 + " / " + callback);
});

createFunction(window, "doSomething", function (arg1, callback) {
    doSomething(arg1, null, callback);
});

This piece of code defines a global function doSomething , one time with three and one time with two arguments. As you see, the first downside of this method is that you have to provide an object to which the functions are being attached, you cannot just say "define a function right here". Also, the function declarations are a little bit more complicated then before. But you can now call your function with a different number of arguments and getting the right result without the use of repeating if..else structures:

doSomething("he", function(){});         //he / null / function(){}
doSomething("he", "ho", function(){});   //he / ho / function(){}

So far, only the number of arguments matter but I can think of extending this, to also react on different data types, so that one can also differentiate between the following:

function doSomething(opt1, opt2, callback){
    //some code
}

doSomething({anObject: "as opt1"}, function(){});
doSomething("a string as opt2", function(){});

Nevertheless, this is probably not the best way to go but it can be convinient in certain circumstances. For a lot of optional arguments, I personally like Pumbaa80's answer of putting those options in one required object-argument.


In Node.js it is, for several reasons, customary/recomended to pass a callback to a function as the last argument

The only reason I can think of is forcing the developers to provide the other arguments.

For example, function (success, error) would result in sloppy programming, since lazy coders would simply omit the error callback. That's why you commonly see function (error, success) .

That being said, the convention above is great for mandatory arguments. If you want optional arguments, just don't do that. One way to handle this scenario is the following scheme:

function (required1, required2, ..., callback, options)
// or
function (required1, required2, ..., options, callback)

where each optional argument may or may not be provided as an attribute of options .

Edit : actually, this is used in some node libraries, eg http://nodejs.org/api/fs.html#fs_fs_appendfile_filename_data_options_callback

链接地址: http://www.djcxy.com/p/72480.html

上一篇: 定义SOMETHING(1 << 0)

下一篇: DRYing JavaScript函数采取可选参数和回调