有没有更好的方法在JavaScript中执行可选函数参数?

我总是在JavaScript中处理可选参数,如下所示:

function myFunc(requiredArg, optionalArg){optionalArg = optionalArg || 'defaultValue';
// Do stuff}

有没有更好的方法来做到这一点?

有没有这样使用||会失败的情况?

476184 次浏览

如果传递optionalArg,您的逻辑将失败,但计算结果为false-尝试将其作为替代方案

if (typeof optionalArg === 'undefined') { optionalArg = 'default'; }

或者另一个成语:

optionalArg = (typeof optionalArg === 'undefined') ? 'default' : optionalArg;

使用任何能最好地传达意图的习语!

你可以使用一些不同的方案。我一直在测试arguments.length:

function myFunc(requiredArg, optionalArg){optionalArg = myFunc.arguments.length<2 ? 'defaultValue' : optionalArg;
...

——这样做,它不可能失败,但我不知道你的方式是否有失败的机会,只是现在我想不出一个场景,它实际上会失败……

然后保罗提供了一个失败的场景!-)

如果你需要插入一个文字NULL,那么你可能会遇到一些问题。除此之外,不,我认为你可能走在正确的轨道上。

有些人选择的另一种方法是使用一个assoc变量数组遍历参数列表。它看起来有点整洁,但我想它的进程/内存密集型多一点(非常少)。

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

与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.prototypechild.prototype = new F()child.prototype.constructor = childreturn child}

…这个可以做得更好一点。

