在javascript中处理可选参数

IT技术 javascript
2021-02-17 23:42:35

我有一个静态 javascript 函数,可以接受 1、2 或 3 个参数:

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

我知道我总是可以测试给定的参数是否未定义,但是我怎么知道传递的是参数还是回调?

这样做的最佳方法是什么?


可以传入的示例:

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);
6个回答

你可以知道有多少参数被传递给你的函数,你可以检查你的第二个参数是否是一个函数:

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

您还可以以这种方式使用参数对象:

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
      parameters = arguments[1];
      callback = arguments[2];
  }
  //...
}

如果您有兴趣,请查看John Resig 的这篇文章文章介绍了一种在 JavaScript 上模拟方法重载的技术。

@TomerCagan 我认为这是一个偏好问题(ish)。上有下的主题的一些优秀的答案/评论这个问题。
2021-05-06 23:42:35
为什么使用 Object.prototype.toString.call(parameters) == "[object Function]" 而不是 typeof(parameters) === 'function'?它们之间有什么重要的区别吗?PS你提到的文章似乎使用了后者
2021-05-09 23:42:35

呃 - 这意味着你正在使用不正确顺序的参数调用你的函数......我不建议这样做。

我建议改为将对象提供给您的函数,如下所示:

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

好处:

  • 你不必考虑参数顺序
  • 你不必做类型检查
  • 定义默认值更容易,因为不需要类型检查
  • 少头痛。想象一下,如果你添加了第四个参数,你必须每次都更新你的类型检查,如果第四个或连续的也是函数怎么办?

缺点:

  • 是时候重构代码了

如果你别无选择,你可以使用一个函数来检测一个对象是否确实是一个函数(见最后一个例子)。

注意:这是检测函数的正确方法:

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

isFunction( function(){} )
我强烈建议你只喂一个物体。如果没有,请使用 CMS 的方法。
2021-04-24 23:42:35
“由于一些 ES 错误,这将在 99% 的情况下工作。” 你能解释更多吗?为什么会出错?
2021-04-26 23:42:35
哦......该死......刚刚发布了同样的想法。
2021-04-27 23:42:35
另一个可能的缺点是缺乏智能感知。我不认为这有什么大不了的,但应该注意。
2021-05-06 23:42:35
我添加了正确的代码来检测函数。我相信错误在这里:bugs.ecmascript.org/ticket/251
2021-05-16 23:42:35

您应该检查接收到的参数的类型。也许您应该使用arguments数组,因为第二个参数有时可以是“参数”,有时是“回调”,将其命名为参数可能会产生误导。

我知道这是一个很老的问题,但我最近处理了这个问题。让我知道您对此解决方案的看法。

我创建了一个实用程序,它允许我强类型参数并让它们是可选的。您基本上将您的功能包装在代理中。如果你跳过一个参数,它是undefined如果您有多个相同类型的可选参数并排在一起,这可能会变得古怪(有一些选项可以传递函数而不是类型来进行自定义参数检查,以及为每个参数指定默认值。)

这是实现的样子:

function displayOverlay(/*message, timeout, callback*/) {
  return arrangeArgs(arguments, String, Number, Function, 
    function(message, timeout, callback) {
      /* ... your code ... */
    });
};

为清楚起见,这里是正在发生的事情:

function displayOverlay(/*message, timeout, callback*/) {
  //arrangeArgs is the proxy
  return arrangeArgs(
           //first pass in the original arguments
           arguments, 
           //then pass in the type for each argument
           String,  Number,  Function, 
           //lastly, pass in your function and the proxy will do the rest!
           function(message, timeout, callback) {

             //debug output of each argument to verify it's working
             console.log("message", message, "timeout", timeout, "callback", callback);

             /* ... your code ... */

           }
         );
};

您可以在此处查看我的 GitHub 存储库中的arrangeArgs 代理代码:

https://github.com/joelvh/Sysmo.js/blob/master/sysmo.js

这是从存储库复制的一些注释的实用程序函数:

/*
 ****** Overview ******
 * 
 * Strongly type a function's arguments to allow for any arguments to be optional.
 * 
 * Other resources:
 * http://ejohn.org/blog/javascript-method-overloading/
 * 
 ****** Example implementation ******
 * 
 * //all args are optional... will display overlay with default settings
 * var displayOverlay = function() {
 *   return Sysmo.optionalArgs(arguments, 
 *            String, [Number, false, 0], Function, 
 *            function(message, timeout, callback) {
 *              var overlay = new Overlay(message);
 *              overlay.timeout = timeout;
 *              overlay.display({onDisplayed: callback});
 *            });
 * }
 * 
 ****** Example function call ******
 * 
 * //the window.alert() function is the callback, message and timeout are not defined.
 * displayOverlay(alert);
 * 
 * //displays the overlay after 500 miliseconds, then alerts... message is not defined.
 * displayOverlay(500, alert);
 * 
 ****** Setup ******
 * 
 * arguments = the original arguments to the function defined in your javascript API.
 * config = describe the argument type
 *  - Class - specify the type (e.g. String, Number, Function, Array) 
 *  - [Class/function, boolean, default] - pass an array where the first value is a class or a function...
 *                                         The "boolean" indicates if the first value should be treated as a function.
 *                                         The "default" is an optional default value to use instead of undefined.
 * 
 */
arrangeArgs: function (/* arguments, config1 [, config2] , callback */) {
  //config format: [String, false, ''], [Number, false, 0], [Function, false, function(){}]
  //config doesn't need a default value.
  //config can also be classes instead of an array if not required and no default value.

  var configs = Sysmo.makeArray(arguments),
      values = Sysmo.makeArray(configs.shift()),
      callback = configs.pop(),
      args = [],
      done = function() {
        //add the proper number of arguments before adding remaining values
        if (!args.length) {
          args = Array(configs.length);
        }
        //fire callback with args and remaining values concatenated
        return callback.apply(null, args.concat(values));
      };

  //if there are not values to process, just fire callback
  if (!values.length) {
    return done();
  }

  //loop through configs to create more easily readable objects
  for (var i = 0; i < configs.length; i++) {

    var config = configs[i];

    //make sure there's a value
    if (values.length) {

      //type or validator function
      var fn = config[0] || config,
          //if config[1] is true, use fn as validator, 
          //otherwise create a validator from a closure to preserve fn for later use
          validate = (config[1]) ? fn : function(value) {
            return value.constructor === fn;
          };

      //see if arg value matches config
      if (validate(values[0])) {
        args.push(values.shift());
        continue;
      }
    }

    //add a default value if there is no value in the original args
    //or if the type didn't match
    args.push(config[2]);
  }

  return done();
}

我建议你使用ArgueJS

你可以这样输入你的函数:

function getData(){
  arguments = __({id: String, parameters: [Object], callback: [Function]})

  // and now access your arguments by arguments.id,
  //          arguments.parameters and arguments.callback
}

我通过你的例子认为你希望你的id参数是一个字符串,对吧?现在,getData需要 aString id并且正在接受可选Object parametersFunction callback您发布的所有用例都将按预期工作。