this 关键字

关键字 this 指的是在函数调用时绑定到该函数的对象的 value。这意味着,当函数作为方法、独立函数或 构造函数 调用时,它的 value 是不同的。

当函数被调用时,它会在后台创建关键字 this 的实例,作为对包含该函数的对象的引用,从而可以从其作用域内访问与其一起定义的属性和方法。在某些方面,使用 this 类似于使用用 const 声明的变量。与常量一样,this 无法删除,并且其 value 无法重新赋值,但可以更改 this 关键字包含的对象的方法和属性。

全局绑定

在函数或对象的上下文之外,this 指的是 globalThis 属性,该属性是对大多数 JavaScript 环境中的全局对象的引用。在 Web 浏览器中运行的脚本上下文中,全局对象是 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 的 value 看起来可能取决于函数及其封闭对象的定义方式。实际上,this 的 value 的上下文是当前的执行上下文。在本例中,执行上下文是 myObject 对象正在调用 myMethod 方法,因此 myObjectthis 的 value。在前面的示例上下文中,这似乎是一个技术细节,但对于 this 的更高级用法,这是一个需要牢记的重要区别。

通常,以不期望周围代码具有任何特定结构的方式使用 this。此规则的例外是 ES5 箭头函数

箭头函数中的 this

箭头函数 中,this 解析为 词法封闭环境 中的绑定。这意味着箭头函数中的 this 指的是该函数最近的封闭上下文中 this 的 value

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 的 value,即包含该箭头函数的方法。由于 myEnclosingMethod 内部的 this 的 value 指的是 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 的 value 来表示特定的执行上下文,而不是假定的上下文。一个说明性的(如果有点过时)示例是在 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 的 value 的显式引用来解决类似的“丢失”this 问题。您有时可能会在旧代码库中看到使用 thatself_this 等标识符将 this 分配给变量的实例。这些是包含传递的 this value 的变量的常用标识符约定。

当您使用 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 value。

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 的 value 设置为 undefinednull,则在非严格模式下,该 value 将被 globalThis 替换

let myFunction = function() {
    console.log( this );
}

myFunction.call( null );
> Window {...}

同样,如果以某种方式调用函数,使 this 具有原始 value,则在非严格模式下,该 value 将被替换为原始 value 的包装器对象

let myFunction = function() {
    console.log( this );
}

let myNumber = 10;

myFunction.call( myNumber );
> Number { 10 }

在严格模式下,传递的 this value 不会以任何方式强制转换为对象,即使它是原始值、nullundefined value 也是如此

"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 的 value 指的是正在创建的对象

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 的 value 引用调用它的对象。在事件处理程序的回调函数内部,这意味着 this 引用与处理程序关联的元素

let button = document.querySelector( "button" );

button.addEventListener( "click", function( event ) { console.log( this ); } );

当用户与上一个代码段中的 button 交互时,结果是包含 <button> 本身的元素对象

> Button {}

当箭头函数用作事件监听器回调时,this 的 value 再次由最近的封闭执行上下文提供。在顶层,这意味着事件处理程序回调函数内部的 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 }

检查您的理解情况

对于在 Web 浏览器中运行的脚本,当在函数或对象的上下文之外使用 this 时,this 指的是哪个全局对象?

window 对象
browser 对象
undefined 对象