1. 函数参数的默认值
在ES6中,函数的参数可用设置默认值,即直接卸载参数定义的后面。通常与结构赋值结合使用。
function f({x,y=5}={}){
console.log(x,y);
}
f({});//undefined 5
f({x:1});//1 5
f({x:1,y:2});//1 2
f(); undefined 5
使用默认值的参数应该在无默认值参数的后面定义,因为在函数使用时会根据参数顺序传参,除非显示输入undefined,但null不会触发默认参数。
function f(x=5,y){
console.log(x,y);
}
f(2,3);//2 3
f(1);//1 undefined
f(undefined,3);//5 3
f(null);//null undefined
function fn(y,x=5){
console.log(y,x);
}
fn(2,3);//2 3
fn(1);//1 5
函数的length属性:
表示该函数预期传入的参数个数。
length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
如果有默认值参数不是尾参数,length属性也不会再计入后面无默认值参数的个数。
length属性不包括rest参数。
console.log((function f(a,b,c,d,e){}).length); //5
console.log((function f(a,b,c=3,d=4,e=5){}).length);//2
console.log((function f(a=2,b,c,d,e){}).length);//0
console.log((function(a,b ,...c) {}).length);//2
有默认值参数的作用域:
有默认值参数的函数进行声明初始化时,参数会形成一个单独的作用域,初始化结束时,该作用域就会消失。当默认值是变量时,会依次往上查找,如果未找到变量的定义,就会报错。
/* var x=1;
function f(x,y=x){
console.log(x,y);
}
f(2);//2 2 */
/* let x=1
function f(y=x){
//let x=2;
console.log(x,y);
}
f();//1 1
f(2);//1 2 */
function f(y=x){
let x=1;
console.log(x,y);
}
f(2);//1 2
f();//Uncaught ReferenceError: x is not defined
应用:指定参数不能省略,或者给参数添加限定
function throwError(){
throw new Error("Missing parameter ");
}
function f(para=throwError()){
console.log(para);
}
f(1);//1
f();//Uncaught Error: Missing parameter
当函数作为值时,加了圆括号表示运行时执行,没有表示定义时执行。
2.rest参数
rest参数用于获取函数多余的参数,该参数是一个数组,多余参数都在数组中。
rest参数必须是最后一个参数,否则会报错。
function add(...numbers){//求和函数
let sum=0;
for(let n of numbers){
sum+=n;
}
console.log(sum);
}
add(1,2,3,4,5);//15
function fn(array,...items){
items.forEach(function(item){
array.push(item);
console.log(item);
});
console.log(array);
}
var a=[];
fn(a,1,2,3);//1 2 3
function f(a,...b ,c) {
};//Uncaught SyntaxError: Rest parameter must be last formal parameter
ES6规定只要函数参数使用了默认值、结构赋值、或者扩展原酸符,那么函数内部就不能显示设定为严格模式,否则会报错。
3.箭头函数
ES6允许使用箭头(=> )定义函数
//箭头函数
let f=(v1,v2)=>{return v1+v2;};
//等同于标准形式
let fn=function (v1,v2){
return v1+v2;
};
console.log(f(1,2));//3
console.log(fn(1,2));//3
圆括号内表示参数部分,多个参数和无参数必须使用圆括号;大括号内放代码块,多行代码必须使用大括号。
箭头函数的特殊形式:
如果是单一变量,参数部分可以省略圆括号;如果直接返回结果,可以省略大括号和return关键字 。
let f=v=>v;
//等同于
let fn=function (v){
return v;
}
console.log(f(1));//1
console.log(fn(1));//1
let fun=(num1,num2)=>num1+num2;
//等同于
let func=function (num1,num2){
return num1+num2;
}
console.log(fun(1,2));//3
console.log(func(1,2));//3
let fun=()=>{return 5;};
console.log(fun());//5
如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let f=name=>({name:name,sex:"男"});
console.log(f("名字"));
let fn=name=>{name:name,sex:"男"};//报错Uncaught SyntaxError
如果箭头函数只有一行语句,且不需要返回值,可以直接声明。
let fn = () => void doesNotReturn();//不知道为什么使用该方法报错
//Uncaught ReferenceError: doesNotReturn is not defined
搭配rest参数使用。
let f=(...values)=>values;
console.log(f(1,2,3,4,5));//[1, 2, 3, 4, 5]
let fn=(head,...values)=>[head,values];
console.log(fn(1,2,3,4,5));//[1,[2, 3, 4, 5]]
箭头函数使用注意:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。箭头函数可以让this指向固定化,这种特性很有利于封装回调函数。
除了this,arguments、super、new.target变量在箭头函数之中也是不存在的,指向外层函数的对应变量。
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// 等同于ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
函数的尾调用
某个函数的最后一步是调用另一个函数。尾调用的另一个函数不能执行其他操作。尾调用不一定出现在函数尾部,只要是最后一步操作即可。
尾调用优化能大大降低“调用帧”,节省内存空间。
函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。