JavaScript 中的 `this` 详解

这是前端面试问题系列的第 5 个问题。如果您希望提高准备水平或保持更新,请考虑注册 Frontend Camp。

`this` 关键字始终指代函数或脚本的当前上下文。

“这”对于我们大多数人来说都是一个令人困惑的话题(双关语),但事实并非如此。您需要做的就是记住一些规则。

以下规则按优先顺序规定了在运行时如何确定“this”的值:

在函数构造函数中的使用

如果使用“new”关键字调用函数,则函数内部的“this”指的是新创建的对象实例。

function Book(title) {
  console.log(this);
  this.title = title;
  console.log(this);
}

const book1 = new Book('Jungle Book');

// {}
// { title: "'Jungle Book' }"

console.log(book1.title); // 'Jungle Book'

明确绑定此

`call()`、`apply()` 或 `bind()` 可用于明确设置函数内部 `this` 的值。

  • 当需要立即调用函数时使用 call() 和 apply()。
  • bind() 返回一个新的函数以供稍后使用。
  • const obj = { name: 'Ben' };
    
    function sayHello() {
      console.log(`Hello, ${this.name}!`);
    }
    
    const sayHelloToBen = sayHello.bind(obj);
    
    sayHelloToBen(); // Hello, Ben!
    sayHello.call(obj); // Hello, Ben!
    sayHello.apply(obj); // Hello, Ben!

    在方法调用中的用法

    如果函数是对象的方法,那么“this”指的是该对象。

    const person = {
      name: 'Ben',
      logThis: function() {
        console.log(this);
      }
    }
    
    person.logThis(); // { name: 'Ben', logThis: fn() }

    在常规函数调用(自由函数调用)中的使用

    如果在全局上下文中调用函数,则“this”引用全局对象(非严格模式下)或“undefined”(严格模式下)。

    对于浏览器来说,全局对象是‘window’。

    // Browser
    
    function showThis() {
      console.log(this);
    }
    
    showThis(); // Window { open: fn, alert: fn, ... }

    当函数在全局上下文中声明时,它将成为 `window` 对象的属性。调用 `window.showThis()` 会产生相同的结果。这就是为什么它是一个隐式方法调用。(请参阅上面的规则)

    **如果适用多条规则,则将应用优先级较高的规则。**

    const obj1 = {
        value: 1,
        showThis: function() {
            console.log(this);
        },
    };
    
    const obj2 = { value: 2 };
    
    obj1.showThis.call(obj2); // { value: 2 }

    在上面的例子中,应用了两个规则:方法调用和显式绑定。由于显式绑定具有更高的优先级,因此它可以设置“this”的值。

    箭头函数

    箭头函数不遵循上述规则,因为它们没有自己的 `this` 值。它们从 **父作用域** 中选择 `this` 值。

    const person = {
      name: 'Ben',
      showThis: () => {
        console.log(this);
      },
      showThisWrapped: function() {
        const arrowFn = () => console.log(this);
        arrowFn();
      }
    }
    
    person.showThis(); // Window { open: fn, alert: fn, ... }
    person.showThisWrapped(); // { name: 'Ben', showThis: fn, showThisWrapped: fn }

    这就是为什么你应该避免使用箭头函数来监听事件。事件监听器将 HTML 元素绑定到 `this` 值,但如果处理程序是箭头函数,则不可能。

    document.querySelector('#cta').addEventListener('click', function(){
     console.log(this); // HTMLButtonElement
    })
    
    document.querySelector('#cancel').addEventListener('click', () => {
     console.log(this); // Window
    })

    概括

  • 在函数构造函数(new Person())中,这将是新创建的对象实例。
  • 如果使用 bind()、call() 或 apply() 明确绑定,则函数内部的 this 将被设置为提供的值。
  • 在方法内部,this 被设置为该方法所属的对象。
  • 在自由函数调用中,this 指向全局对象(非严格模式)或未定义(严格模式)。
  • 如果适用多条规则,则将适用最先陈述的规则(1-4)。
  • 箭头函数没有自己的 this。它的值是从父作用域中获取的。