默认参数
可以为函数参数设置默认值,这里当第二个参数不传,或者为undefined时取默认值:
function makeRequest(url,timeout=2000,callback){
}
默认参数对arguments的影响
arguments只与调用时传入的参数有关,不受参数默认值的影响。传入参数后arguments的值不会再改变,所以可以通过arguments将参数恢复为初始值。
function mixArgs(first,second=2){
console.log(first===arguments[0]); //true
console.log(second===arguments[1]); //false
console.log(arguments[1]); //undefined
first=2;
console.log(arguments[0]); //1
}
mixArgs(1);
默认参数表达式
参数的默认值,可以是表达式。
默认参数求值,是在函数被调用时才求值,所以后一个参数的默认值可以等于前一个参数。
function foo(first,second=first,thrid=getValue()){
}
但前一个参数的默认值不能是后一个参数。其原因是因为函数参数是在临时死区中,有自己的作用域(不能访问函数体的参数),未执行声明语句前不能进行访问,访问会报错。执行以下函数时
function erroFoo(first=second,second){
}
执行erroFoo(undefined,1)时,其实是经过以下步骤:
let first=second; //在second声明未执行时访问,报错。
let second=1;
不定参数——处理无名参数
语法如下:
function foo(book,...keys){
}
这里keys为不定参数代表所有无名参数组成的数组。
不定参数使用限制:
- 每个函数最多只有一个不定参数,并且只能在末尾。
- 不能用于对象字面量的setter中。
不定参数对arguments和length无影响。
增强的Function构造函数
可以在Function构造函数中使用默认参数和不定参数。如下:
var foo=new Function("first","second=first","...args","return first+second+args[0]")
展开运算符
前面把...放在函数的参数中,是把传入的无名参数放入一个不定参数数组中。而把...放在调用函数的地方,则是把数组打散成一个个参数传入。
var value=[1,2,3,4,8,9];
//等价于
//Math.max(1,2,3,4,8,9,0)
console.log(Math.max(...value,0)); //9,这里展示了展开运算符和正常传入的参数混合使用,这是apply()无法做到的。
name属性
用来方便调试的,并且函数声明比函数表达式的名字权重高。
用Function构造函数时,前缀为"anoymous";
用bind()构建的函数,前缀为"bound"
函数的多重用途
es6的函数内部有[[Call]]和[[Construct]]双重方法。通过new时,调用的是[[Construct]]。不是所有的函数都有[[Construct]]方法的,箭头函数就没有,没有的不能通过new调用。
元属性new.target
元属性:非对象的属性。
当使用new调用构造函数时,new.target为当前实例的构造函数。不用new调用时为undefined。
在函数外使用new.target会报语法错误。
可以判断new.target===Pers代替之前的this intstanceof Pers:
function Pers(name){
if(new.target===Pers){
this.name=name;
}
else{
throw new Error("必须通过new调用");
}
}
var pr1=Pers("xiao"); //报错
块级函数
在代码块内:严格模式下声明的函数,会被提升至块级作用域顶部,并且与let表达式一样,块级代码执行完就销毁;非严格模式下,会被提升至外围函数作用域或者全局作用域顶部。(虽然有块级作用域,但只有let和const能体现,而这里也体现了es6的块级作用域,es5严格模式下不能在块级作用域声明函数,只能用表达式)。
"use strict"
sayName(); //报错
if(true){
sayName(); //可以
function sayName(){
alert("nihao");
}
}
箭头函数
- 没有argumets对象,所有使用时会通过作用域链找到外层非箭头函数的argums。可以用不定参数代替;
- 没有 [[construct]],不能通过new调用,也没new.target;
- 没有原型,即没有prototype属性;
- this始终是外层非箭头函数的this,没有外层非箭头函数则为window,不能改变this绑定;
- 不支持重复命名参数。
具体使用方式可以看P60。
箭头函数创建立即执行函数表达式时,小括号只包含函数,不包含调用的括号。非箭头函数没有这个限制。如下
(name=>{
console.log(name);
})("xiao");
适用场景:即用即弃,回调函数、匿名函数表达式,setTimeout()、事件、数组的迭代、归并方法、sort等。
可以使用typeof、instanceof测定箭头函数,和普通函数没什么区别。
尾调用优化
帮助函数保持一个更小的调用栈,从而减少内存使用,避免栈溢出错误。
可以尾调用优化的条件:
- 尾调用函数不是一个闭包
- 尾调用的结果作为值返回,没有任何修饰
尾调用优化常用于递归。如下代码可以优化阶乘函数性能:
//阶乘函数
function factorial(n,p=1){
if(n<=1){
return 1*p;
}
else{
p=p*n;
return factorial(n-1,p); //这步体现了尾调用优化
}
}
console.log(factorial(4)); //24