《深入理解ES6》读书笔记(三):函数的发展和变化

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_43624878/article/details/102648254

默认参数

在ES5的版本中,我们习惯于通过下面的模式创建函数并为参数赋予默认值:

function makeRequest(url,timeout,callback){
	timeout=timeout || 200;
	callback=callback || function(){};
	//其余代码
}

我们认为,在含有逻辑操作符的表达式中,前一个操作数的值为假时,总会返回后一个值。对于函数的命名参数,如果不显式传值,则其默认值为undefined。因此我们经常使用逻辑或操作符来为缺失的参数提供默认值。
然而这个方法有一个致命的缺陷:当给timeout传入值0,即使这个值是合法的,也会被视为一个假值。(这很好理解)
这种情况下,更安全的选择貌似就是通过typeof检查参数类型:

function makeRequest(url,timeout,callback){
	timeout=(typeof timeout !== "undefined") ? timeout : 200;
	callback=(typeof callback !== "undefined") ? callback : function(){};
	//其余代码
}

尽管这种方法看上去更安全了,而且现在流行的JavaScript库中几乎都使用类似的模式进行默认补全,但是我们仍感觉这很“繁琐”——它仍需额外的代码来执行这种非常基础的操作。这让我们很不爽。

function makeRequest(url,timeout=200,callback=function(){}){
	//其余代码
}

ES6站了出来,简化了为形参提供默认值的过程:
如上,只有第一个参数被认为 总是要 为其传入值的,其他两个参数可以有默认值,而且不需要添加任何校验值是否缺失的代码。
当然后两个参数也可以有传入值,这时,传入值会覆盖默认值。

makeRequest("/foo");

makeRequest("/foo",500);

makeRequest("/foo",500,function(body){
	doSomething(body);
});

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

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了。
故而在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");   // true true false false

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

function mixArgs(first,second="b"){
	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");   // 1 true false false false

默认参数表达式

就像这样:

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

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

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

默认参数的临时死区

前文中,我提到了“let和const变量的临时死区TDZ”,其实默认参数也有临时死区:在这里的参数不可访问。
与let相似,定义参数时会为每个参数创建一个新的 标识符绑定,该绑定在初始化之前不可被引用。

function getValue(value){
	return value+5;
}
function add(first,second=getValue(first)){
	return first+second;
}
console.log(add(1,1));   // 2
console.log(add(1));   // 7

当除此执行函数add()时,绑定first与second被添加到一个专属于函数参数的临时死区(与let行为类似)
由于初始化second时first已经被初始化,所以他可以访问first的值,但是反过来就错了:

function add(first=second,second){
	return first+second;
}
console.log(add(1,1));   // 2
console.log(add(1));   //抛出错误!

处理不定参数

在函数的命名参数前添加三个点(…)就表明这是一个不定参数,该参数为一个数组,包含着自他之后传入的所有参数,通过这个数组名即可注意访问里面的参数。

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;
}

不定参数的使用限制

  1. 每个函数最多只能声明一个不定参数,而且一定放在所有参数的末尾
  2. 不定参数不能用于对象字面量setter中(对象字面量setter的参数有且只能有一个)

增强的Function构造函数

Function构造函数通常被我们用来动态创建新的函数。这种构造函数接收字符串形式的参数,分别为函数的参数及函数体:

var add=new Function("first","second","return first+second");
console.log(add(1,1));    // 2

ES6为其增加了默认参数和不定参数的功能。

var pickFirst=new Function("...args","return args[0]");
console.log(pickFirst(1,2));   // 1

猜你喜欢

转载自blog.csdn.net/qq_43624878/article/details/102648254