美文网首页
函数(上)

函数(上)

作者: 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,但是今天几乎一整天都没有学习,除了化妆收拾。还是自控力与执行力不够,以后加油吧!

相关文章