此关键字

关键字 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 );
> undefined

虽然 setTimeout 的这一缺点后来已通过其他功能得到了解决,但之前我们曾通过在预期上下文范围内创建对 this 值的显式引用来解决“丢失”this 的类似问题。在旧版代码库中,您偶尔可能会看到 this 实例被分配给使用 thatself_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 值不会以任何方式强制转换为对象,即使它是基元值、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 的值。从顶层来看,这意味着事件处理程序回调函数内的 thisglobalThis

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