有没有更好的方法来在Javascript中做可选的函数参数?
我一直在Javascript中处理可选参数,如下所示:
function myFunc(requiredArg, optionalArg){ optionalArg = optionalArg || 'defaultValue'; //do stuff }
有没有更好的方法来做到这一点?
有没有使用||
像那样会失败?
你的逻辑失败,如果optionalArg被传递,但评估为假 – 试试这个作为替代
if (typeof optionalArg === 'undefined') { optionalArg = 'default'; }
或者另一种成语:
optionalArg = (typeof optionalArg === 'undefined') ? 'default' : optionalArg;
用最习惯的方式向你传达意图!
我觉得这是最简单,最可读的方式:
if (typeof myVariable === 'undefined') { myVariable = 'default'; } //use myVariable here
保罗·狄克逊(Paul Dixon)的回答(在我看来)比这个更不可读,但归结为偏好。
insin的答案是更先进的,但更大的function更有用!
编辑11/17/2013 9:33 pm:我已经创build了一个Node.js包,使得更容易“重载”函数(方法)称为参数 。
在ECMAScript 2015(又名“ES6”)中,您可以在函数声明中声明默认参数值:
function myFunc(requiredArg, optionalArg = 'defaultValue') { // do stuff }
关于MDN的更多信息(尽pipe文章标题是JavaScript中的“参数”,而不是“参数”)。
目前这只支持Firefox ,但是随着标准的完成,希望支持能够迅速提高。
如果你需要一个文字NULL
,你可能会遇到一些问题。 除此之外,不,我想你可能是在正确的轨道上。
有些人select的另一种方法是通过迭代参数列表的variables的相关数组。 它看起来有点整洁,但我想这是一点点(很less)一点点过程/内存密集型。
function myFunction (argArray) { var defaults = { 'arg1' : "value 1", 'arg2' : "value 2", 'arg3' : "value 3", 'arg4' : "value 4" } for(var i in defaults) if(typeof argArray[i] == "undefined") argArray[i] = defaults[i]; // ... }
理想情况下,你会重构传递一个对象, 并将它与一个默认对象合并 ,所以传递参数的顺序并不重要(见下面的答案的第二部分)。
但是,如果您只想要快速,可靠,易于使用且不笨重的东西,请尝试以下操作:
一个干净的快速修复任何数量的默认参数
- 它优雅地缩放:每个新的默认最小的额外代码
- 你可以把它粘贴到任何地方:只需要改变所需的参数和variables的数量
- 如果你想将
undefined
传递给一个具有默认值的参数,那么这个variables被设置为undefined
。 此页面上的大多数其他选项将用默认值replaceundefined
。
下面是一个为三个可选参数提供默认值的示例(带有两个必需的参数)
function myFunc( requiredA, requiredB, optionalA, optionalB, optionalC ) { switch (arguments.length - 2) { // 2 is the number of required arguments case 0: optionalA = 'Some default'; case 1: optionalB = 'Another default'; case 2: optionalC = 'Some other default'; // no breaks between cases: each case implies the next cases are also needed } }
简单的演示 。 这与roenving的答案类似,但是对于任何数量的默认参数都很容易扩展,更容易更新,而且使用的arguments
不是Function.arguments
。
传递和合并对象以获得更大的灵活性
上面的代码和许多默认参数的方法一样,不能将parameter passing给序列,比如传递optionalC
但是将optionalB
返回到缺省值。
一个好的select是传递对象并与默认对象合并。 这也有利于可维护性(只要注意保持代码的可读性,以便未来的协作者不会猜测所传递的对象的可能内容)。
使用jQuery的例子 如果你不使用jQuery,你可以使用_.defaults(object, defaults)
的_.defaults(object, defaults)
或浏览这些选项 :
function myFunc( args ) { var defaults = { optionalA: 'Some default', optionalB: 'Another default', optionalC: 'Some other default' }; args = $.extend({}, defaults, args); }
这是一个简单的例子 。
你可以使用一些不同的scheme,我总是testingarguments.length:
function myFunc(requiredArg, optionalArg){ optionalArg = myFunc.arguments.length<2 ? 'defaultValue' : optionalArg; ...
– 这样做,不可能失败,但我不知道你的方式有没有失败的机会,只是现在我想不出一个场景,它实际上会失败…
编辑:然后保罗提供了一个失败的场景!)
与Oli的答案类似,我使用一个参数Object和一个定义默认值的Object。 加一点点糖…
/** * Updates an object's properties with other objects' properties. All * additional non-falsy arguments will have their properties copied to the * destination object, in the order given. */ function extend(dest) { for (var i = 1, l = arguments.length; i < l; i++) { var src = arguments[i] if (!src) { continue } for (var property in src) { if (src.hasOwnProperty(property)) { dest[property] = src[property] } } } return dest } /** * Inherit another function's prototype without invoking the function. */ function inherits(child, parent) { var F = function() {} F.prototype = parent.prototype child.prototype = new F() child.prototype.constructor = child return child }
…这可以做得更好一些。
function Field(kwargs) { kwargs = extend({ required: true, widget: null, label: null, initial: null, helpText: null, errorMessages: null }, kwargs) this.required = kwargs.required this.label = kwargs.label this.initial = kwargs.initial // ...and so on... } function CharField(kwargs) { kwargs = extend({ maxLength: null, minLength: null }, kwargs) this.maxLength = kwargs.maxLength this.minLength = kwargs.minLength Field.call(this, kwargs) } inherits(CharField, Field)
这种方法有什么好处?
- 你可以忽略尽可能多的参数 – 如果你只想重写一个参数的值,那么你可以提供这个参数,而不必显式地传递
undefined
,比如有5个参数,你只想定制最后一个,因为你不得不采取一些build议的其他方法。 - 当使用构造函数时,对于从另一个inheritance的对象的函数,可以很容易地接受inheritance对象的构造函数所需的任何参数,因为您不必在构造函数签名中命名这些参数,或者甚至提供你自己的默认值(让父对象的构造函数为你做,就像
CharField
调用Field
的构造函数时所看到的那样)。 - inheritance层次结构中的子对象可以自定义其父构造函数的参数,执行自己的默认值或确保总是使用某个值。
如果你广泛使用默认值,这看起来更可读:
function usageExemple(a,b,c,d){ //defaults a=defaultValue(a,1); b=defaultValue(b,2); c=defaultValue(c,4); d=defaultValue(d,8); var x = a+b+c+d; return x; }
只要在全局范围声明这个函数即可。
function defaultValue(variable,defaultValue){ return(typeof variable!=='undefined')?(variable):(defaultValue); }
使用模式fruit = defaultValue(fruit,'Apple');
* PS你可以重命名defaultValue
函数为一个简短的名字,只是不要使用default
它是一个JavaScript中的保留字。
松散的types检查
易于编写,但0
, ''
, false
, null
和undefined
将被转换为默认值,这可能不是预期的结果。
function myFunc(requiredArg, optionalArg) { optionalArg = optionalArg || 'defaultValue'; }
严格的types检查
时间更长,但涵盖大部分情况。 只有在错误地分配默认值的情况下,我们传递undefined
作为参数。
function myFunc(requiredArg, optionalArg) { optionalArg = typeof optionalArg !== 'undefined' ? optionalArg : 'defaultValue'; }
检查参数variables
捕捉所有的情况,但是写得最笨拙。
function myFunc(requiredArg, optionalArg1, optionalArg2) { optionalArg1 = arguments.length > 1 ? optionalArg1 : 'defaultValue'; optionalArg2 = arguments.length > 2 ? optionalArg2 : 'defaultValue'; }
ES6
不幸的是,目前浏览器支持很差
function myFunc(requiredArg, optionalArg = 'defaultValue') { }
如果你正在使用Underscore库(你应该,这是一个很棒的库):
_.defaults(optionalArg, 'defaultValue');
使用ES2015 / ES6,您可以利用Object.assign
来replace$.extend()
或_.defaults()
function myFunc(requiredArg, options = {}) { const defaults = { message: 'Hello', color: 'red', importance: 1 }; const settings = Object.assign({}, defaults, options); // do stuff }
您也可以使用像这样的默认参数
function myFunc(requiredArg, { message: 'Hello', color: 'red', importance: 1 } = {}) { // do stuff }
我习惯了在处理可选variables时看到一些基本的变化。 有时,轻松的版本是有用的。
function foo(a, b, c) { a = a || "default"; // matches 0, "", null, undefined, NaN, false. a || (a = "default"); // matches 0, "", null, undefined, NaN, false. if (b == null) { b = "default"; } // matches null, undefined. if (typeof c === "undefined") { c = "default"; } // matches undefined. }
例如,在variablesa
使用的falsy默认值在backbone.js中被广泛使用。
登陆到这个问题, 在EcmaScript 2015search默认参数 ,因此只是提到…
有了ES6,我们可以做默认参数 :
function doSomething(optionalParam = "defaultValue"){ console.log(optionalParam);//not required to check for falsy values } doSomething(); //"defaultValue" doSomething("myvalue"); //"myvalue"
不知道为什么保罗的答复是低估的,但是对null
是一个不错的select。 可能更肯定的例子会更好理解:
在JS中,一个错过的参数就像一个未初始化的声明variables(只是var a1;)。 而且相等运算符将undefined转换为null,所以这对于值types和对象都很好,这就是CoffeeScript处理可选参数的方法。
function overLoad(p1){ alert(p1 == null); // caution, don't use the strict comparison: === won't work. alert(typeof p1 === 'undefined'); } overLoad(); // true, true overLoad(undefined); // true, true. Yes, undefined is treated as null for equality operator. overLoad(10); // false, false function overLoad(p1){ if (p1 == null) p1 = 'default value goes here...'; //... }
尽pipe如此,最好的语义是typeof variable === 'undefined'
稍微好点。 我并不是要防御这个,因为它是基础API如何实现一个函数的问题,它不应该对API用户感兴趣。
还应该补充一点,这里是唯一的方法,以确保任何参数遗漏,使用in
运算符,不幸的是不会使用参数名称,所以必须通过arguments
的索引。
function foo(a, b) { // Both a and b will evaluate to undefined when used in an expression alert(a); // undefined alert(b); // undefined alert("0" in arguments); // true alert("1" in arguments); // false } foo (undefined);
未定义的testing是不必要的,并不像它可能的那样健壮,因为user568458指出如果传递null或false,提供的解决scheme失败。 您的API的用户可能会false或null将强制该方法,以避免该参数。
function PaulDixonSolution(required, optionalArg){ optionalArg = (typeof optionalArg === "undefined") ? "defaultValue" : optionalArg; console.log(optionalArg); }; PaulDixonSolution("required"); PaulDixonSolution("required", "provided"); PaulDixonSolution("required", null); PaulDixonSolution("required", false);
结果是:
defaultValue provided null false
最后两个可能不好。 请尝试:
function bulletproof(required, optionalArg){ optionalArg = optionalArg ? optionalArg : "defaultValue";; console.log(optionalArg); }; bulletproof("required"); bulletproof("required", "provided"); bulletproof("required", null); bulletproof("required", false);
其结果是:
defaultValue provided defaultValue defaultValue
唯一的情况是,这不是最佳的,当你实际上有可选的参数是布尔值或故意为空。
这是我结束了:
function WhoLikesCake(options) { options = options || {}; var defaultOptions = { a : options.a || "Huh?", b : options.b || "I don't like cake." } console.log('a: ' + defaultOptions.b + ' - b: ' + defaultOptions.b); // Do more stuff here ... }
这样调用:
WhoLikesCake({ b : "I do" });
伙计们 –
看了这些和其他的解决scheme后,我使用W3Schools最初的一段代码作为基础,尝试了其中的一些。 你可以find以下的工作。 每个项目都注释掉了,这样就可以让你通过删除单个评论来实验。 要清楚的是,它是没有被定义的“眼睛颜色”参数。
function person(firstname, lastname, age, eyecolor) { this.firstname = firstname; this.lastname = lastname; this.age = age; this.eyecolor = eyecolor; // if(null==eyecolor) // this.eyecolor = "unknown1"; //if(typeof(eyecolor)==='undefined') // this.eyecolor = "unknown2"; // if(!eyecolor) // this.eyecolor = "unknown3"; this.eyecolor = this.eyecolor || "unknown4"; } var myFather = new person("John", "Doe", 60); var myMother = new person("Sally", "Rally", 48, "green"); var elem = document.getElementById("demo"); elem.innerHTML = "My father " + myFather.firstname + " " + myFather.lastname + " is " + myFather.age + " with " + myFather.eyecolor + " eyes.<br/>" + "My mother " + myMother.firstname + " " + myMother.lastname + " is " + myMother.age + " with " + myMother.eyecolor + " eyes.";
function Default(variable, new_value) { if(new_value === undefined) { return (variable === undefined) ? null : variable; } return (variable === undefined) ? new_value : variable; } var a = 2, b = "hello", c = true, d; var test = Default(a, 0), test2 = Default(b, "Hi"), test3 = Default(c, false), test4 = Default(d, "Hello world"); window.alert(test + "\n" + test2 + "\n" + test3 + "\n" + test4);
我尝试了一些在这里提到的选项,性能testing。 目前这个逻辑似乎是最快的。 虽然这是随时间变化的主题(不同的JavaScript引擎版本)
这是我的结果(Microsoft Edge 20.10240.16384.0)
Function executed Operations/sec Statistics TypeofFunction('test'); 92,169,505 ±1.55% 9% slower SwitchFuntion('test'); 2,904,685 ±2.91% 97% slower ObjectFunction({param1: 'test'}); 924,753 ±1.71% 99% slower LogicalOrFunction('test'); 101,205,173 ±0.92% fastest TypeofFunction2('test'); 35,636,836 ±0.59% 65% slower
这个性能testing可以很容易地复制: http : //jsperf.com/optional-parameters-typeof-vs-switch/2
这是testing的代码:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> Benchmark.prototype.setup = function() { function TypeofFunction(param1, optParam1, optParam2, optParam3) { optParam1 = (typeof optParam1 === "undefined") ? "Some default" : optParam1; optParam2 = (typeof optParam2 === "undefined") ? "Another default" : optParam2; optParam3 = (typeof optParam3 === "undefined") ? "Some other default" : optParam3; } function TypeofFunction2(param1, optParam1, optParam2, optParam3) { optParam1 = defaultValue(optParam1, "Some default"); optParam2 = defaultValue(optParam2, "Another default"); optParam3 = defaultValue(optParam3, "Some other default"); } function defaultValue(variable, defaultValue) { return (typeof variable !== 'undefined') ? (variable) : (defaultValue); } function SwitchFuntion(param1, optParam1, optParam2, optParam3) { switch (arguments.length - 1) { // <-- 1 is number of required arguments case 0: optParam1 = 'Some default'; case 1: optParam2 = 'Another default'; case 2: optParam3 = 'Some other default'; } } function ObjectFunction(args) { var defaults = { optParam1: 'Some default', optParam2: 'Another default', optParam3: 'Some other default' } args = $.extend({}, defaults, args); } function LogicalOrFunction(param1, optParam1, optParam2, optParam3) { optParam1 || (optParam1 = 'Some default'); optParam2 || (optParam1 = 'Another default'); optParam3 || (optParam1 = 'Some other default'); } }; </script>
在一个项目中,我注意到我用可选的参数和设置重复了自己,所以我创build了一个处理types检查的类,并指定一个默认值,从而得到整洁可读的代码。 看看例子,让我知道这是否适合你。
var myCar = new Car('VW', {gearbox:'automatic', options:['radio', 'airbags 2x']}); var myOtherCar = new Car('Toyota'); function Car(brand, settings) { this.brand = brand; // readable and adjustable code settings = DefaultValue.object(settings, {}); this.wheels = DefaultValue.number(settings.wheels, 4); this.hasBreaks = DefaultValue.bool(settings.hasBreaks, true); this.gearbox = DefaultValue.string(settings.gearbox, 'manual'); this.options = DefaultValue.array(settings.options, []); // instead of doing this the hard way settings = settings || {}; this.wheels = (!isNaN(settings.wheels)) ? settings.wheels : 4; this.hasBreaks = (typeof settings.hasBreaks !== 'undefined') ? (settings.hasBreaks === true) : true; this.gearbox = (typeof settings.gearbox === 'string') ? settings.gearbox : 'manual'; this.options = (typeof settings.options !== 'undefined' && Array.isArray(settings.options)) ? settings.options : []; }
使用这个类:
(function(ns) { var DefaultValue = { object: function(input, defaultValue) { if (typeof defaultValue !== 'object') throw new Error('invalid defaultValue type'); return (typeof input !== 'undefined') ? input : defaultValue; }, bool: function(input, defaultValue) { if (typeof defaultValue !== 'boolean') throw new Error('invalid defaultValue type'); return (typeof input !== 'undefined') ? (input === true) : defaultValue; }, number: function(input, defaultValue) { if (isNaN(defaultValue)) throw new Error('invalid defaultValue type'); return (typeof input !== 'undefined' && !isNaN(input)) ? parseFloat(input) : defaultValue; }, // wrap the input in an array if it is not undefined and not an array, for your convenience array: function(input, defaultValue) { if (typeof defaultValue === 'undefined') throw new Error('invalid defaultValue type'); return (typeof input !== 'undefined') ? (Array.isArray(input) ? input : [input]) : defaultValue; }, string: function(input, defaultValue) { if (typeof defaultValue !== 'string') throw new Error('invalid defaultValue type'); return (typeof input === 'string') ? input : defaultValue; }, }; ns.DefaultValue = DefaultValue; }(this));
这是我的解决scheme。 有了这个,你可以留下任何你想要的参数。 可选参数的顺序并不重要,您可以添加自定义validation。
function YourFunction(optionalArguments) { //var scope = this; //set the defaults var _value1 = 'defaultValue1'; var _value2 = 'defaultValue2'; var _value3 = null; var _value4 = false; //check the optional arguments if they are set to override defaults... if (typeof optionalArguments !== 'undefined') { if (typeof optionalArguments.param1 !== 'undefined') _value1 = optionalArguments.param1; if (typeof optionalArguments.param2 !== 'undefined') _value2 = optionalArguments.param2; if (typeof optionalArguments.param3 !== 'undefined') _value3 = optionalArguments.param3; if (typeof optionalArguments.param4 !== 'undefined') //use custom parameter validation if needed, in this case for javascript boolean _value4 = (optionalArguments.param4 === true || optionalArguments.param4 === 'true'); } console.log('value summary of function call:'); console.log('value1: ' + _value1); console.log('value2: ' + _value2); console.log('value3: ' + _value3); console.log('value4: ' + _value4); console.log(''); } //call your function in any way you want. You can leave parameters. Order is not important. Here some examples: YourFunction({ param1: 'yourGivenValue1', param2: 'yourGivenValue2', param3: 'yourGivenValue3', param4: true, }); //order is not important YourFunction({ param4: false, param1: 'yourGivenValue1', param2: 'yourGivenValue2', }); //uses all default values YourFunction(); //keeps value4 false, because not a valid value is given YourFunction({ param4: 'not a valid bool' });
-
arg || 'default'
arg || 'default'
是一个很好的方法,适用于90%的情况 -
当你需要传递可能是“虚假”的值时,它会失败
-
false
-
0
-
NaN
-
""
对于这些情况,您需要稍微详细一点,并检查
undefined
-
-
当你首先有可选的参数时要小心,你必须注意所有参数的types
纠正我,如果我错了,但这似乎是最简单的方法(无论如何一个论点):
function myFunction(Required,Optional) { if (arguments.length<2) Optional = "Default"; //Your code }
这些比运营商版本更短。
function foo(a, b) { a !== undefined || (a = 'defaultA'); if(b === undefined) b = 'defaultB'; ... }
在所有的情况下,可选的arg是虚假的,你将以defaultValue结束。
function myFunc(requiredArg, optionalArg) { optionalArg = optionalArg || 'defaultValue'; console.log(optionalArg); //do stuff } myFunc(requiredArg); myFunc(requiredArg, null); myFunc(requiredArg, undefined); myFunc(requiredArg, ""); myFunc(requiredArg, 0); myFunc(requiredArg, false);
所有上面的日志defaultValue,因为所有的6都是虚假的。 在情况4,5,6你可能没有兴趣设置optionalArg作为defaultValue,但它设置,因为他们是虚假的
我build议你这样使用ArgueJS :
function myFunc(){ arguments = __({requiredArg: undefined, optionalArg: [undefined: 'defaultValue'}) //do stuff, using arguments.requiredArg and arguments.optionalArg // to access your arguments }
你也可以用你希望接收的参数的typesreplaceundefined
,如下所示:
function myFunc(){ arguments = __({requiredArg: Number, optionalArg: [String: 'defaultValue'}) //do stuff, using arguments.requiredArg and arguments.optionalArg // to access your arguments }
看来,最安全的方式 – 在决定使用默认值之前处理所有提供的参数的任何谬误types – 是检查被调用函数中可选参数的存在\存在。
依赖于参数对象成员的创build,如果缺less参数,甚至不会创build它,不pipe它可能被声明的事实,我们可以像这样编写你的函数:
function myFunc(requiredArg, optionalArg){ optionalArg = 1 in arguments ? optionalArg : 'defaultValue'; //do stuff }
利用这种行为:我们可以安全地检查参数列表中的任何缺失值,无论何时需要确保函数获得其过程中所需的某个值。
在下面的演示代码中,我们将故意将一个无types和无价值的 undefined作为默认值,以便能够确定它是否可能会在falsy参数值(例如0 false等)上失败,或者它的行为如预期那样。
function argCheck( arg1, arg2, arg3 ){ arg1 = 0 in arguments || undefined; arg2 = 1 in arguments || false; arg3 = 2 in arguments || 0; var arg4 = 3 in arguments || null; console.log( arg1, arg2, arg3, arg4 ) }
现在,检查几个虚假的参数值,看看他们的存在是否被正确检测,因此评估为真 :
argCheck( "", 0, false, null ); >> true true true true
这意味着 – 他们并没有失去对预期价值的认可。 在这里,我们有一个检查所有的论点缺失,根据我们的algorithm应该获得他们的默认值,即使他们是虚假的 。
argCheck( ); >> undefined false 0 null
正如我们所看到的,参数arg1,arg2,arg3和未声明的arg4正按照顺序返回它们的确切默认值。 因为我们现在已经确定了它的工作原理,所以我们可以像第一个例子那样使用: if或者三元条件来重写实际上可以使用它们的函数。
在具有多个可选参数的函数上,循环遍历可能为我们节省了一些位。 但是由于参数名如果没有提供值,就不会被初始化,即使我们已经编写了一个默认值,我们也不能通过名称来访问它们,我们只能通过无用代码可读性的参数[索引]来访问它们。
但是除了这种不便之处,在某些编码情况下可能完全可以接受的情况下,对于多个和任意数量的参数默认值还有另一个未解决的问题。 哪些可能而且应该被视为一个错误,因为我们不能再跳过争论,正如我们曾经能够在没有给出价值的情况下能够以如下的语法:
argCheck("a",,22,{});
因为它会扔! 这使得我们不可能用我们期望的默认值的特定的错误types来替代我们的论点。 Which is stupid, since the arguments object is an array-like object and is expected to support this syntax and convention as is, natively or by default!
Because of this shortsighted decision we can no longer hope to write a function like this:
function argCheck( ) { var _default = [undefined, 0, false, null ], _arg = arguments; for( var x in _default ) { x in _arg ? 1 : _arg[x] = _default[x]; } console.log( _arg[0],_arg[1],_arg[2],_arg[3] ); }
in which case, we would be able to write each default value of a desired type in arguments row and be able to at least access them by args.index.
For instance this function call would yield:
argCheck(); >>undefined 0 false null
as defined in our default array of arguments values. However the following is still possible:
argCheck({}) >>Object { } 0 false null argCheck({}, []) >>Object { } Array [ ] false null
But regretfully not:
argCheck("a",,,22); >>SyntaxError: expected expression, got ','
Which would otherwise be logging:
>>a 0 false 22
but that's in a better world! However – for the original question – the topmost function will do just fine. 例如:
function argCheck( arg, opt ) { 1 in arguments ? 1 : opt = "default"; console.log( arg, opt ); }
ps: sorry for not preserving the types of chosen defaults in my argument inputs while writing them.
Some code intel tools, in particular the WebStorm IDE and perhaps jshint, give warnings when you call a function with less arguments than declared:
function foo(a, b) { if (typeof b === 'undefined') { b = 5; } … } foo(1); /* warning */
What you can do instead is this:
function foo(a /*, b */) { var b = arguments.length > 1 ? arguments[1] : 2; … } foo(1);
Note that the b
argument is declared in a comment for clarity.
function foo(requiredArg){ if(arguments.length>1) var optionalArg = arguments[1]; }