箭头函数有几个使用注意点。

(1)函数体内的​​this​​对象,就是定义时所在的对象,而不是使用时所在的对象。(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

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​​。箭头函数可以让​​setTimeout​​里面的​​this​​,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子。

function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

上面代码中,​​Timer​​函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的​​this​​绑定定义时所在的作用域(即​​Timer​​函数),后者的​​this​​指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,​​timer.s1​​被更新了 3 次,而​​timer.s2​​一次都没更新。箭头函数可以让​​this​​指向固定化,这种特性很有利于封装回调函数。下面是一个例子,DOM 事件的回调函数封装在一个对象里面。

var handler = {
id: '123456',

init: function() {
document.addEventListener('click',
event => this.doSomething(event.type), false);
},

doSomething: function(type) {
console.log('Handling ' + type + ' for ' + this.id);
}
};

var handler = {
id: '123456',

init: function() {
document.addEventListener('click',
event => this.doSomething(event.type), false);
},

doSomething: function(type) {
console.log('Handling ' + type + ' for ' + this.id);
}
};

上面代码的​​init​​方法中,使用了箭头函数,这导致这个箭头函数里面的​​this​​,总是指向​​handler​​对象。否则,回调函数运行时,​​this.doSomething​​这一行会报错,因为此时​​this​​指向​​document​​对象。​​this​​指向的固定化,并不是因为箭头函数内部有绑定​​this​​的机制,实际原因是箭头函数根本没有自己的​​this​​,导致内部的​​this​​就是外层代码块的​​this​​。正是因为它没有​​this​​,所以也就不能用作构造函数。

所以,箭头函数转成 ES5 的代码如下。

// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}

// ES5
function foo() {
var _this = this;

setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}

// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}

// ES5
function foo() {
var _this = this;

setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}

上面代码中,转换后的 ES5 版本清楚地说明了,箭头函数里面根本没有自己的​​this​​,而是引用外层的​​this​​。请问下面的代码之中有几个​​this​​?

function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1

function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1

上面代码之中,只有一个​​this​​,就是函数​​foo​​的​​this​​,所以​​t1​​、​​t2​​、​​t3​​都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的​​this​​,它们的​​this​​其实都是最外层​​foo​​函数的​​this​​。除了​​this​​,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:​​arguments​​、​​super​​、​​new.target​​。

function foo() {
setTimeout(() => {
console.log('args:', arguments);
}, 100);
}

foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]

function foo() {
setTimeout(() => {
console.log('args:', arguments);
}, 100);
}

foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]

上面代码中,箭头函数内部的变量​​arguments​​,其实是函数​​foo​​的​​arguments​​变量。另外,由于箭头函数没有自己的​​this​​,所以当然也就不能用​​call()​​、​​apply()​​、​​bind()​​这些方法去改变​​this​​的指向。

(function() {
return [
(() => this.x).bind({ x: 'inner' })()
];
}).call({ x: 'outer' });
// ['outer']

(function() {
return [
(() => this.x).bind({ x: 'inner' })()
];
}).call({ x: 'outer' });
// ['outer']

上面代码中,箭头函数没有自己的​​this​​,所以​​bind​​方法无效,内部的​​this​​指向外部的​​this​​。长期以来,JavaScript 语言的​​this​​对象一直是一个令人头痛的问题,在对象方法中使用​​this​​,必须非常小心。箭头函数”绑定”​​this​​,很大程度上解决了这个困扰。

文章摘自阮一峰大神的ES6这本书中:​​http://es6.ruanyifeng.com/#docs/function#%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0​