function Field(kwargs) {kwargs = extend({required: true, widget: null, label: null, initial: null,helpText: null, errorMessages: null}, kwargs)this.required = kwargs.requiredthis.label = kwargs.labelthis.initial = kwargs.initial// ...and so on...}
function CharField(kwargs) {kwargs = extend({maxLength: null, minLength: null}, kwargs)this.maxLength = kwargs.maxLengththis.minLength = kwargs.minLengthField.call(this, kwargs)}inherits(CharField, Field)

这种方法有什么好?

  • 您可以省略尽可能多的参数-如果您只想覆盖一个参数的值,您可以只提供该参数,而不必显式传递undefined,假设有5个参数并且您只想自定义最后一个参数,就像您必须处理建议的其他一些方法一样。
  • 当为一个继承自另一个对象的构造函数函数工作时,很容易接受你继承的对象的构造函数所需要的任何参数,因为你不必在构造函数签名中命名这些参数,甚至提供你自己的默认值(让父对象的构造函数为你做这件事,如上所述,当CharField调用Field的构造函数时)。
  • 继承层次结构中的子对象可以根据需要自定义其父构造函数的参数,强制执行自己的默认值或确保使用某个值总是

我发现这是最简单,最可读的方式:

if (typeof myVariable === 'undefined') { myVariable = 'default'; }//use myVariable here

保罗·迪克森的回答(在我的愚见中)可读性不如这个,但它归结为偏好。

insin的答案更高级,但对大函数更有用!

编辑11/17/2013 9:33pm:我为Node.js创建了一个包,可以更轻松地“重载”名为参数的函数(方法)。

理想情况下,您将重构以传递一个对象并使用默认对象合并它,因此传递参数的顺序无关紧要(请参阅下面这个答案的第二部分)。

但是,如果您只是想要快速,可靠,易于使用且不笨重的东西,请尝试以下操作:


一个干净的快速修复任何数量的默认参数

  • 它优雅地扩展:每个新默认值的额外代码最少
  • 您可以将其粘贴到任何地方:只需更改所需参数和变量的数量
  • 如果要将undefined传递给具有默认值的参数,则将变量设置为undefined。此页面上的大多数其他选项将用默认值替换undefined

下面是一个为三个可选参数(两个必需参数)提供默认值的示例

function myFunc( requiredA, requiredB,  optionalA, optionalB, optionalC ) {
switch (arguments.length - 2) { // 2 is the number of required argumentscase 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的回答,但很容易扩展为任意数量的默认参数,更容易更新,使用#0而不是#1


传递和合并对象以获得更大的灵活性

上面的代码,像许多做默认参数的方法一样,不能乱传递参数,例如,传递optionalC但让optionalB回退到它的默认值。

一个很好的选择是传递对象并与默认对象合并。这对可运维性也有好处(只要注意保持代码的可读性,这样未来的协作者就不会猜测你传递的对象的可能内容)。

使用jQuery的示例。如果您不使用jQuery,您可以使用Underscore的_.defaults(object, defaults)浏览这些选项

function myFunc( args ) {var defaults = {optionalA: 'Some default',optionalB: 'Another default',optionalC: 'Some other default'};args = $.extend({}, defaults, args);}

这里是它在行动中的一个简单例子

如果您广泛使用默认值,这似乎更具可读性:

function usageExemple(a,b,c,d){//defaultsa=defaultValue(a,1);b=defaultValue(b,2);c=defaultValue(c,4);d=defaultValue(d,8);
var x = a+b+c+d;return x;}

只需在全局ESCOPOR上声明此函数即可。

function defaultValue(variable,defaultValue){return(typeof variable!=='undefined')?(variable):(defaultValue);}

使用模式fruit = defaultValue(fruit,'Apple');

*PS您可以将defaultValue函数重命名为短名称,只是不要使用default它是JavaScript中的保留字。

ECMAScript 2015(又名“ES6”)中,您可以在函数声明中声明默认参数值:

function myFunc(requiredArg, optionalArg = 'defaultValue') {// do stuff}

更多关于他们在本文关于MDN

这是目前的仅支持Firefox,但随着标准的完成,预计支持将迅速改善。


编辑(2019-06-12):

默认参数现在被现代浏览器广泛支持。
所有版本的InternetExplorer都不支持此功能。但是,ChromeFirefoxEdge目前支持它。

如果我错了,请纠正我,但这似乎是最简单的方法(无论如何,对于一个论点):

function myFunction(Required,Optional){if (arguments.length<2) Optional = "Default";//Your code}

我建议你这样使用ArgueJS

function myFunc(){arguments = __({requiredArg: undefined, optionalArg: [undefined: 'defaultValue'})
//do stuff, using arguments.requiredArg and arguments.optionalArg//    to access your arguments
}

您还可以将undefined替换为您希望接收的参数类型,如下所示:

function myFunc(){arguments = __({requiredArg: Number, optionalArg: [String: 'defaultValue'})
//do stuff, using arguments.requiredArg and arguments.optionalArg//    to access your arguments
}

我不知道为什么@Paul的回复被否决,但是对null的验证是一个不错的选择。也许一个更肯定的例子会更有意义:

在JavaScript中,遗漏的参数就像一个未初始化的声明变量(只是var a1;)。等号操作符将未定义的转换为null,所以这对值类型和对象都很有效,这就是CoffeeScript处理可选参数的方式。

function overLoad(p1){alert(p1 == null); // Caution, don't use the strict comparison: === won't work.alert(typeof p1 === 'undefined');}
overLoad(); // true, trueoverLoad(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...';//...}

不过,有人担心最好的语义学是typeof variable === 'undefined'稍微好一点。我不打算为这辩护,因为这是底层API如何实现函数的问题;API用户不应该感兴趣。

我还应该补充一点,这是物理上确保任何参数都被遗漏的唯一方法,使用in运算符,不幸的是它不适用于参数名称,因此必须传递arguments的索引。

function foo(a, b) {// Both a and b will evaluate to undefined when used in an expressionalert(a); // undefinedalert(b); // undefined
alert("0" in arguments); // truealert("1" in arguments); // false}
foo (undefined);

未定义的测试是不必要的,并且没有那么健壮,因为正如user568458指出的那样,如果通过null或false,提供的解决方案会失败。您的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);

结果是:

defaultValueprovidednullfalse

最后两个可能是坏的。相反,尝试:

function bulletproof(required, optionalArg){optionalArg = optionalArg ? optionalArg : "defaultValue";;console.log(optionalArg);};bulletproof("required");bulletproof("required", "provided");bulletproof("required", null);bulletproof("required", false);

其结果是:

defaultValueprovideddefaultValuedefaultValue

这不是最佳的唯一情况是当您实际上有可选参数是布尔值或有意的null时。

那些比typeof运算符版本短。

function foo(a, b) {a !== undefined || (a = 'defaultA');if(b === undefined) b = 'defaultB';...}

这就是我最终得到的:

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

我习惯于看到一些处理可选变量的基本变化。有时,宽松的版本很有用。

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

例如,与变量a一起使用的false sy默认值在Backbone.js中广泛使用。

function foo(requiredArg){if(arguments.length>1) var optionalArg = arguments[1];}

乡亲们

在查看了这些和其他解决方案之后,我尝试了其中的一些,使用了最初来自W3School的代码片段作为基础。你可以在下面找到有效的方法。注释的每个项目也可以工作,并且通过删除单个注释来允许你进行实验。需要明确的是,没有定义的是“eyecolor”参数。

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.";

我尝试了这里提到的一些选项并对它们进行了性能测试。目前,逻辑似乎是最快的。尽管这会随着时间的推移而变化(不同的JavaScript引擎版本)。

这些是我的结果(Microsoft Edge20.10240.16384.0):

Function executed            Operations/sec     StatisticsTypeofFunction('test');          92,169,505     ±1.55%   9% slowerSwitchFuntion('test');            2,904,685     ±2.91%  97% slowerObjectFunction({param1: 'test'});   924,753     ±1.71%  99% slowerLogicalOrFunction('test');      101,205,173     ±0.92%     fastestTypeofFunction2('test');         35,636,836     ±0.59%  65% slower

此性能测试可以在以下情况下轻松复制:http://jsperf.com/optional-parameters-typeof-vs-switch/2

这是测试的代码:

<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 argumentscase 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>

如果你使用下划线库(你应该,这是一个很棒的库):

_.defaults(optionalArg, 'defaultValue');
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);

松散的类型检查

易于编写,但0''falsenullundefined将转换为默认值,这可能不是预期的结果。

function myFunc(requiredArg, optionalArg) {optionalArg = optionalArg || 'defaultValue';}

严格的类型检查

更长,但涵盖了大多数情况。只有当我们传递undefined作为参数时,它才会错误地分配默认值。

function myFunc(requiredArg, optionalArg) {optionalArg = typeof optionalArg !== 'undefined' ? optionalArg : 'defaultValue';}

检查参数变量

抓住所有的情况,但写起来最笨拙。

function myFunc(requiredArg, optionalArg1, optionalArg2) {optionalArg1 = arguments.length > 1 ? optionalArg1 : 'defaultValue';optionalArg2 = arguments.length > 2 ? optionalArg2 : 'defaultValue';}

ES6

不幸的是,这在目前的浏览器支持很差

function myFunc(requiredArg, optionalArg = 'defaultValue') {
}

登陆到这个问题,搜索EcmaScript 2015中的默认参数,因此只是提到…

ES6我们可以做默认参数

function doSomething(optionalParam = "defaultValue"){console.log(optionalParam);//not required to check for falsy values}
doSomething(); //"defaultValue"doSomething("myvalue"); //"myvalue"

使用ES2015/ES6,您可以利用Object.assign替换$.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}

在一个项目中,我注意到我在可选参数和设置上重复了太多,所以我创建了一个类来处理类型检查并分配一个默认值,从而产生整洁易读的代码。请参阅示例,让我知道这是否适合您。

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 codesettings        = 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 waysettings        = 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 conveniencearray: 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));

这是我的解决方案。有了这个,你可以留下任何你想要的参数。可选参数的顺序并不重要,你可以添加自定义验证。

function YourFunction(optionalArguments) {//var scope = this;
//set the defaultsvar _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 importantYourFunction({param4: false,param1: 'yourGivenValue1',param2: 'yourGivenValue2',});
//uses all default valuesYourFunction();
//keeps value4 false, because not a valid value is givenYourFunction({param4: 'not a valid bool'});
  1. arg || 'default'是一个很好的方法,适用于90%的情况

  2. 当你需要传递可能是'false'的值时,它会失败

    • false
    • 0
    • NaN
    • ""

    对于这些情况,您需要更加详细并检查undefined

  3. 同样要小心的是,当你有可选的参数时,你必须意识到你所有参数的类型

在optionalArg为false的所有情况下,您最终都会得到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);

上面所有的log defaultValue,因为所有6都是假的。在情况4、5、6中,您可能对将optionalArg设置为defaultValue不感兴趣,但它会设置,因为它们是假的。

似乎最安全的方法-在决定使用默认之前处理所有提供的参数的任何假类型-是检查调用函数中是否存在可选参数

依靠参数对象成员创建,如果参数丢失,甚至不会创建,无论它可能被声明,我们可以像这样编写您的函数:

  function myFunc(requiredArg, optionalArg){optionalArg = 1 in arguments ? optionalArg : 'defaultValue';//do stuff}

使用此行为:只要我们需要确保函数在其过程中获得所需的某个值,我们就可以安全地任意显式地检查参数列表上的任何缺失值。

在下面的演示代码中,我们将故意将无类型毫无价值未定义作为默认值,以便能够确定它是否可能在假参数值上失败,例如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 )}

现在,检查几个false sy参数值,看看它们的存在是否被正确检测到,因此计算结果为true

argCheck( "", 0, false, null );>> true true true true

这意味着-他们没有未能识别/作为预期的参数值。在这里,我们有一个缺少所有参数的检查,根据我们的算法,即使它们是Falsy,也应该获取它们的默认值。

argCheck( );>> undefined false 0 null

正如我们所看到的,参数ARG1, ARG2, ARG3和未声明的art4按顺序返回它们的确切默认值。因为我们现在已经确保它可以工作,我们可以重写函数,它实际上可以像第一个例子中那样使用它们:如果三元条件。

对于有多个可选参数的函数,-一个循环,可能会为我们节省一些时间。但是由于参数名字如果没有提供它们的值就不会初始化,即使我们以编程方式编写了默认值,我们也不能再通过名称访问它们了,我们只能通过参数[索引]访问它们,这是无用的代码易读性。

但是除了这种不便之外,在某些编码情况下可能是完全可以接受的,还有另一个未考虑的问题,即多个和任意数量的参数默认值。这可能并且应该被认为是一个bug,因为我们不能再跳过参数,就像我们曾经可能能够在不给出值的情况下跳过语法,例如:

argCheck("a",,22,{});

因为它会抛出!这使得我们无法用我们想要的默认值的特定Falsy类型替换我们的参数。这是愚蠢的,因为参数对象是一个类似数组的对象,并且预计将按原样、本机或默认情况支持此语法和约定!

由于这个短视的决定,我们不能再希望编写这样的函数:

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

在这种情况下,我们将能够在参数行中写入所需类型的每个默认值,并且至少能够通过args.index.访问它们

例如,这个函数调用将产生:

argCheck();>>undefined 0 false null

在我们默认的参数值数组中定义。以下是可能的:

argCheck({})>>Object {  } 0 false null
argCheck({}, [])>>Object {  } Array [  ] false null

但遗憾的是:

 argCheck("a",,,22);>>SyntaxError: expected expression, got ','

否则将记录:

>>a 0 false 22

这是一个更美好的世界!然而,对于最初的问题,最上面的函数会很好。例如:

function argCheck( arg, opt ) {1 in arguments ? 1 : opt = "default";console.log( arg, opt );}

附注:很抱歉在编写参数输入时没有保留所选默认值的类型