对于普通函数,this 的值基于函数如何被调用。对于箭头函数,this 的值基于函数周围的上下文。换句话说,箭头函数内的,this 的值与函数外面的 this 的值一样。
我们先看一个普通函数中的 this 示例,再看一个箭头函数是如何使用 this 的。
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
setTimeout(function() {
this.scoops++;
console.log('scoop added!');
}, 500);
};
const dessert = new IceCream();
dessert.addScoop();
Prints:
scoop added!
运行上述代码后,你会认为半毫秒之后,dessert.scoops 会是1。但并非这样:
console.log(dessert.scoops);
Prints:
0
####能说说原因吗?
传递给 setTimeout() 的函数被调用时没用到 new、call() 或 apply(),也没用到上下文对象。意味着函数内的 this 的值是全局对象,不是 dessert 对象。实际上发生的情况是,创建了新的 scoops 变量(默认值为 undefined),然后递增(undefined + 1 结果为 NaN):
console.log(scoops);
Prints:
NaN
解决这个问题的方法之一是使用闭包:
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
const cone = this; // sets `this` to the `cone` variable
setTimeout(function() {
cone.scoops++; // references the `cone` variable
console.log('scoop added!');
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
上述代码将可行,因为它没有在函数内使用 this,而是将 cone 变量设为 this,然后当函数被调用时查找 cone 变量。这样可行,因为使用了函数外面的 this 值。如果现在查看甜点中的勺子数量,正确值将为 1:
console.log(dessert.scoops);
Prints:
1
这正是箭头函数的作用,我们将传递给 setTimeout() 的函数替换为箭头函数:
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
setTimeout(() => { // an arrow function is passed to setTimeout
this.scoops++;
console.log('scoop added!');
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
因为箭头函数从周围上下文继承了 this 值,所以这段代码可行!
console.log(dessert.scoops);
Prints:
1
当 addScoop() 被调用时,addScoop() 中的 this 的值指的是 dessert。因为箭头函数被传递给 setTimeout(),它使用周围上下文判断它里面的 this 指的是什么。因为箭头函数外面的 this 指的是 dessert,所以箭头函数里面的 this 的值也将是 dessert。
如果我们将 addScoop() 方法改为箭头函数,你认为会发生什么?
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = () => { // addScoop is now an arrow function
setTimeout(() => {
this.scoops++;
console.log('scoop added!');
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
是的,这段代码因为同一原因而不起作用,即箭头函数从周围上下文中继承了 this 值。在 addScoop() 方法外面,this 的值是全局对象。因此如果 addScoop() 是箭头函数,addScoop() 中的 this 的值是全局对象。这样的话,传递给 setTimeout() 的函数中的 this 的值也设为了该全局对象!