使用可选参数和回调来干掉JavaScript函数

jjm*_*jjm 7 javascript node.js

在Node.js中,由于多种原因,习惯/推荐将回调函数作为最后一个参数传递给函数.可能还有一个或多个可选参数,我们希望在回调之前传递这些参数.你最终会看到很多非常重复的代码,例如

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

添加其他可选参数意味着添加额外的检查,当然:

 // 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 = {};
     }
     // ...
 }
Run Code Online (Sandbox Code Playgroud)

编辑这种模式似乎 标准库也是如此.

我可以想到一些干扰它的hacky方法,但我想知道是否有人有一个特别优雅或通用的解决方案来获得绑定到正确位置参数的参数.

bas*_*kum 3

我想到的一件事是方法重载。从技术上讲,JavaScript 不支持这一点,但有一些方法可以实现类似的功能。John Resig 在他的博客中有一篇关于它的文章:http://ejohn.org/blog/javascript-method-overloading/

另外,这是我的实现,与 John Resig 的非常相似,并且深受其启发:

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";
            }
        };
    };
}());
Run Code Online (Sandbox Code Playgroud)

这使您能够多次定义相同的函数,每次使用不同数量的参数:

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

createFunction(window, "doSomething", function (arg1, callback) {
    doSomething(arg1, null, callback);
});
Run Code Online (Sandbox Code Playgroud)

这段代码定义了一个全局函数doSomething,一次有三个参数,一次有两个参数。正如您所看到的,此方法的第一个缺点是您必须提供一个附加函数的对象,您不能只是说“在此处定义一个函数”。此外,函数声明比以前稍微复杂一些。但是您现在可以使用不同数量的参数调用函数并获得正确的结果,而无需使用重复if..else结构:

doSomething("he", function(){});         //he / null / function(){}
doSomething("he", "ho", function(){});   //he / ho / function(){}
Run Code Online (Sandbox Code Playgroud)

到目前为止,只有参数的数量很重要,但我可以考虑扩展它,以对不同的数据类型做出反应,以便还可以区分以下内容:

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

doSomething({anObject: "as opt1"}, function(){});
doSomething("a string as opt2", function(){});
Run Code Online (Sandbox Code Playgroud)

然而,这可能不是最好的方法,但在某些情况下可能很方便。对于许多可选参数,我个人喜欢 Pumbaa80 的答案,即将这些选项放入一个必需的对象参数中。