带参数默认值的函数
ES6中的参数默认值
ES6能更容易地为参数提供默认值,它使用了初始化形式,以便在参数未被正式传递进来时使用。
function makeRequest(url, timeout =2000, callback = funtcion(){}){
// 函数的剩余部分
}
此函数只要求第一个参数始终要被传递。其余两个参数则都有默认值,这使得函数体更为小巧,因为不需要再添加更多代码来检查缺失的参数值。
如果使用全部三个参数来调用makeRequest(), 那么默认值将不会被使用
// 使用默认的timeout 与 callback
makeRequest("/foo");
//使用默认的callback
makeRequest("/foo",500);
//不使用默认值
makeRequest("/foo",500,function(body){
dosomething(body);
})
在函数声明中能指定任意一个参数的默认值,即使该参数排在未指定默认值的参数之前也是可以的。
function makeRequest(url, timeout = 2000, callback){
//函数内容
}
// 使用默认的timeout
makeRequest("/foo",undefined, function(body){
doSomething(body);
});
// 使用默认的timeout
makeRequest("/foo");
//不使用默认值
makeRequest("/foo", null, function(body){
doSomething(body);
})
在关于参数默认值的这个例子中,null值被认为是有效的,意味着对于makeRequest()的第三次调用并不会使用timeout的默认值。
参数默认值如何影响arguments对象
需要记住的是,arguments 对象会在使用参数默认值时有不同的表现。在ES5的非严格模式下,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");
输出:
true
true
true
true
在非严格模式下, arguments 对象总是会被更新以反映出具名参数的变化。因此当 first与 second 变量被赋予新值时, arguments[0] 与 arguments[1] 也就相应地更新了,使得这里所有的 === 比较的结果都为 true 。
然而在使用ES6参数默认值的函数中,arguments对象的表现总是会与ES5的严格模式一致,无论此时函数是否明确运行在严格模式下。参数默认值的存在触发了arguments对象与具名参数的分离。这是个细微但重要的细节,因为arguments对象的使用方式发生了变化。
// 非严格模式
function mixArgs(first, second = "b"){
console.log(arguments.length);
console.log(first === arguments[0]);
console.log(second === argument[1]);
first = "c";
second = "d";
console.log(first === arguments[0]);
console.log(second === arguments[1]);
}
mixArgs("a");
输出
1
true
false
false
false
本例中arguments.length的值为1,因为只给 mixArgs() 传递了一个参数。这也意味着 arguments[1] 的值是undefined,符合将单个参数传递给函数时的预期;这同时意味着first与arguments[0]是相等的。改变first 和 second 的值不会对 arguments 对象造
成影响,无论是否在严格模式下,所以你可以始终依据 arguments 对象来反映初始调用状态。
参数默认值的暂时性死区
前面介绍了let与const的暂时性死区,而参数默认值同样有着无法访问特定参数的暂时性死区。与let声明相似,函数每个参数都会创建一个新的标识符绑定,它在初始化之前不允许被访问,否则会抛出错误。参数初始化会在函数被调用时进行,无论是给参数传递一个值、还是使用了参数的默认值。
function ad(first = second, second){
return first + second;
}
console.log(add(1,1)); //2
console.log(add(undefined,1)); //抛出错误
本例中调用add(1,1)与add(undefined,1)对应着以下的后台代码:
//JS调用add(1,1) 可表示为
let first = 1;
let second = 1;
//JS调用add(1)可表示为
let first = second;
let second = 1;
本例中调用add(undefined,1)抛出了错误,是因为在first被初始化时second尚未被初始化。此处的second 存在于暂时性死区内,对于second的引用就抛出了错误。体现出let的绑定行为
函数参数拥有各自的作用域和暂时性死区,与函数体的作用域相分离,这意味着参数的默认值不允许访问在函数体内部声明的任意变量。
剩余参数
剩余参数(rest parameter)由三个点(…)与一个紧跟着的具名参数指定,它会是包含传递给函数的其余参数的一个数组,名称中的“剩余”也由此而来。
function pick(object,...keys){
let result = object.create(null);
for(let i = 0, len = keys.length; i < len; i++){
result[keys[i]] = object[keys[i]];
}
return result;
}
剩余参数的限制条件
剩余参数受到两点限制。
第一个限制是函数只能有一个剩余参数,并且它必须被放在最后。例如,如下代码是无法工作的
// 语法错误:不能在剩余参数后使用具名参数
function pick(object, ...keys, last){
let result = object.create(null);
for(let i = 0, len = keys.length; i < len; i++){
result[keys[i]] = object[keys[i]];
}
return result;
}
第二个限制 剩余参数不能再对象字面量的setter属性中使用,这意味着如下代码导致语法错误
let object = {
//语法错误:不能在setter中使用剩余参数
set name(...value)
}
存在此限制的原因是:对象字面量的setter被限定只能使用单个参数;而剩余参数按照定义是不限制参数数量的,因此它在此处不被许可。
剩余参数如何影响arguments对象
设计剩余参数是为了替代ES中的arguments。原先在ES4中就移除了arguments并添加了剩余参数,以便允许向函数传入不限数量的参数。尽管ES4从未被实施,但这个想法被保持下来并在ES6中重新引入,虽然arguments仍未在语言中被移除。
function checkArgs(...args){
console.log(args.length);
console.log(arguments.length);
console.log(args[0], arguments[0]);
console.log(args[1], arguments[1]);
}
checkArgs("a" , "b");
函数构造器的增强能力
Function 构造器允许你动态创建一个新函数,但在JS中并不常用。传给该构造器的参数都是字符串,它们就是目标函数的参数与函数体。
var add = new Function("first", "“second”, "return first + second" ;
console.log(add(1 , 1));
)
ES6增强了Function构造器的能力,允许使用默认参数以及剩余参数。对于默认参数来说,你只需为参数名称添加等于符号以及默认值。如下例:
var add = new Function("first" , "second = first", "return first + second");
console.log(add(1,1)); //2
console.log(add(1)); //2
//剩余参数
var pickFirst = new Function("...args","return args[0]");
console.log(pickFirst(1,2));
扩展运算符
与剩余参数关联最密切的就是扩展运算符。
剩余参数允许你把多个独立的参数合并到一个数组中
扩展运算符允许将一个数组分割,并将各个项作为分离的参数传给函数。
let values = [25, 50, 75, 100];
// 等价于 console.log(Math.max(25, 50, 75, 100));
console.log(Math.max(...values)); //100
//可以将扩展运算符与其他参数混用。
let values = [-25, -50, -75 ,-100];
console.log(Math.max(...values, 0)); //0
本例中传给Math.max() 的最后一个参数是0,它跟在使用扩展运算符的其他参数之后传入。
用扩展运算符传递参数,使得更容易将数组作为函数参数来使用,你会发现在大部分场景中扩展运算符都是apply() 方法的合适替代品。