this

this 关键字引用代码片段(例如函数体)应该运行的上下文。最典型的是在对象方法中使用它,其中 this 指向方法所附加的对象,从而允许在不同对象上重用相同方法。

JavaScript 中 this 的值取决于函数的调用方式(运行时绑定),而不是定义方式。当常规函数作为对象的某个方法调用时(obj.method()),this 指向该对象。当作为独立函数调用时(不附加到任何对象:func()),this 通常引用全局对象(在非严格模式下)或 undefined(在严格模式下)。Function.prototype.bind() 方法可以创建一个 this 绑定不会改变的函数,而方法 Function.prototype.apply()Function.prototype.call() 也可以为特定调用设置 this 值。

箭头函数 在处理 this 方面有所不同:它们继承定义时父作用域的 this。这种行为使得箭头函数特别适用于回调和保留上下文。但是,箭头函数没有自己的 this 绑定。因此,它们的 this 值不能通过 bind()apply()call() 方法设置,也不会指向对象方法中的当前对象。

试试看

语法

js
this

在非严格模式下,this 始终是对对象的引用。在严格模式下,它可以是任何值。有关如何确定值的更多信息,请参见下面的描述。

描述

this 的值取决于它出现的上下文:函数、类或全局。

函数上下文

在函数内部,this 的值取决于函数的调用方式。将 this 视为函数的隐藏参数 - 就像函数定义中声明的参数一样,this 是在评估函数体时语言为您创建的绑定。

对于常规函数(不是箭头函数、绑定函数等),this 的值是函数访问的对象。换句话说,如果函数调用采用 obj.f() 的形式,那么 this 指向 obj。例如

js
function getThis() {
  return this;
}

const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };

obj1.getThis = getThis;
obj2.getThis = getThis;

console.log(obj1.getThis()); // { name: 'obj1', getThis: [Function: getThis] }
console.log(obj2.getThis()); // { name: 'obj2', getThis: [Function: getThis] }

注意,函数是相同的,但根据调用方式,this 的值不同。这类似于函数参数的工作方式。

this 的值不是将函数作为自身属性的对象,而是用来调用函数的对象。您可以通过在原型链中向上调用对象的方法来证明这一点。

js
const obj3 = {
  __proto__: obj1,
  name: "obj3",
};

console.log(obj3.getThis()); // { name: 'obj3' }

this 的值始终根据函数的调用方式而改变,即使函数在创建时是在对象上定义的

js
const obj4 = {
  name: "obj4",
  getThis() {
    return this;
  },
};

const obj5 = { name: "obj5" };

obj5.getThis = obj4.getThis;
console.log(obj5.getThis()); // { name: 'obj5', getThis: [Function: getThis] }

如果访问方法的值是原始值,则 this 也将是原始值 - 但前提是函数在严格模式下。

js
function getThisStrict() {
  "use strict"; // Enter strict mode
  return this;
}

// Only for demonstration — you should not mutate built-in prototypes
Number.prototype.getThisStrict = getThisStrict;
console.log(typeof (1).getThisStrict()); // "number"

如果函数在没有访问任何内容的情况下被调用,则 this 将是 undefined - 但前提是函数在严格模式下。

js
console.log(typeof getThisStrict()); // "undefined"

在非严格模式下,一个称为this 替换 的特殊过程确保 this 的值始终是对象。这意味着

  • 如果函数使用 this 设置为 undefinednull 调用,this 将被替换为globalThis.
  • 如果函数使用 this 设置为原始值调用,this 将被替换为原始值的包装对象。
js
function getThis() {
  return this;
}

// Only for demonstration — you should not mutate built-in prototypes
Number.prototype.getThis = getThis;
console.log(typeof (1).getThis()); // "object"
console.log(getThis() === globalThis); // true

在典型的函数调用中,this 就像参数一样隐式地通过函数的前缀(点之前的部分)传递。您还可以使用 Function.prototype.call()Function.prototype.apply()Reflect.apply() 方法显式地设置 this 的值。使用 Function.prototype.bind(),您可以创建一个具有特定 this 值的新函数,该值无论函数如何调用都不会改变。使用这些方法时,如果函数是非严格的,则上述 this 替换规则仍然适用。

回调

当函数作为回调传递时,this 的值取决于回调的调用方式,这由 API 的实现者决定。回调通常使用 thisundefined(直接调用而不附加到任何对象)调用,这意味着如果函数是非严格的,则 this 的值是全局对象 (globalThis)。对于迭代数组方法Promise() 构造函数等,情况也是如此。

js
function logThis() {
  "use strict";
  console.log(this);
}

