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` because in JavaScript class bodies, all methods are strict mode by default, even if you don't add 'use strict'
显式绑定 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 应用程序至关重要。
延伸阅读