解释 JavaScript 中 `this` 的工作原理
TL;DR
this
没有简单的解释;它是 JavaScript 中最令人困惑的概念之一,因为它的行为与其他许多编程语言不同。this
关键字的单行解释是,它是一个对函数执行上下文的动态引用。
更长的解释是,this
遵循以下规则:
- 如果在调用函数时使用了
new
关键字,这意味着该函数被用作函数构造函数,则函数内部的this
是新创建的对象实例。 - 如果在
class
constructor
中使用了this
,则constructor
内部的this
是新创建的对象实例。 - 如果使用
apply()
、call()
或bind()
调用/创建函数,则函数内部的this
是作为参数传入的对象。 - 如果一个函数被调用为一个方法(例如
obj.method()
)——this
是该函数所属的对象。 - 如果一个函数被调用为自由函数调用,这意味着它是在没有任何上述条件的情况下被调用的,
this
是全局对象。在浏览器中,全局对象是window
对象。如果在严格模式 ('use strict';
) 中,this
将是undefined
而不是全局对象。 - 如果应用了多个上述规则,则优先级较高的规则将获胜并设置
this
的值。 - 如果该函数是 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: Bobobj.regularFunction.apply(anotherObj); // Regular function: Bobconst 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 应用程序至关重要。