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