[1, 2, 3].forEach(logThis); // undefined, undefined, undefined

一些 API 允许您为回调的调用设置 this 值。例如,所有迭代数组方法和相关的 Set.prototype.forEach() 等方法都接受一个可选的 thisArg 参数。

js
[1, 2, 3].forEach(logThis, { name: "obj" });
// { name: 'obj' }, { name: 'obj' }, { name: 'obj' }

偶尔,回调会使用除 undefined 之外的 this 值调用。例如,JSON.parse()reviver 参数和 JSON.stringify()replacer 参数都使用 this 设置为正在解析/序列化的属性所属的对象调用。

箭头函数

箭头函数 中,this 保留封闭词法上下文的 this 值。换句话说,在评估箭头函数体时,语言不会创建新的 this 绑定。

例如,在全局代码中,this 始终是 globalThis,无论严格程度如何,这是因为全局上下文 绑定

js
const globalObject = this;
const foo = () => this;
console.log(foo() === globalObject); // true

箭头函数在其周围作用域的 this 值上创建闭包,这意味着箭头函数的行为就像它们是“自动绑定的”——无论它如何调用,this 都绑定到函数创建时的值(在上面的示例中,是全局对象)。同样适用于在其他函数内部创建的箭头函数:它们的 this 保持封闭词法上下文的 this请参见下面的示例.

此外,当使用 call()bind()apply() 调用箭头函数时,thisArg 参数会被忽略。不过,您仍然可以使用这些方法传递其他参数。

js
const obj = { name: "obj" };

// Attempt to set this using call
console.log(foo.call(obj) === globalObject); // true

// Attempt to set this using bind
const boundFoo = foo.bind(obj);
console.log(boundFoo() === globalObject); // true

构造函数

当函数用作构造函数(使用new关键字)时,无论访问构造函数的哪个对象,其this都绑定到正在构造的新对象。this的值将成为new表达式的值,除非构造函数返回另一个非原始值。

js
function C() {
  this.a = 37;
}

let o = new C();
console.log(o.a); // 37

function C2() {
  this.a = 37;
  return { a: 38 };
}

o = new C2();
console.log(o.a); // 38

在第二个示例(C2)中,因为在构造期间返回了一个对象,所以this绑定的新对象被丢弃了。(这实际上使语句this.a = 37;成为死代码。它并不完全是死代码,因为它会被执行,但它可以被消除而不会产生外部影响。)

super

当函数以super.method()的形式调用时,method函数内部的thissuper.method()调用周围的this值相同,通常不等于super引用的对象。这是因为super.method不是像上面那样的对象成员访问——它是一种具有不同绑定规则的特殊语法。有关示例,请参见super参考

类上下文

一个可以分为两个上下文:静态和实例。 构造函数、方法和实例字段初始化器(公共私有)属于实例上下文。 静态方法、静态字段初始化器和静态初始化块属于静态上下文。this的值在每个上下文中都不同。

类构造函数总是用new调用,因此它们的行为与函数构造函数相同:this的值是正在创建的新实例。类方法的行为类似于对象文字中的方法——this的值是访问该方法的对象。如果该方法没有被转移到另一个对象,this通常是该类的实例。

静态方法不是this的属性。它们是类本身的属性。因此,它们通常在类上访问,而this是该类(或子类)的值。静态初始化块也是用this设置为当前类来计算的。

字段初始化器也在类的上下文中计算。实例字段是用this设置为正在构造的实例来计算的。静态字段是用this设置为当前类来计算的。这就是为什么字段初始化器中的箭头函数绑定到实例字段的实例和静态字段的类

js
class C {
  instanceField = this;
  static staticField = this;
}

const c = new C();
console.log(c.instanceField === c); // true
console.log(C.staticField === C); // true

派生类构造函数

与基类构造函数不同,派生构造函数没有初始的this绑定。调用super()将在构造函数中创建一个this绑定,并且基本上具有计算以下代码行的效果,其中Base是基类

js
this = new Base();

警告:在调用super()之前引用this将抛出错误。

派生类必须在调用super()之前不要返回,除非构造函数返回一个对象(因此this值被覆盖)或者类根本没有构造函数。

js
class Base {}
class Good extends Base {}
class AlsoGood extends Base {
  constructor() {
    return { a: 5 };
  }
}
class Bad extends Base {
  constructor() {}
}

new Good();
new AlsoGood();
new Bad(); // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

全局上下文

在全局执行上下文(在任何函数或类之外;可能在全局范围内的箭头函数内部),this值取决于脚本运行的执行上下文。与回调函数一样,this值由运行时环境(调用者)确定。

