simple's Studio.

this和箭头函数

2017/11/03

对于普通函数,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 的值也设为了该全局对象!

CATALOG