在JavaScript中处理可选参数

我有一个静态的JavaScript函数,可以采取1,2或3个参数:

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

我知道我总是可以testing一个给定的参数是否是未定义的,但是如何知道传递的是参数还是callback?

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


什么可以通过的例子:

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

你可以知道有多less个parameter passing给你的函数,你可以检查你的第二个参数是否是一个函数:

 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对象:

 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上模拟方法重载的技术。

呃 – 这意味着你正在用不正确的顺序来调用你的函数,这是我不推荐的。

我会build议,而不是像这样喂对象:

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

优点:

  • 你不必考虑论证顺序
  • 你不必做types检查
  • 定义默认值比较容易,因为不需要types检查
  • 更less的头痛。 想象一下,如果你添加了第四个参数,你必须每次都更新你的types,如果第四个或者连续也是函数呢?

缺点:

  • 时间来重构代码

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

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

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

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

这可以给一些build议: http : //www.planetpdf.com/developer/article.asp? ContentID= testing_for_object_types_in_ja

我不确定这是工作还是家庭作业,所以我现在不想给你答案,但是这种types可以帮助你确定。

你应该检查收到参数的types。 也许你应该使用arguments数组,因为第二个参数有时可能是'参数',有时'callback',并命名参数可能会误导。

你可以在函数内部使用arguments对象的属性 。

你是说你可以有这样的调用:getData(id,parameters); getData(id,callback)?

在这种情况下,你不能明显依赖于位置,你必须依靠分析types:getType(),然后如果需要getTypeName()

检查所讨论的参数是一个数组还是一个函数。

我知道这是一个相当古老的问题,但我最近处理了这个问题。 让我知道你对这个解决scheme的看法。

我创build了一个实用程序,让我强烈types的参数,让他们是可选的。 你基本上包装你的function在代理。 如果你跳过一个参数,它是未定义的 。 如果您有多个相同types的可选参数,则可能会变得古怪。 (可以select传递函数而不是types来执行自定义参数检查,也可以为每个参数指定默认值。)

这是实现的样子:

 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

以下是从存储库复制的一些注释的实用程序function:

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

我build议你使用ArgueJS 。

你可以这样input你的函数:

 function getData(){ arguments = __({id: String, parameters: [Object], callback: [Function]}) // and now access your arguments by arguments.id, // arguments.parameters and arguments.callback } 

我认为你的例子,你想你的id参数是一个string,对不对? 现在, getData需要一个String id ,并接受optionals Object parametersFunction callback 。 您发布的所有用例将按预期工作。

我想你想在这里使用typeof():

 function f(id, parameters, callback) { console.log(typeof(parameters)+" "+typeof(callback)); } f("hi", {"a":"boo"}, f); //prints "object function" f("hi", f, {"a":"boo"}); //prints "function object" 

如果你的问题只是与函数重载(你需要检查'参数'参数是'参数',而不是'callback'),我会build议你不要担心参数types和
使用这种方法 。 这个想法很简单 – 使用文字对象来组合你的参数:

 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!");} }); 

我想这可能是一个自我解释的例子:

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

你可以覆盖function? 这是行不通的:

 function doSomething(id){} function doSomething(id,parameters){} function doSomething(id,parameters,callback){}