在脚本的最顶层,this引用globalThis,无论是否处于严格模式。这通常与全局对象相同——例如,如果源代码被放在 HTML <script>元素内并作为脚本执行,则this === window

注意:globalThis通常与全局对象的概念相同(即向globalThis添加属性会使它们成为全局变量)——这在浏览器和 Node 中是这种情况——但主机可以为globalThis提供不同的值,该值与全局对象无关。

js
// In web browsers, the window object is also the global object:
console.log(this === window); // true

this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"

如果源代码作为模块加载(对于 HTML,这意味着向<script>标签添加type="module"),则this在最顶层始终为undefined

如果源代码用eval()执行,则this对于直接 eval与封闭上下文相同,或者对于间接 eval 与globalThis相同(就像在单独的全局脚本中运行一样)。

js
function test() {
  // Direct eval
  console.log(eval("this") === this);
  // Indirect eval, non-strict
  console.log(eval?.("this") === globalThis);
  // Indirect eval, strict
  console.log(eval?.("'use strict'; this") === globalThis);
}

test.call({ name: "obj" }); // Logs 3 "true"

请注意,一些源代码虽然看起来像全局范围,但实际上在执行时被包装在一个函数中。例如,Node.js CommonJS 模块被包装在一个函数中,并使用this值设置为module.exports来执行。 事件处理程序属性使用this设置为它们所附加的元素来执行。

对象文字不会创建this范围——只有在对象内定义的函数(方法)才会创建。在对象文字中使用this会从周围范围继承该值。

js
const obj = {
  a: this,
};

console.log(obj.a === window); // true

示例

函数上下文中的 this

this参数的值取决于函数的调用方式,而不是定义方式。

js
// An object can be passed as the first argument to 'call'
// or 'apply' and 'this' will be bound to it.
const obj = { a: "Custom" };

// Variables declared with var become properties of 'globalThis'.
var a = "Global";

function whatsThis() {
  return this.a; // 'this' depends on how the function is called
}

whatsThis(); // 'Global'; the 'this' parameter defaults to 'globalThis' in non–strict mode
obj.whatsThis = whatsThis;
obj.whatsThis(); // 'Custom'; the 'this' parameter is bound to obj

使用call()apply(),您可以传递this的值,就好像它是一个显式参数一样。

js
function add(c, d) {
  return this.a + this.b + c + d;
}

const o = { a: 1, b: 3 };

// The first argument is bound to the implicit 'this' parameter; the remaining
// arguments are bound to the named parameters.
add.call(o, 5, 7); // 16

// The first argument is bound to the implicit 'this' parameter; the second
// argument is an array whose members are bound to the named parameters.
add.apply(o, [10, 20]); // 34

this 和对象转换

在非严格模式下,如果函数使用不是对象的this值调用,则this值将被对象替换。nullundefined变成globalThis。诸如7'foo'之类的原始类型使用相关构造函数转换为对象,因此原始数字7被转换为Number包装类,字符串'foo'被转换为String包装类。

js
function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]
bar.call("foo"); // [object String]
bar.call(undefined); // [object Window]

bind() 方法

调用f.bind(someObject)将创建一个与f具有相同主体和范围的新函数,但无论函数如何被调用,this的值都将永久绑定到bind的第一个参数。

js
function f() {
  return this.a;
}

const g = f.bind({ a: "azerty" });
console.log(g()); // azerty

const h = g.bind({ a: "yoo" }); // bind only works once!
console.log(h()); // azerty

const o = { a: 37, f, g, h };
console.log(o.a, o.f(), o.g(), o.h()); // 37 37 azerty azerty

箭头函数中的 this

箭头函数在封闭执行上下文的this值上创建闭包。在以下示例中,我们使用一个名为getThisGetter的方法创建obj,该方法返回一个返回this值的函数。返回的函数被创建为箭头函数,因此它的this被永久绑定到其封闭函数的thisgetThisGetter内部的this值可以在调用中设置,这反过来设置了返回函数的返回值。我们将假设getThisGetter是一个非严格函数,这意味着它包含在非严格脚本中,并且没有进一步嵌套在类或严格函数中。

js
const obj = {
  getThisGetter() {
    const getter = () => this;
    return getter;
  },
};

我们可以将getThisGetter作为obj的方法调用,这将在其主体内部将this绑定到obj。返回的函数被分配给一个名为fn的变量。现在,当调用fn时,返回的this值仍然是getThisGetter调用设置的值,即obj。如果返回的函数不是箭头函数,此类调用会导致this值为globalThis,因为getThisGetter是非严格的。

