前言
ES6为我们在函数的使用上也提供了许多的便捷的东西,这里只阐述一部分,详情请参考官方文档。
1.函数参数的默认值
2.rest 参数
3.箭头函数(重点:this的指向问题)
一、函数参数的默认值
ES6 之前,不能直接为函数的参数指定默认值。而 ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x = 'Hello', y = 'World') {
console.log(x, y);
}
log() // Hello World
log('Hello', 'China') // Hello China
log('Hi', '') // Hi
length属性(直接获取所需传递的参数(是必须传递的参数),如果参数有默认值,则不会被计算)
function add(a,b = 1){
return a+b
}
console.log(add.length); // 1 , b有默认值,所以必须传递的只有a一个参数
二、rest 参数
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
注意:rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
function add(a, ...values) {
let sum = 0;
console.log(a); // 2
console.log(values); // [5, 3] , 剩余的参数会被放到数组之中
sum += a
for (var val of values) {
sum += val;
}
return sum;
}
console.log(add(2, 5, 3)); // 10
三、箭头函数
1.基本用法
(1)简化定义函数
var f = v => v;
// 等同于
var f = function (v) { return v; };
-----------------------------------------
var f = () => 5; // 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
// 等同于
var f = function () { return 5 };
------------------------------------------
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
(2) 可以和变量解构结合使用
const fullName = ({ firstName, lastName }) => firstName + ' ' + lastName ;
// 等同于
function fullName (person) {
return person.firstName + ' ' + person.lastName ;
}
(3) 简化回调函数
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x);
// 正常函数写法
var result = values.sort(function (a, b) {
return a - b;
});
// 箭头函数写法
var result = values.sort((a, b) => a - b);
2.使用注意点
箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
( 简单点说就是:
————> 如果箭头函数被一个函数或类包裹着,那他的this就是其包裹着它的函数或类的this;
————> 如果箭头函数外部没有函数包裹,则this指向window。 )
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。
箭头函数可以让函数里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子:
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数 , 没绑定 this,所以s2++ 不生效
setInterval(function () {
this.s2++;
}, 1000);
// 绑定this之后
// setInterval((function () {
// this.s2++;
// }).bind(this), 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
// 绑定this之后
// s2: 3
箭头函数可以让this指向固定化,这种特性很有利于封装回调函数。下面是一个例子,DOM 事件的回调函数封装在一个对象里面。
var handler = {
id: '123456',
init: function() {
document.addEventListener('click',
// this指向handler
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log('Handling ' + type + ' for ' + this.id);
}
};
上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。
3. 箭头函数不适用的场合
由于箭头函数使得this从“动态”变成“静态”,下面两个场合不应该使用箭头函数。
(1)第一个场合是定义对象的方法,且该方法内部包括this。
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。
(2)第二个场合是需要动态this的时候,也不应使用箭头函数。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。
(3)另外,如果函数体很复杂,有许多行,或者函数内部有大量的读写操作,不单纯是为了计算值,这时也不应该使用箭头函数,而是要使用普通函数,这样可以提高代码可读性。