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

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

  // Do stuff
}

答案

如果传递了 optionalArg,您的逻辑将失败,但评估为 false - 尝试使用此方法

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

或另一种习语:

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

使用任何一种最能传达您意图的习语!

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

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

有关MDN 的更多信息,请参见本文

当前仅 Firefox 支持此功能,但是随着标准的完成,希望支持会迅速提高。

编辑(2019-06-12):

现在,现代浏览器广泛支持默认参数。 Internet Explorer 的所有版本都不支持此功能。但是,Chrome,Firefox 和 Edge 当前支持它。

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

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

保罗 · 迪克森(Paul Dixon)的答案(以我的拙见)比这更难读,但是归结为偏好。

insin 的答案要高级得多,但对大型函数而言却有用得多!

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

如果您需要插入字面值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];

    // ...
}

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

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


一个干净的快速修复程序,可用于任意数量的默认参数

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

这是为三个可选参数(带有两个必需参数)提供默认值的示例

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


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

上面的代码像执行默认参数的许多方式一样,不能不按顺序传递参数,例如,传递optionalC但保留optionalB还原为默认值。

一个不错的选择是传递对象并与默认对象合并。这也有利于可维护性(请注意保持代码的可读性,因此以后的合作者将不会再猜测传递的对象的可能内容)。

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

这是一个实际的例子

您可以为此使用一些不同的方案。我一直在测试 arguments.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 个参数,你只需要定制最后一个,因为您可能需要使用其他建议的方法。
  • 当使用从另一个继承的对象的构造函数时,很容易接受您要从其继承的对象的构造函数所需要的任何参数,因为您不必在构造函数签名中命名这些参数,甚至提供您自己的默认值(让父对象的构造函数为您做到这一点,如上面CharField调用Field的构造函数时所示)。
  • 继承层次结构中的子对象可以为其父构造函数自定义自定义参数,以强制使用其自己的默认值或确保始终使用某个值。

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

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 中的保留字。

松散类型检查

易于编写,但是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') {

}

使用 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
}