js
const fn = obj.getThisGetter();
console.log(fn() === obj); // true

但如果在不调用方法的情况下解除obj的方法绑定,请注意,因为getThisGetter仍然是一个具有可变this值的方法。在以下示例中调用fn2()()将返回globalThis,因为它遵循fn2()this,即globalThis,因为它是在没有附加到任何对象的情况下调用的。

js
const fn2 = obj.getThisGetter;
console.log(fn2()() === globalThis); // true in non-strict mode

此行为在定义回调函数时非常有用。通常,每个函数表达式都会创建自己的this绑定,它会覆盖上层范围的this值。现在,如果我不关心this值,则可以将函数定义为箭头函数,并且只在需要的地方创建this绑定(例如,在类方法中)。请参阅使用setTimeout()的示例

使用 getter 或 setter 的 this

getter 和 setter 中的this基于访问属性的对象,而不是定义属性的对象。用作 getter 或 setter 的函数将它的this绑定到设置或获取属性的对象。

js
function sum() {
  return this.a + this.b + this.c;
}

const o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  },
};

Object.defineProperty(o, "sum", {
  get: sum,
  enumerable: true,
  configurable: true,
});

console.log(o.average, o.sum); // 2 6

DOM 事件处理程序中的 this

当函数用作事件处理程序时,它的this参数绑定到放置监听器的 DOM 元素(一些浏览器不遵循此约定,因为监听器是使用除addEventListener()之外的方法动态添加的)。

js
// When called as a listener, turns the related element blue
function bluify(e) {
  // Always true
  console.log(this === e.currentTarget);
  // true when currentTarget and target are the same object
  console.log(this === e.target);
  this.style.backgroundColor = "#A5D9F3";
}

// Get a list of every element in the document
const elements = document.getElementsByTagName("*");

// Add bluify as a click listener so when the
// element is clicked on, it turns blue
for (const element of elements) {
  element.addEventListener("click", bluify, false);
}

内联事件处理程序中的 this

当代码从内联事件处理程序属性调用时,它的this绑定到放置监听器的 DOM 元素

html
<button onclick="alert(this.tagName.toLowerCase());">Show this</button>

上面的警报显示button。但是,请注意,只有外部范围的this以这种方式绑定

html
<button onclick="alert((function () { return this; })());">
  Show inner this
</button>

在这种情况下,内部函数的this参数绑定到globalThis(即非严格模式下的默认对象,其中this未在调用中传递)。

类中的绑定方法

就像使用常规函数一样,方法内部的this值取决于它们被调用的方式。有时修改此行为很有用,以便类内部的this始终引用类实例。为了实现这一点,请在构造函数中绑定类方法

js
class Car {
  constructor() {
    // Bind sayBye but not sayHi to show the difference
    this.sayBye = this.sayBye.bind(this);
  }

  sayHi() {
    console.log(`Hello from ${this.name}`);
  }

  sayBye() {
    console.log(`Bye from ${this.name}`);
  }

  get name() {
    return "Ferrari";
  }
}

class Bird {
  get name() {
    return "Tweety";
  }
}

const car = new Car();
const bird = new Bird();

// The value of 'this' in methods depends on their caller
car.sayHi(); // Hello from Ferrari
bird.sayHi = car.sayHi;
bird.sayHi(); // Hello from Tweety

// For bound methods, 'this' doesn't depend on the caller
bird.sayBye = car.sayBye;
bird.sayBye(); // Bye from Ferrari

注意:类始终处于严格模式。如果方法尝试访问this上的属性,则使用未定义的this调用方法将抛出错误。

js
const carSayHi = car.sayHi;
carSayHi(); // TypeError because the 'sayHi' method tries to access 'this.name', but 'this' is undefined in strict mode.

但是,请注意,自动绑定方法与将箭头函数用于类属性存在相同的问题:类的每个实例将拥有它自己方法的副本,这会增加内存使用量。只在绝对必要时使用它。您也可以模仿Intl.NumberFormat.prototype.format()的实现:将属性定义为一个 getter,当访问时返回一个绑定函数并将其保存,以便函数只创建一次,并且只在必要时创建。

with 语句中的 this

尽管 with 语句已弃用,并且在严格模式下不可用,但它们仍然是正常 this 绑定规则的例外。如果一个函数是在 with 语句中调用的,并且该函数是作用域对象的属性,则 this 值将绑定到作用域对象,就好像 obj1. 前缀存在一样。

js
const obj1 = {
  foo() {
    return this;
  },
};

with (obj1) {
  console.log(foo() === obj1); // true
}

规范

规范
ECMAScript 语言规范
# sec-this-keyword

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