面试题:️ES6 箭头函数的 this 指向哪⾥?

ES6 箭头函数(Arrow Functions)最核心的特性之一就是它不绑定自己的 this。箭头函数中的 this 指向是在函数被创建时就确定的,它继承自外层(包裹它的)普通函数或全局作用域的 this。这种机制被称为 this 的词法绑定(Lexical this

这与传统的普通函数(使用 function 关键字定义)形成鲜明对比,普通函数的 this 是在函数被调用时动态确定的。


1. 箭头函数 this 的核心规则

  • 没有自己的 this:箭头函数在执行时,不会创建自己的 this 上下文。
  • 继承外层 this:它会沿着作用域链向上查找,使用最近的非箭头函数this 值。
  • 创建时决定this 的指向在箭头函数被定义时就已经确定,无法通过 callapplybind 方法改变。

2. 与普通函数的对比示例

场景一:作为对象方法

const person = {
  name: 'Alice',
  // 普通函数:this 指向调用者 (person)
  greetNormal: function() {
    console.log(this.name); // 'Alice'
  },
  // 箭头函数:this 不指向 person!
  greetArrow: () => {
    console.log(this.name); // undefined (在浏览器中通常是 window.name)
  }
};

person.greetNormal(); // 'Alice'
person.greetArrow();  // undefined 或空字符串
  • 解释greetArrow 是一个箭头函数,它没有自己的 this。它的外层是全局作用域(或模块作用域),所以 this 指向全局对象(浏览器中是 window)。window 没有 name 属性,因此输出 undefined

场景二:在 setTimeout 或事件回调中

这是箭头函数 this 优势最明显的场景。

function Timer() {
  this.seconds = 0;

  // 普通函数作为回调:this 会丢失!
  setInterval(function() {
    this.seconds++; // 错误!这里的 this 通常指向全局对象或 undefined (严格模式)
    console.log(this.seconds); // NaN 或报错
  }, 1000);

  // 箭头函数作为回调:完美继承外层 this!
  setInterval(() => {
    this.seconds++; // 正确!this 指向 Timer 实例
    console.log(this.seconds); // 每秒输出递增的数字
  }, 1000);
}

const timer = new Timer();
  • 解释:第一个 setInterval 的回调是普通函数,当它执行时,this 被设置为调用它的环境(通常是全局对象),不再是 Timer 实例。而箭头函数 () => { ... }this 继承自外层的 Timer 构造函数,因此 this 正确地指向了 Timer 的实例。

场景三:在数组方法(如 map, filter)中

const numbers = [1, 2, 3];
const multiplier = {
  factor: 10,
  // 普通函数:需要保存 this
  multiplyNormal: function() {
    const self = this; // 保存 this
    return numbers.map(function(num) {
      return num * self.factor; // 必须用 self,不能用 this
    });
  },
  // 箭头函数:直接使用 this
  multiplyArrow: function() {
    return numbers.map(num => num * this.factor); // this 指向 multiplier 对象
  }
};

console.log(multiplier.multiplyNormal()); // [10, 20, 30]
console.log(multiplier.multiplyArrow());  // [10, 20, 30]
  • 解释:在 multiplyNormal 中,map 的回调是普通函数,它有自己的 this(不是 multiplier),所以必须用 self = this 来保存外层的 this。而在 multiplyArrow 中,箭头函数直接继承了 multiplyArrow 方法的 this(即 multiplier 对象),可以直接使用 this.factor

3. 箭头函数 this 的“不可变性”

你无法通过 callapplybind 来改变箭头函数的 this 指向。

const arrowFunc = () => console.log(this);
const obj = { name: 'Bob' };

// 以下调用都无法改变 arrowFunc 内部的 this
arrowFunc.call(obj);   // 仍然输出全局对象的属性
arrowFunc.apply(obj);  // 同上
arrowFunc.bind(obj)(); // 同上

4. 总结

  • 箭头函数没有自己的 this
  • this 指向外层作用域的 this,在函数创建时就已确定(词法绑定)。
  • 不能用 callapplybind 改变其 this
  • 适用场景
    • 作为回调函数(如 setTimeout, setInterval, 事件处理器)。
    • 在数组方法(map, filter, reduce)的回调中。
    • 需要确保 this 指向外层函数的场景。
  • 不适用场景
    • 作为对象的方法(除非你明确知道 this 会指向全局)。
    • 需要动态 this 的场景(如构造函数、需要 this 指向调用者的函数)。
    • 需要使用 arguments 对象的场景(箭头函数也没有自己的 arguments)。

简单来说,记住:箭头函数的 this 指向它被定义时所处的环境的 this。这个特性使得它在处理回调时非常方便,避免了 this 丢失的问题。

THE END
喜欢就支持一下吧
点赞11 分享