此关键字

关键字 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 中,globalThisglobal 对象:

$ 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

在引入严格模式之前,thisnullundefined 值会被替换为对全局对象的引用。由于这种旧版行为,您有时可能会看到全局绑定称为“默认绑定”。

隐式绑定

将函数作为对象的方法进行调用时,该方法内的 this 实例会引用包含该方法的对象,从而授予对其旁边的方法和属性的访问权限:

let myObject = {
    myValue: "This is my string.",
    myMethod() {
            console.log( this.myValue );
    }
};

myObject.myMethod();
> "This is my string."

this 的值可能取决于函数及其封装对象的定义方式。相反,this 值的上下文是当前执行上下文。在这种情况下,执行上下文是 myObject 对象正在调用 myMethod 方法,因此 myObjectthis 的值。在前面的示例中,这似乎是一项技术性问题,但对于 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 );
> Window

虽然其他功能已经解决了 setTimeout 的这一特定缺点,但“丢失”this 的类似问题以前已经通过在预期上下文范围内创建对 this 值的显式引用来解决。在旧版代码库中,您可能偶尔会看到使用 thatself_this 等标识符分配给变量的 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 的值设置为 undefinednull,在严格模式之外,该值会被 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 值不会以任何方式强制转换为对象,即使是基元值、nullundefined 值也不例外:

"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 的值将再次由最近的封装执行上下文提供。在顶层,事件处理脚本回调函数中的 thisglobalThis(在严格模式下,为 undefined):

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 对象