ES6中的默认参数


ES6学习系列


在ES6中,可以为函数定义默认参数,而在ES6之前,可能需要在函数体内添加额外的代码来检查参数是否存在,如若不存在则手动赋一个默认值。

  1. 在ES5中模拟默认参数
function makeRequest1(url, timeout, callback) {
    timeout = timeout || 2000;
    callback = callback || function () { };
    console.log(timeout);
    console.log(callback);
}

在上面的函数中,timeout和callback是可选参数,如果用户没有传值,通过逻辑或操作符来为缺失的参数提供默认值。但是这个方法有缺陷,比如想给函数的第二个参数传递0,即使这个值是合法的,也会被认为是假值,而使用默认值。

let url = "https://www.baidu.com/";
makeRequest1(url);   // 2000  ƒ () { }
makeRequest1(url, 0);  // 2000  ƒ () { }

为了解决上面的缺陷,更安全的方式时通过typeof检查参数类型。这种方式也是一种常见的模式,在流行的JavaScript库中均使用类似的模式进行补全。

function makeRequest2(url, timeout, callback) {
    timeout = (typeof timeout !== "undefined") ? timeout : 2000;
    callback = (typeof callback !== "undefined") ? callback : function () { };
    console.log(timeout);
    console.log(callback);
}
makeRequest2(url);   // 2000  ƒ () { }
makeRequest2(url, 0);  // 0  ƒ () { }
  1. ES6中的默认参数值
function makeRequest(url, timeout = 2000, callback = function () { }) {
    console.log(timeout);
    console.log(callback);
}
makeRequest(url);   // 2000  ƒ () { }
makeRequest(url, 0);  // 0  ƒ () { }
// 传入undefined也会使用默认值
makeRequest(url, undefined);  // 2000  ƒ () { }

这个函数中,只有第一个参数被认为是总要传值的,其他两个参数都有默认值.而且也不需要添加任何校验值是否缺失的代码,函数体会更小 .
如果不想使用默认值,除了传递自己的值之外,传递null也会被认为是有效值,而不使用默认值.

makeRequest(url, null, null);  // null null 
  1. 默认参数值对arguments对象的影响
    在ES6中,在函数体中修改命名参数不会同步到arguments参数中(与ES5中的严格模式保持一致不进行同步,而ES5的非严格模式会保持同步)。而函数未传入值使用默认值时,默认值也不会同步到arguments参数中。
function mixArgs(first, second = "b") {
    console.log(first === arguments[0])
    console.log(second === arguments[1])
    first = "c";
    second = "d";
    console.log(first === arguments[0])
    console.log(second === arguments[1])
}

即使first second的值在函数体中发生了变化,arguments对象也不会被同步修改,如果是ES5中非严格模式,则全部输出为true

mixArgs("a", "b");   // true true false false

如果第二个参数不传,默认值也不会出现在arguments数组当中。

mixArgs("a"); // true false false false

此时不会因为默认值而导致arguments发生变化,此时arguments的长度为1,arguments[1]为空

  1. 默认参数表达式

关于默认参数表达式,最有趣的特性可能是非原始值传参了。举个例子,你可以通过函数执行来得到默认参数的值,就像这样:

function getValue(){
    return 5;
}
function add(first,second = getValue()){
    return first + second;
}
// 2
console.log(add(1,1));
// 6
console.log(add(1));

在这段代码中,如果不传入最后的一个参数,就会调用getValue()函数来得到默认的值。

但是,初次解析函数时并不会执行函数,而是当调用add函数且不传入第二个参数时才会调用。修改上面的代码如下


let value = 5;

function getValue(){
    return value ++;
}
function add(first,second = getValue()){
    return first + second;
}
// 2
console.log(add(1,1));
// 6
console.log(add(1));
// 7
console.log(add(1));

在此示例中,变量value的值为5,每次调用getValue方法时加1.第一次调用add(1)为6,第二次调用返回7,因为变量value已经增加了1。但解析函数和第一次add(1,1)时value的值并没有发生改变。

这里还有一点需要注意的是,如果getValue后面的括号没有写的话,则默认参数是对函数的引用。

let value = 5;

function getValue(){
    return value ++;
}
function add(first,second = getValue){
    return first + second;
}
// 1function getValue(){
//    return value ++;
// }
console.log(add(1));

此时的输出就变为了1加上函数定义字符串了,而非执行函数时的返回值。

正因为默认参数是在函数调用时求值,所以可以使用先定义的参数作为后定义参数的默认值,就像这样:

function add(first,second = first){
    return first + second;
}
// 2
console.log(add(1));

在上面这段代码中,参数second的默认值是参数first的值,如果只传入一个参数,则两个参数的值相同,从而add(1)返回的结果为2.

更进一步,可以将第一个参数传入函数来获得参数second的值。就像这样

function getValue(value) {
    return value + 5;
}

function add(first, second = getValue(first)) {
    return first + second;
}
// 2
console.log(add(1, 1));
// 7
console.log(add(1));

在引用默认值的时候,只允许引用前面参数的值,即先定义的参数不能访问后定义的值。

function add(first = second, second) {
    return first + second;
}
// 2
console.log(add(1, 1));
// Uncaught ReferenceError: Cannot access 'second' before initialization
console.log(add(undefined,1));

因为second比first晚定义,因此其不能作为first的默认值,这里涉及到临时死区(TDZ)的概念。

  1. 默认参数的临界死区问题

ES6之块级作用域绑定 当中介绍了临时死区(TDZ),其实默认参数也有同样的临时死区,在临时死区的参数不可访问。与let声明类似,定义参数时会为每个参数创建一个新的标识符绑定,该绑定在初始化之前不可被引用,如果试图访问会导致程序抛出错误。当调用函数时,会通过传入的值或参数的默认值初始化该参数。
比如上一个例子当中

function getValue(value) {
    return value + 5;
}

function add(first, second = getValue(first)) {
    return first + second;
}

// 2
console.log(add(1, 1));
// 7
console.log(add(1));

调用add(1,1)时实际上相当于执行以下代码来创建first和second参数值。

let first = 1;
let second = 1;

而调用add(1)时实际上相当于执行以下代码来创建first和second参数值

let first = 1;
let second = getValue(first);

当初次执行函数add()时,绑定first和second被添加到一个专属于函数参数的临时死区(与let的行为类似)。由于初始化second时first已经被初始化,所以它可以访问first的值,但是反过来就错了。比如

function add(first = second, second) {
    return first + second;
}
// 2
console.log(add(1, 1));
// Uncaught ReferenceError: Cannot access 'second' before initialization
console.log(add(undefined,1));

在add(undefined,1)的时候类似于

let first = second;
let second = 1;

因为first初始化时,second还在临时死区当中,尚未初始化,所以会导致程序抛出错误。

函数参数有自己的作用域和临时死区,其与函数体的作用域时各自独立的,也是说参数的默认值不可访问函数体内声明的变量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值