美文网首页
函数(上)

函数(上)

作者: NnnLillian | 来源:发表于2019-10-16 23:59 被阅读0次

    ES6函数的改变不算太大,都是一些其他语言早就有的功能,而Javascript一直比较欠缺的,比如函数参数默认值,任意参数的表示法,最大的变化应该是支持箭头函数(其他语言称之为LAMBDA表达式),一种对匿名函数的一种简写方式,以下来探讨一下函数在ES6中的一些改变:

    函数形参的默认值

    JavaScrip 函数有个特别的地方:无论在函数定义中声明了多少形参,都可以传入任意数量的参数,也可以在定义函数时添加针对参数数量的处理逻辑,当已定义的形参无对应的传入参数时为其指定一个默认值。

    在ES5中模拟默认参数

    // ES5模拟默认参数
    function person(name, age) {
        name = name || "James";
        age = age || "18";
        console.log(name + " " + age);
    }
    // 一般情况下这种写法是没问题的,当逻辑或前面的值为false值,整个表达式返回后面的值
    // 例如:
    person("Louis"); // ok
    person(); // ok
    person(undefined, 20); // ok
    person("baby", 0); // "baby 18" error, 0为false值
    

    这种情况下更安全的选择是通过typeof检查参数类型。

    function person(name, age) {
        if (typeof name === "undefined") {
            name = name || "James"; 
        }
        if (typeof age === "undefined") {
            age = age || "18";
        }
        console.log(name + " " + age);
    }
    person(undefined, 0); // ok "James 0"
    

    该方法虽然安全,但需要额外的代码来执行这种非常基础的操作。

    ES6中的默认参数

    ES6中简化了为形参提供默认值的过程。

    // 如果没有参数传入值则为其提供一个初始值
    // timeout和callback都有默认值。
    function getRequest(url, timeout = 2000, callback= function(){}) {
        // do something
    }
    
    //该函数中,url是必需参数,其余两个有默认值的参数为可选参数
    function getRequest(url, timeout = 2000, callback= function(){}) {
        // do something
    }
    
    // 使用参数timeout、callback的默认值。
    gerRequest("/foo") ;
    // 使用参数timeout的默认值。
    gerRequest("/foo", 500);
    // 不使用参数的默认值。
    gerRequest("/foo", 500, function(body) {
    // do something(body);
    });
    

    声明函数时,可以为任意参数指定默认值,在已指定默认值的参数后可以继续声明无默认值参数

    // 只有timeout有默认值,这样声明函数不会抛出错误
    function getRequest(url, timeout = 2000, callback) {
        // do something
    }
    
    // 使用参数timeout的默认值。
    gerRequest("/foo", undefinde, function(body) {
        doSomething(body);
    }) ;
    // 使用参数timeout的默认值。
    gerRequest("/foo");
    // 不使用参数的默认值。
    gerRequest("/foo", null, function(body) { // 对于默认参数值,null是一个合法值。
        doSomething(body); 
    });
    

    默认参数表达式

    可以非原始值传参。

    let value = 5;
    function getValue() {
        return value++;
    }
    // 可以通过函数执行来得到默认参数值
    function add(first, second = getValue()) {
        return first + second;
    }
    
    add(1, 1); // 2
    add(3); // 8 (第一次调用getValue的时候返回的是默认值5)
    add(3); // 9
    

    注意:如果使用函数调用结果作为默认参数时,忘记写小括号,例如second = getValue(),则最终传入的是对函数的引用而不是函数调用的结果。

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

    // 后面参数引用前面参数
    function add1(first, second = first) {
        return first + second;
    }
    add1(2); // 4
    add1(3, 4); // 7
    
    // 若写成第一个参数引用第二个参数
    function add2(first = second, second) {
        return first + second;
    }
    add2(1, 1); // ok 2
    add2(undefined, 4); // THROW AN ERROR 第二个参数未声明就引用就会抛出错误
    

    second比first晚定义,所以它不能作为first的默认值,是默认参数的TDZ(临时死区)

    默认参数的临时死区

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

    // 后面参数引用前面参数
    function add1(first, second = first) {
        return first + second;
    }
    
    add1(3, 4); // 7
    add1(2); // 4
    

    相当于引擎在背后做了如下的事情

    // add1(3, 4);
    let first = 3;
    let second = 4;
    
    // add1(2);
    let first = 2;
    let second = first;
    

    执行函数add1()时,绑定first和second被添加到一个专属于函数参数的临时死区。初始化second的时候first已经被初始化,所以second可以访问first的值。

    // 第一个参数引用第二个参数
    function add2(first = second, second) {
        return first + second;
    }
    add2(undefined, 4); // THROW AN ERROR 
    
    // 就相当于
    let first = second; // error
    let second = 4;
    

    调用add2(undefined, 4); 函数,因为当first初始化时second尚未初始化,导致程序抛出错误,此时second尚处于临时死区中,所有引用临时死区中绑定的行为都会报错。

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

    默认参数值对arguments对象的影响

    当使用默认参数时,arguments对象的行为与以往不同。

    function mixArgs(first, second){
        console.log(first === arguments[0]);
        console.log(second === arguments[1]);
        first = "c";
        second = "d";
        console.log(first === arguments[0]);
        console.log(second === arguments[1]);
    }
    
    mixArgs("a", "b");
    

    输入



    非严格模式下,命名参数的变化会同步更新到arguments对象中,所以当first和second被赋予新值时,arguments[0]和arguments[1]相应地也就更新了。所以最终都为true。
    然而在ES5严格模式下,无论参数如何变化,arguments对象不再随之改变。

    function mixArgs(first, second){
        "use strict";
    
        console.log(first === arguments[0]);
        console.log(second === arguments[1]);
        first = "c";
        second = "d";
        console.log(first === arguments[0]);
        console.log(second === arguments[1]);
    }
    
    mixArgs("a", "b");
    

    输出



    在ES6中,如果一个函数使用了默认参数值,无论是否显式定义了严格模式,arguments对象的行为都与ES5严格模式下保持一致。默认参数值的存在使得arguments对象保持与命名参数分离。

    function mixArgs(first, second){
        console.log(arguments.length);
        console.log(first === arguments[0]);
        console.log(second === arguments[1]);
        first = "c";
        second = "d";
        console.log(first === arguments[0]);
        console.log(second === arguments[1]);
    }
    
    mixArgs("a");
    

    arguments[1]的值为undefined, first与arguments[0]全等,改变first和second并不会影响arguments对象。总是可以通过arguments对象将参数恢复为初始值,无论当前是否在严格模式的环境下。

    今天参加了苏州生物医学技术研究所的面试,emmm,其实面试时间只有30min,但是今天几乎一整天都没有学习,除了化妆收拾。还是自控力与执行力不够,以后加油吧!

    相关文章

      网友评论

          本文标题:函数(上)

          本文链接:https://www.haomeiwen.com/subject/pmtpmctx.html