测验

解释 JavaScript 中 `this` 的工作原理

主题
JavaScriptOOP
在GitHub上编辑

TL;DR

this 没有简单的解释;它是 JavaScript 中最令人困惑的概念之一,因为它的行为与其他许多编程语言不同。this 关键字的单行解释是,它是一个对函数执行上下文的动态引用。

更长的解释是,this 遵循以下规则:

  1. 如果在调用函数时使用了 new 关键字,这意味着该函数被用作函数构造函数,则函数内部的 this 是新创建的对象实例。
  2. 如果在 class constructor 中使用了 this,则 constructor 内部的 this 是新创建的对象实例。
  3. 如果使用 apply()call()bind() 调用/创建函数,则函数内部的 this 是作为参数传入的对象。
  4. 如果一个函数被调用为一个方法(例如 obj.method())——this 是该函数所属的对象。
  5. 如果一个函数被调用为自由函数调用,这意味着它是在没有任何上述条件的情况下被调用的,this 是全局对象。在浏览器中,全局对象是 window 对象。如果在严格模式 ('use strict';) 中,this 将是 undefined 而不是全局对象。
  6. 如果应用了多个上述规则,则优先级较高的规则将获胜并设置 this 的值。
  7. 如果该函数是 ES2015 箭头函数,它会忽略上述所有规则,并在创建时接收其周围作用域的 this 值。

如需深入解释,请查看 Arnav Aggrawal 在 Medium 上的文章


this 关键字

在 JavaScript 中,this 是一个关键字,它引用函数或脚本的当前执行上下文。这是 JavaScript 中的一个基本概念,理解 this 的工作原理对于构建健壮且可维护的应用程序至关重要。

全局使用

在全局范围内,this 引用全局对象,在 Web 浏览器中是 window 对象,在 Node.js 环境中是 global 对象。

console.log(this); // 在浏览器中,这将记录 window 对象(对于非严格模式)。

在常规函数调用中

当一个函数在全局上下文中或作为独立函数被调用时,this 引用全局对象(在非严格模式下)或 undefined(在严格模式下)。

function showThis() {
console.log(this);
}
showThis(); // 在非严格模式下:Window(全局对象)。在严格模式下:undefined。

在方法调用中

当一个函数作为对象的方法被调用时,this 引用该方法被调用的对象。

const obj = {
name: 'John',
showThis: function () {
console.log(this);
},
};
obj.showThis(); // { name: 'John', showThis: ƒ }

请注意,如果你这样做,它就和常规函数调用一样,而不是方法调用。this 已经失去了它的上下文,不再指向 obj

const obj = {
name: 'John',
showThis: function () {
console.log(this);
},
};
const showThisStandalone = obj.showThis;
showThisStandalone(); // 在非严格模式下:Window(全局对象)。在严格模式下:undefined。

在函数构造器中

当一个函数被用作构造器(使用 new 关键字调用)时,this 指的是新创建的实例。在下面的例子中,this 指的是正在创建的 Person 对象,并且 name 属性是在该对象上设置的。

function Person(name) {
this.name = name;
}
const person = new Person('John');
console.log(person.name); // "John"

在类构造函数和方法中

在 ES2015 类中,this 的行为与在对象方法中一样。它指的是类的实例。

class Person {
constructor(name) {
this.name = name;
}
showThis() {
console.log(this);
}
}
const person = new Person('John');
person.showThis(); // Person {name: 'John'}
const showThisStandalone = person.showThis;
showThisStandalone(); // `undefined` 因为类的所有部分都是严格模式。

显式绑定 this

你可以使用 bind()call()apply() 来显式设置函数的 this 的值。

使用 call()apply() 方法允许您在调用函数时显式设置 this 的值。

function showThis() {
console.log(this);
}
const obj = { name: 'John' };
showThis.call(obj); // { name: 'John' }
showThis.apply(obj); // { name: 'John' }

bind() 方法创建一个新函数,并将 this 绑定到指定的值。

function showThis() {
console.log(this);
}
const obj = { name: 'John' };
const boundFunc = showThis.bind(obj);
boundFunc(); // { name: 'John' }

在箭头函数中

箭头函数没有自己的 this 上下文。相反,this 是词法作用域的,这意味着它继承了定义时周围作用域的 this 值。

在这个例子中,this 指的是全局对象(window 或 global),因为箭头函数没有绑定到 person 对象。

const person = {
firstName: 'John',
sayHello: () => {
console.log(`Hello, my name is ${this.firstName}!`);
},
};
person.sayHello(); // "Hello, my name is undefined!"

在下面的例子中,箭头函数中的 this 将是其封闭上下文的 this 值,因此它取决于 showThis() 的调用方式。

const obj = {
name: 'John',
showThis: function () {
const arrowFunc = () => {
console.log(this);
};
arrowFunc();
},
};
obj.showThis(); // { name: 'John', showThis: ƒ }
const showThisStandalone = obj.showThis;
showThisStandalone(); // 在非严格模式下:Window(全局对象)。在严格模式下:undefined。

因此,箭头函数中的 this 值不能通过 bind()apply()call() 方法设置,它也不会指向对象方法中的当前对象。

const obj = {
name: 'Alice',
regularFunction: function () {
console.log('Regular function:', this.name);
},
arrowFunction: () => {
console.log('Arrow function:', this.name);
},
};
const anotherObj = {
name: 'Bob',
};
// 使用 call/apply/bind 和常规函数
obj.regularFunction.call(anotherObj); // Regular function: Bob
obj.regularFunction.apply(anotherObj); // Regular function: Bob
const boundRegularFunction = obj.regularFunction.bind(anotherObj);
boundRegularFunction(); // Regular function: Bob
// 使用 call/apply/bind 和箭头函数,`this` 指的是全局作用域,并且不能被修改。
obj.arrowFunction.call(anotherObj); // Arrow function: window/undefined (depending if strict mode)
obj.arrowFunction.apply(anotherObj); // Arrow function: window/undefined (depending if strict mode)
const boundArrowFunction = obj.arrowFunction.bind(anotherObj);
boundArrowFunction(); // Arrow function: window/undefined (depending if strict mode)

在事件处理程序中

当一个函数被调用为 DOM 事件处理程序时,this 指的是触发该事件的元素。在这个例子中,this 指的是被点击的 <button> 元素。

<button id="my-button" onclick="console.log(this)">Click me</button>
<!-- Logs the button element -->

使用 JavaScript 设置事件处理程序时,this 也指的是接收事件的元素。

document.getElementById('my-button').addEventListener('click', function () {
console.log(this); // Logs the button element
});

如上所述,ES2015 引入了 箭头函数,它使用 封闭的词法范围。这通常很方便,但确实阻止了调用者通过 .call/.apply/.bind 定义 this 上下文。其中一个后果是,如果您使用箭头函数定义 .addEventListener() 的回调参数,DOM 事件处理程序将无法在您的事件处理程序函数中正确绑定 this

document.getElementById('my-button').addEventListener('click', () => {
console.log(this); // Window / undefined (depending on whether strict mode) instead of the button element.
});

总而言之,JavaScript 中的 this 指的是函数或脚本的当前执行上下文,其值可以根据使用它的上下文而改变。理解 this 的工作方式对于构建健壮且可维护的 JavaScript 应用程序至关重要。

延伸阅读

在GitHub上编辑