关键字 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
对象