【问题标题】:Changing function parameters in javascript?在javascript中更改函数参数?
【发布时间】:2013-10-17 23:23:56
【问题描述】:

假设我有一个如下的 javascript 函数:

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

假设我现在想为这个函数添加另一个参数。让我们打电话给flag。我也不想到处更改函数调用。在我看来,大多数 javascript 函数都将它们的回调作为最后一个参数。这是否意味着我应该像这样改变函数:

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

或者像这样改变它更好:

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

第一种方法允许回调在最后,但如果我走这条路线,我需要添加一些东西来处理所有将回调作为第二个参数传回的旧调用。比如:

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

第二种方法看起来有点奇怪,需要我做这样的事情来处理不包含第三个参数的旧调用:

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

在这种情况下,是否有一种普遍接受的方式来处理参数的顺序?另外,我是否应该只跟踪对函数的所有调用并更改传入的参数而不是在函数本身中处理它?

【问题讨论】:

  • 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? - 我会这样做。我认为更好的做法是让它接受一个参数 - 一个对象。然后检查属性是否存在(如果缺少必需的则抛出错误)
  • 当您想要保持这种向后兼容性时,进行类型检查是一般模式。如果您要更新其中许多方法,您可能希望使用路由器来处理最后 2 个参数
  • 更好?这就是价值判断。我会更改您的所有函数以采用作为对象的单个参数。然后,您可以为data 和flag 和callback 创建属性,以及您想要的任何其他内容。那你就再也不会遇到这种情况了……当然,就预期而言,它也会相当不透明。
  • @Mike-McCauhan - 我是这种方法的忠实拥护者,我在下面的回答有一个基本相同的修改。

标签: javascript


【解决方案1】:

只是一个可能获得两全其美的选择。保留所有调用该函数的现有代码,但仍将第三个参数作为标志添加到“myFunction”函数:

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

定义一个新函数,该函数简单地为将要传递标志的位置调用原始函数:

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

您仍然需要在原始“myFunction”中添加对标志的检查,但是您想要使用所有三个参数调用的任何新位置,您都可以使用回调作为第三个参数调用“myFunctionSane”。

编辑:抛出另一个想法是完全保留原始签名并为旧调用传递原始“数据”元素(不需要更改),但传递一个 json 对象作为新调用的数据元素如:

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

在您的函数中,您只需要测试是否定义了元素“data.data”,以了解是否正在进行旧函数调用或传递 JSON 的新调用。

【讨论】:

    【解决方案2】:

    你可以使用arguments。

    这是一个简单、幼稚的例子:

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

    【讨论】:

      【解决方案3】:
      1. 如果您不确定需要哪些参数,可以null 表示“此参数没有值”。
      2. 您可以将原始函数重命名为myFunctionFlag(data, flag, callback),并创建一个新函数myFunction(data, callback),它只调用myFunctionFlag,默认值为flag。这在考虑向后兼容性时特别有用,但代码会随着时间的推移变得混乱。
      3. 您可以使用arguments,如其​​他答案所示。

      【讨论】:

        【解决方案4】:

        您可以使用arguments 数组,但我建议将可选参数放在最后。此外,您可以使用typeof 来确定您的参数类型。

        【讨论】:

          【解决方案5】:

          为我在 op post 评论中建议的装饰器函数提供代码

          //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);
              }
          }
          

          用法

          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 () {});
          

          【讨论】:

            【解决方案6】:

            如果检查所有代码并更改调用签名是可能的,那不是一个坏主意。不过,有时有些代码超出了您的控制范围,您希望保留旧签名但也支持新签名。

            例如,在 jQuery 等库中,具有多个签名/可选参数的方法非常常见。这是一个参数解析器的最小实现,它仅根据参数类型进行切换。

            这与默认对象相结合,以确保所有值都被初始化。如果没有则抛出错误。这比要求始终使用对象语法调用方法更好,因此您可以使用myFun(a, b, c) 而不是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;
                };
            }
            

            显然,这可以组合某些类型的方法签名,特别是带有可识别类型的可选参数的方法签名。随着事情变得越来越复杂,我猜您可能不得不为每种方法使用自定义代码。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-11-20
              • 1970-01-01
              • 1970-01-01
              • 2015-11-27
              • 1970-01-01
              相关资源
              最近更新 更多