为JavaScript函数设置默认参数值

2651

我想要一个JavaScript函数可以有可选参数,并且可以设置默认值,如果该值未定义则使用默认值(如果传递了值,则忽略默认值)。在Ruby中,您可以这样做:

def read_file(file, delete_after = false)
  # code
end

这个JavaScript代码可以运行吗?

function read_file(file, delete_after = false) {
  // Code
}

2
ES6 开始,这将起作用:function read_file(file, delete_after=false){}; - Azhar Uddin Sheikh
29个回答

3559

ES6/ES2015开始, 默认参数已经在语言规范中了。

function read_file(file, delete_after = false) {
  // Code
}

只需运行。

参考:默认参数 - MDN

如果没有传递值或未定义,则默认函数参数允许使用默认值初始化形式参数。

在ES6中,您可以通过解构模拟默认的命名参数

// the `= {}` below lets you call the function without any parameters
function myFor({ start = 5, end = 1, step = -1 } = {}) { // (A)
    // Use the variables `start`, `end` and `step` here
    ···
}

// sample call using an object
myFor({ start: 3, end: 0 });

// also OK
myFor();
myFor({});

ES2015之前的版本

有很多方法,但这是我首选的方法——它允许您传递任何想要的内容,包括false或null。(typeof null == "object"

function foo(a, b) {
  a = typeof a !== 'undefined' ? a : 42;
  b = typeof b !== 'undefined' ? b : 'default_b';
  ...
}

229
你也可以将它封装成这样:function defaultFor(arg, val) { return typeof arg !== 'undefined' ? arg : val; },然后你可以这样调用它:a = defaultFor(a, 42); - Camilo Martin
7
在尝试使用“undefined”对象时,@SiPlus不仅遇到了额外的引用错误(免费赠送),而且即使在某些浏览器中可能会更快,但“null”仍然是一个对象,“undefined”是指试图表明这里没有任何内容,甚至没有“null”的原始类型的引用。在此处查看两种情况的概要:JavaScript/Reference/Global_Objects/undefined - Sampo Sarrala - codidact.org
10
如果你检查一个对象的属性,那么 typeof 就是多余的。function foo(data) { var bar = data.bar !== undefined ? data.bar : 'default'; } 这样不会抛出引用错误并且简洁明了。 - Dziamid
12
我知道这只是小问题,但我不喜欢在参数已定义的情况下赋值a = ab = b。而且,那个带有复杂条件的三元运算符可能会让未来的维护者难以阅读。我更喜欢以下语法: if (typeof a == 'undefined') a = 42
  • 没有不必要的赋值,同时也更容易阅读。
- Daddy32
13
使用 typeof 有什么原因吗?直接使用 a === undefined 似乎更简单。而且这样做,如果拼错了 undefined,你会得到一个引用错误(reference error)。 - Abe Voelker
显示剩余16条评论

633
function read_file(file, delete_after) {
    delete_after = delete_after || "my default here";
    //rest of code
}
这段代码给delete_after赋值。如果它不是falsey的值,则将其赋值为delete_after的值,否则将其赋值为字符串"my default here"。如需更多详细信息,请查看Doug Crockford对该语言的调查,并查看运算符部分。
如果要传递一个falsey的值,例如falsenullundefined0"",则此方法无效。如果需要传递falsey值,则需要使用Tom Ritter解答中的方法
在处理函数的多个参数时,通常允许消费者通过对象传递参数,并将这些值与包含函数默认值的对象进行合并,这样很有用。
function read_file(values) {
    values = merge({ 
        delete_after : "my default here"
    }, values || {});

    // rest of code
}

// simple implementation based on $.extend() from jQuery
function merge() {
    var obj, name, copy,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length;

    for (; i < length; i++) {
        if ((obj = arguments[i]) != null) {
            for (name in obj) {
                copy = obj[name];

                if (target === copy) {
                    continue;
                }
                else if (copy !== undefined) {
                    target[name] = copy;
                }
            }
        }
    }

    return target;
};

使用

// will use the default delete_after value
read_file({ file: "my file" }); 

// will override default delete_after value
read_file({ file: "my file", delete_after: "my value" }); 

123
我觉得这不够,因为我可能想传入 false。 - Tom Ritter
54
我认为它在大多数情况下都足够了。 - Russ Cam
54
由于它不适用于假值,可能会产生维护上的问题。如果一些代码在之前总是传递真值,突然因为传入假值而失败,应该尽可能避免这种情况,并使用更可靠的方法。 - jinglesthula
2
在这种情况下,为什么 delete_after 没有得到表达式 delete_after || "my default value" 的布尔结果呢? - Ayush
4
或者,你可以选择默认为 falsey 值 - 这通常是一个很好的默认值(例如:bools 的 false,字符串的空字符串,数字的 0 等等) - 在这种情况下,传入什么值并不重要。 - Mark Brackett
显示剩余6条评论

158

个人认为像这样简单的写法更加简洁易读。

function pick(arg, def) {
   return (typeof arg == 'undefined' ? def : arg);
}

function myFunc(x) {
  x = pick(x, 'my default');
} 

6
更新:如果你已经在使用 underscore.js,我建议使用 _.defaults(iceCream, {flavor: "vanilla", sprinkles: "lots"});。按照这个答案中所显示的方式使用全局命名空间被许多人认为是一种不好的做法。如果你不想使用 underscore,你也可以考虑自己编写此常见任务的实用程序(例如 util.default(arg, "defaul value")),但我大多数情况下最终还是会使用 underscore,没必要重复造轮子。 - andersand
2
我实际上推荐这个,我使用它并称其为“por”,代表“参数或”。 - user2039981
2
不要说 typeof arg == 'undefined',而是说 arg === undefined。 - OsamaBinLogin
3
@OsamaBinLogin,你所说的对于当前的JS是正确的 - 旧的测试仍然存在,是因为在一些浏览器上曾经可以用其他值覆盖undefined,导致测试失败。 - Alnitak
3
@Alnitak:仍然可以覆盖“undefined”。因此,使用“typeof”和“'undefined'”仍然是一个好主意。 - Mr. Lance E Sloan
显示剩余2条评论

68

在ECMAScript 6中,你实际上可以像你所写的那样编写:

function read_file(file, delete_after = false) {
  // Code
}

如果delete_after未定义或不存在,这将将其设置为false。 您可以使用ES6功能,例如Babel进行转译。

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


1
等待浏览器支持ECMAScript 6。 - Adriano Resende
1
Babel会将这个转译成什么? - harryg
2
这是ES6的兼容性表 https://kangax.github.io/compat-table/es6/#default_function_parameters 不幸的是,这种语法尚未得到支持 - freemanoid
Edge14、当前的Chrome和Opera现在都支持它,去年开始FF也支持了...现在只等Safari了,在当前的测试版中已经支持,所以希望在大约4个月内,所有浏览器的最新版本都将支持它。然后你只需要等待你的用户实际更新他们的浏览器(或者推动他们这样做)。 - ArtOfWarfare

33

默认参数值

在ES6中,您可以执行可能是JavaScript中最常见的惯用语之一,它与为函数参数设置默认值有关。多年来我们所做的方式应该看起来非常熟悉:

function foo(x,y) {
 x = x || 11;
 y = y || 31;
 console.log( x + y );
}
foo(); // 42
foo( 5, 6 ); // 11
foo( 5 ); // 36
foo( null, 6 ); // 17

这种模式最常用,但当我们传递像

这样的值时,可能存在风险。

foo(0, 42)
foo( 0, 42 ); // 53 <-- Oops, not 42

为什么呢?因为0为假,所以x || 11结果是11,而不是直接传入的0。为了解决这个问题,有些人会更详细地编写检查代码,就像这样:

function foo(x,y) {
 x = (x !== undefined) ? x : 11;
 y = (y !== undefined) ? y : 31;
 console.log( x + y );
}
foo( 0, 42 ); // 42
foo( undefined, 6 ); // 17

我们现在可以检查一个很好的,有用的语法,这个语法是在ES6中添加的,它可以简化给缺失参数设置默认值的赋值过程:

现在,我们可以检查一种很有帮助的语法,这是在ES6中添加的。它可以简化将默认值分配给缺失参数的过程:

function foo(x = 11, y = 31) {
 console.log( x + y );
}

foo(); // 42
foo( 5, 6 ); // 11
foo( 0, 42 ); // 42
foo( 5 ); // 36
foo( 5, undefined ); // 36 <-- `undefined` is missing
foo( 5, null ); // 5 <-- null coerces to `0`
foo( undefined, 6 ); // 17 <-- `undefined` is missing
foo( null, 6 ); // 6 <-- null coerces to `0`

x = 11在函数声明中更像是x !== undefined ? x : 11,而不是常见的习惯用法x || 11

默认值表达式

函数默认值不仅可以是简单的值,如31;它们可以是任何有效的表达式,甚至是函数调用

function bar(val) {
 console.log( "bar called!" );
 return y + val;
}
function foo(x = y + 3, z = bar( x )) {
 console.log( x, z );
}
var y = 5;
foo(); // "bar called"
 // 8 13
foo( 10 ); // "bar called"
 // 10 15
y = 6;
foo( undefined, 10 ); // 9 10

正如您所看到的, 默认值表达式是惰性求值的,这意味着它们仅在需要时才会运行 - 也就是说,当省略参数的参数或未定义参数时才会运行。

默认值表达式甚至可以是内联函数表达式调用-通常称为立即调用函数表达式(IIFE):

function foo( x =
 (function(v){ return v + 11; })( 31 )
) {
 console.log( x );
}
foo(); // 42

16

这个解决方案对我在js中起作用:

function read_file(file, delete_after) {
    delete_after = delete_after || false;
    // Code
}

4
如果“delete_after”是“0”或“null”,会发生什么?对于这些情况,它将无法正确工作。 - Dmitri Pavlutin
@StephanBijzitter 这只在某些情况下才是正确的。但如果你只想将默认值设置为未设置的值,这种方法不起作用,因为0或null !=== false。 - Rutrus
@Rutrus:我不明白你刚才说的任何内容。 - Stephan Bijzitter

12

在JavaScript中使用默认参数值时,我强烈建议谨慎使用。当与诸如forEach、map和reduce等高阶函数一起使用时,它经常会导致错误。例如,请考虑以下代码行:

['1', '2', '3'].map(parseInt); // [1, NaN, NaN]

parseInt有一个可选的第二个参数function parseInt(s, [radix=10]),但是map调用parseInt时会传递三个参数:(elementindexarray)。

我建议您将必需的参数与可选/默认值参数分开。如果您的函数需要1、2或3个必需参数,而没有任何默认值是有意义的,则应将它们作为函数的位置参数,任何可选参数都应作为单个对象的命名属性跟随其后。如果您的函数需要4个或更多个参数,也许通过单个对象参数的属性提供所有参数更有意义。

在您的情况下,我建议您这样编写您的deleteFile函数:(根据instead的评论编辑)...

// unsafe
function read_file(fileName, deleteAfter=false) {
    if (deleteAfter) {
        console.log(`Reading and then deleting ${fileName}`);
    } else {
        console.log(`Just reading ${fileName}`);
    }
}

// better
function readFile(fileName, options) {
  const deleteAfter = !!(options && options.deleteAfter === true);
  read_file(fileName, deleteAfter);
}

console.log('unsafe...');
['log1.txt', 'log2.txt', 'log3.txt'].map(read_file);

console.log('better...');
['log1.txt', 'log2.txt', 'log3.txt'].map(readFile);

运行上述片段展示了默认参数值对未使用参数的危险潜伏。


1
这个所谓的“更好的解决方案”非常难以理解。我建议检查变量类型,比如 typeof deleteAfter === 'bool',如果类型不符则抛出异常。此外,在使用 map/foreach 等方法时,请小心使用,并将被调用的函数包装在匿名函数中。例如:['1', '2', '3'].map((value) => {read_file(value);}) - instead
这是一个很好的观点。我试图避免在我的代码片段中使用库,但在纯JavaScript中,这种习惯用法非常混乱。个人而言,我会使用lodash的get方法来检索deleteAfter。如果typeof 'deleteAfter' !== 'bool',抛出异常也可能是一个很好的谨慎措施。我不喜欢设计需要调用者“小心使用”的方法。小心使用是函数的责任,而不是调用者的责任。最终行为应该遵循最少惊讶原则。 - Doug Coburn
我同意,那个方法不应该被编写成调用它会破坏其行为的方式。但请记住,函数就像模型,调用者就像控制器。模型应该负责数据处理。函数(方法)应该期望数据可能被错误地传递并处理它。这就是为什么检查参数类型加上抛出异常是最好的方式。即使它更复杂。但它仍然可以让您免于挠头并不断问自己...为什么它不起作用?!然后...为什么它又起作用了?! - instead

11

只需使用一个明确的与 undefined 的比较。

function read_file(file, delete_after)
{
    if(delete_after === undefined) { delete_after = false; }
}

10

更新一下,使用ECMAScript 6,你终于可以像这样在函数参数声明中设置默认值:FINALLY

function f (x, y = 7, z = 42) {
  return x + y + z
}

f(1) === 50

正如- http://es6-features.org/#DefaultParameterValues 所提到的


13
这个答案没有用,因为它是一个重复的。 - user

9

作为一个长期从事C++开发的人(从新手到Web开发 :)),当我第一次遇到这种情况时,我按照问题中所提到的方式在函数定义中进行了参数赋值,如下所示。

function myfunc(a,b=10)

但要注意,它在不同浏览器上的表现并不一致。对我来说,在我的台式机上使用chrome可以正常工作,但在安卓手机上的chrome上无法正常工作。 更为安全的选择,就像上面很多人提到的那样是 -

    function myfunc(a,b)
    {
    if (typeof(b)==='undefined') b = 10;
......
    }

本回答的意图不是重复其他人已经提到的相同解决方案,而是要通知您函数定义中的参数赋值可能在某些浏览器上有效,但请不要依赖于它。


3
在函数定义内部进行赋值在IE 11中也无法工作,这是Windows 8.1上最新版本的IE。 - DiMono
1
如果有人想知道,function myfunc(a,b=10) 是 ES6 语法,其他答案中有兼容性表的链接。 - gcampbell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接