在javascript中处理可选参数

Handling optional parameters in javascript

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

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

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

最好的方法是什么?

可以传递的示例:

1:

1
getData('offers');

2:

1
2
3
var array = new Array();
array['type']='lalal';
getData('offers',array);

三:

1
2
var foo = function (){...}
getData('offers',foo);

4:

1
getData('offers',array,foo);


You can know how many arguments were passed to your function and you can check if your second argument is a function or not:

1
2
3
4
5
6
7
8
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;
    }
  }
  //...
}

您也可以这样使用arguments对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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];
  }
  //...
}

如果您感兴趣,请看一下JohnResig撰写的这篇文章,它介绍了一种在JavaScript上模拟方法重载的技术。


这意味着您正在用不正确顺序的参数调用函数…我不推荐。

我建议您将一个对象输入到您的函数中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
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'); }
} );

效益:

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

缺点:

  • 重构代码的时间

如果您没有选择,您可以使用一个函数来检测一个对象是否确实是一个函数(参见上一个示例)。

注意:这是检测功能的正确方法:

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

isFunction( function(){} )


我建议你使用Arguejs。

您可以这样键入函数:

1
2
3
4
5
6
function getData(){
  arguments = __({id: String, parameters: [Object], callback: [Function]})

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

我从你的例子中考虑过,你希望你的id参数是一个字符串,对吗?现在,getData需要String id并接受Object parametersFunction callback的期权。您发布的所有用例都将按预期工作。


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

我创建了一个实用程序,它允许我强输入参数,并允许它们是可选的。您基本上将函数包装在代理中。如果跳过一个参数,它是未定义的。如果您旁边有多个具有相同类型的可选参数,这可能会变得很奇怪。(可以选择传递函数而不是类型来执行自定义参数检查,以及为每个参数指定默认值。)

实现如下所示:

1
2
3
4
5
6
function displayOverlay(/*message, timeout, callback*/) {
  return arrangeArgs(arguments, String, Number, Function,
    function(message, timeout, callback) {
      /* ... your code ... */
    });
};

为了清楚起见,下面是正在发生的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

下面是实用程序函数,其中有些注释是从存储库中复制的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 ****** 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();
}

您应该检查接收参数的类型。也许您应该使用arguments数组,因为第二个参数有时可能是"参数",有时是"回调",并且命名它的参数可能会产生误导。


因此,使用typeof运算符来确定第二个参数是数组还是函数。

这可以给出一些建议:http://www.planetpdf.com/developer/article.asp?contentID=测试_-ja中的_-object_-types_

我不确定这是工作还是家庭作业,所以我现在不想给你答案,但类型将帮助你确定它。


Are you saying you can have calls like these:
getData(id, parameters);
getData(id, callback)?

In this case you can't obviously rely on position and you have to rely on analysing the type:
getType() and then if necessary getTypeName()

Check if the parameter in question is an array or a function.


您可以在函数内使用arguments对象属性。


我想这可能是一个不言自明的例子:

1
2
3
4
5
6
7
8
function clickOn(elem /*bubble, cancelable*/) {
    var bubble =     (arguments.length > 1)  ? arguments[1] : true;
    var cancelable = (arguments.length == 3) ? arguments[2] : true;

    var cle = document.createEvent("MouseEvent");
    cle.initEvent("click", bubble, cancelable);
    elem.dispatchEvent(cle);
}

如果您的问题只是函数重载(您需要检查"parameters"参数是否为"parameters",而不是"callback"),我建议您不要担心参数类型和使用这种方法。这个想法很简单-使用文本对象组合参数:

1
2
3
4
5
6
7
8
9
10
function getData(id, opt){
    var data = voodooMagic(id, opt.parameters);
    if (opt.callback!=undefined)
      opt.callback.call(data);
    return data;        
}

getData(5, {parameters:"1,2,3", callback:
    function(){for (i=0;i<=1;i--)alert("FAIL!");}
});

我想您要在这里使用typeof():

1
2
3
4
5
6
7
8
function f(id, parameters, callback) {
  console.log(typeof(parameters)+""+typeof(callback));
}

f("hi", {"a":"boo
<p><center>[wp_ad_camp_3]</center></p><hr><P>你能覆盖这个功能吗?这不管用吗?</P>[cc lang="
javascript"]function doSomething(id){}
function doSomething(id,parameters){}
function doSomething(id,parameters,callback){}