关键字 this 是指在调用函数时绑定到该函数的对象的值,这意味着其值因函数是作为方法、作为独立函数还是作为构造函数调用而异。
调用函数时,它会在后台创建关键字 this 的实例,作为对包含该函数的对象的引用,从而允许从其作用域内访问与该函数一起定义的属性和方法。在某些方面,使用 this 与使用 const 声明的变量类似。与常量一样,this 无法移除,其值也无法重新分配,但 this 关键字包含的对象的方法和属性可以更改。
全局绑定
在函数或对象上下文之外,this 是指 globalThis 属性,它是指向大多数 JavaScript 环境中的全局对象的引用。在网络浏览器中运行的脚本的上下文中,全局对象是 window 对象:
this; > Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, ...} 在 Node.js 中,globalThis 是 global 对象:
$ node Welcome to Node.js v20.10.0. Type ".help" for more information. > this <ref *1> Object [global] { ... } 在严格模式之外,this 还会引用独立函数内的全局对象,因为父 Window 是有效“拥有”这些函数的对象。
function myFunction() { console.log( this ); } myFunction(); > Window {...} (function() { console.log( this ); }()); > Window {...} 使用严格模式时,this 在独立函数内的值为 undefined:
(function() { "use strict"; console.log( this ); }()); > undefined 在引入严格模式之前,this 的 null 或 undefined 值会被替换为对全局对象的引用。由于这种旧版行为,您有时可能会看到全局绑定被称为“默认绑定”。
隐式绑定
将函数作为对象的方法调用时,该方法内的 this 实例会引用包含该方法的对象,从而可以访问与该方法并存的方法和属性:
let myObject = { myValue: "This is my string.", myMethod() { console.log( this.myValue ); } }; myObject.myMethod(); > "This is my string." 看起来 this 的值取决于函数及其封闭对象的定义方式。相反,this 值的上下文是当前的执行上下文。在本例中,执行上下文是 myObject 对象调用 myMethod 方法,因此 myObject 是 this 的值。在前面的示例中,这可能看起来像是技术细节,但对于 this 的更高级用法,这是一个需要牢记的重要区别。
通常,使用 this 时,不应要求周围代码具有任何特定结构。这条规则的例外情况是 ES5 箭头函数。
箭头函数中的 this
在箭头函数中,this 会解析为词法封闭环境中的绑定。这意味着箭头函数中的 this 是指该函数最外层封闭上下文中的 this 值:
let myObject = { myMethod() { console.log( this ); }, myArrowFunction: () => console.log( this ), myEnclosingMethod: function () { this.myArrowFunction = () => { console.log(this) }; } }; myObject.myMethod(); > Object { myMethod: myMethod(), myArrowFunction: myArrowFunction() } myObject.myArrowFunction(); > Window {...} 在前面的示例中,myObject.myMethod() 会将 myObject 记录为“拥有”该方法的对象,但 myObject.myArrowFunction() 会返回 globalThis(或 undefined),因为箭头函数内的 this 实例实际上是指向最外层封闭作用域。
在以下示例中,myEnclosingMethod 会在执行时在包含它的对象上创建箭头函数。箭头函数内的 this 实例现在是指封闭环境(即包含该箭头函数的方法)内的 this 值。由于 myEnclosingMethod 中的 this 值引用了 myObject,因此在您定义箭头函数后,箭头函数中的 this 也会引用 myObject:
let myObject = { myMethod() { console.log( this ); }, myEnclosingMethod: function () { this.myArrowFunction = () => { console.log(this) }; } }; myObject.myEnclosingMethod(); myObject.myArrowFunction(); > Object { myMethod: myMethod(), myArrowFunction: myArrowFunction() } 显式绑定
隐式绑定可处理与 this 相关的大多数用例。不过,您有时可能需要 this 的值来表示特定执行上下文,而不是假定上下文。下面是一个示例(虽然略微过时),它展示了如何在 setTimeout 的回调函数中使用 this,因为此回调具有唯一的执行上下文:
var myObject = { myString: "This is my string.", myMethod() { console.log( this.myString ); } }; myObject.myMethod(); > "This is my string." setTimeout( myObject.myMethod, 100 ); > undefined 虽然 setTimeout 的这一缺点后来已通过其他功能得到了解决,但之前我们曾通过在预期上下文范围内创建对 this 值的显式引用来解决“丢失”this 的类似问题。在旧版代码库中,您偶尔可能会看到 this 实例被分配给使用 that、self 或 _this 等标识符的变量。以下是包含传递的 this 值的变量的常见标识符惯例。
当您使用 call()、bind() 或 apply() 方法调用函数时,this 会明确引用被调用的对象:
let myFunction = function() { console.log( this.myValue ); } let myObject = { "myValue" : "This is my string." }; myFunction.call( myObject ); > "This is my string." var myObject = { myString: "This is my string.", myMethod() { console.log( this.myString ); } }; setTimeout( myObject.myMethod.bind( myObject ), 100 ); > "This is my string." 显式绑定会替换隐式绑定提供的 this 值。
let myObject = { "myValue" : "This string sits alongside myMethod.", myMethod() { console.log( this.myValue ); } }; let myOtherObject = { "myValue" : "This is a string in another object entirely.", }; myObject.myMethod.call( myOtherObject ); > "This is a string in another object entirely." 如果以将 this 的值设置为 undefined 或 null 的方式调用函数,则该值会在严格模式下替换为 globalThis:
let myFunction = function() { console.log( this ); } myFunction.call( null ); > Window {...} 同样,如果以会向 this 提供基元值的方式调用函数,则该值会在严格模式之外替换为基元值的封装容器对象:
let myFunction = function() { console.log( this ); } let myNumber = 10; myFunction.call( myNumber ); > Number { 10 } 在严格模式下,传入的 this 值不会以任何方式强制转换为对象,即使它是基元值、null 值或 undefined 值也是如此:
"use strict"; let myFunction = function() { console.log( this ); } let myNumber = 10; myFunction.call( myNumber ); > 10 myFunction.call( null ); > null new 绑定
使用 new 关键字将类用作构造函数时,this 会引用新创建的实例:
class MyClass { myString; constructor() { this.myString = "My string."; } logThis() { console.log( this ); } } const thisClass = new MyClass(); thisClass.logThis(); > Object { myString: "My string." } 同样,使用 new 调用的构造函数内的 this 值也指向要创建的对象:
function MyFunction() { this.myString = "My string."; this.logThis = function() { console.log( this ); } } const myObject = new MyFunction(); myObject.logThis(); > Object { myString: "My string.", logThis: logThis() } 事件处理脚本绑定
在事件处理程序上下文中,this 的值引用调用它的对象。在事件处理脚本的回调函数内,这意味着 this 会引用与处理脚本关联的元素:
let button = document.querySelector( "button" ); button.addEventListener( "click", function( event ) { console.log( this ); } ); 当用户与上一部分代码中的 button 交互时,结果是包含 <button> 本身的元素对象:
> Button {} 将箭头函数用作事件监听器回调时,最接近的封闭式执行上下文再次提供 this 的值。从顶层来看,这意味着事件处理程序回调函数内的 this 为 globalThis:
let button = document.querySelector( "button" ); button.addEventListener( "click", ( event ) => { console.log( this ); } ); > undefined 与任何其他对象一样,当您使用 call()、bind() 或 apply() 方法引用事件监听器的回调函数时,this 会显式引用该对象:
let button = document.querySelector( "button" ); let myObject = { "myValue" : true }; function handleClick() { console.log( this ); } button.addEventListener( "click", handleClick.bind( myObject ) ); > Object { myValue: true } 检查您的理解情况
对于在网络浏览器中运行的脚本,当 this 在函数或对象上下文之外使用时,它会引用哪个全局对象?
window 对象browser 对象undefined 对象