• ES6中的默认参数



    ES6学习系列


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

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

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

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

    为了解决上面的缺陷,更安全的方式时通过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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    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  ƒ () { }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    makeRequest(url, null, null);  // null null 
    
    • 1
    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])
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

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

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

    此时不会因为默认值而导致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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这段代码中,如果不传入最后的一个参数,就会调用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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在此示例中,变量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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

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

    在上面这段代码中,参数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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    因为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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    let first = 1;
    let second = 1;
    
    • 1
    • 2

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

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

    当初次执行函数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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    let first = second;
    let second = 1;
    
    • 1
    • 2

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

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

  • 相关阅读:
    【C++】list的介绍及使用说明
    Spring Security HTTP认证
    初识设计模式 - 中介模式
    你不能不了解的Vue【模板语法上集】!
    低代码平台进行应用开发--异行星低代码平台为例(二)
    LeetCode:709.转换成小写字母
    数据库链接池示实例
    Python:实现fermat little theorem费马小定理算法(附完整源码)
    Python基础教程(三十一):pyecharts模块
    使用docker安装配置oracle 11g
  • 原文地址:https://blog.csdn.net/m0_37607945/article/details/127561894