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() 方法设置,在对象方法中它也不会指向当前对象。
试一试
const test = {
prop: 42,
func() {
return this.prop;
},
};
console.log(test.func());
// Expected output: 42
语法
this
值
在非严格模式下,this 总是对一个对象的引用。在严格模式下,它可以是任何值。有关如何确定该值的更多信息,请参阅下面的描述。
描述
this 的值取决于它出现在哪个上下文:函数、类或全局。
函数上下文
在函数内部,this 的值取决于函数的调用方式。可以将 this 视为函数的隐藏参数——就像函数定义中声明的参数一样,当评估函数体时,this 是语言为你创建的一个绑定。
对于普通函数(非箭头函数、绑定函数等),this 的值是访问该函数的对象。换句话说,如果函数调用形式为 obj.f(),那么 this 指向 obj。例如:
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 的值不是拥有该函数作为自有属性的对象,而是用于调用该函数的对象。你可以通过调用原型链中对象的某个方法来证明这一点。
const obj3 = {
__proto__: obj1,
name: "obj3",
};
console.log(obj3.getThis()); // { name: 'obj3' }
this 的值总是根据函数的调用方式而变化,即使函数是在创建时定义在对象上的。
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 也会是一个原始值——但仅限于函数处于严格模式下。
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——但仅限于函数处于严格模式下。
console.log(typeof getThisStrict()); // "undefined"
在非严格模式下,一个名为 this 替换的特殊过程确保 this 的值始终是一个对象。这意味着:
- 如果函数调用时
this设置为undefined或null,则this会被替换为globalThis。 - 如果函数调用时
this设置为原始值,则this会被替换为该原始值的包装对象。
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 的实现者决定。回调函数通常以 this 值为 undefined 调用(直接调用,不附加到任何对象),这意味着如果函数是非严格的,则 this 的值为全局对象(globalThis)。迭代数组方法、Promise() 构造函数等都属于这种情况。
function logThis() {
"use strict";
console.log(this);
}
[1, 2, 3].forEach(logThis); // undefined, undefined, undefined
一些 API 允许你为回调函数的调用设置 this 值。例如,所有迭代数组方法和相关方法,如 Set.prototype.forEach(),都接受一个可选的 thisArg 参数。
[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,无论是否严格,因为存在全局上下文绑定
const globalObject = this;
const foo = () => this;
console.log(foo() === globalObject); // true
箭头函数在其周围作用域的 this 值上创建了一个闭包,这意味着箭头函数的行为就像它们是“自动绑定”的一样——无论如何调用,this 都绑定到函数创建时的值(在上面的示例中是全局对象)。这同样适用于在其他函数内部创建的箭头函数:它们的 this 仍然是其封闭词法上下文的 this。请参阅下面的示例。
此外,当使用 call()、bind() 或 apply() 调用箭头函数时,thisArg 参数会被忽略。不过,你仍然可以使用这些方法传递其他参数。
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 表达式的值,除非构造函数返回另一个非原始值。
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 函数内部的 this 值与 super.method() 调用周围的 this 值相同,并且通常不等于 super 所引用的对象。这是因为 super.method 不像上面那样是对象成员访问——它是一种具有不同绑定规则的特殊语法。有关示例,请参阅 super 参考。
类上下文
类可以分为两种上下文:静态和实例。构造函数、方法和实例字段初始化器(公共或私有)属于实例上下文。静态方法、静态字段初始化器和静态初始化块属于静态上下文。this 值在每个上下文中都不同。
类构造函数总是通过 new 调用,因此它们的行为与函数构造函数相同:this 值是正在创建的新实例。类方法的行为类似于对象字面量中的方法——this 值是访问该方法的对象。如果该方法未转移到另一个对象,则 this 通常是该类的一个实例。
静态方法不是 this 的属性。它们是类本身的属性。因此,它们通常在类上访问,并且 this 是类(或子类)的值。静态初始化块也以 this 设置为当前类进行评估。
字段初始化器也在类的上下文中进行评估。实例字段以 this 设置为正在构造的实例进行评估。静态字段以 this 设置为当前类进行评估。这就是为什么字段初始化器中的箭头函数对于实例字段绑定到实例,对于静态字段绑定到类的原因。
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 是基类:
this = new Base();
警告:在调用 super() 之前引用 this 将抛出错误。
派生类在调用 super() 之前不得返回,除非构造函数返回一个对象(因此 this 值被覆盖),或者类根本没有构造函数。
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 提供一个与全局对象无关的不同值。
// 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() 执行,则对于直接 eval,this 与封闭上下文相同;对于间接 eval,则为 globalThis(就像它在单独的全局脚本中运行一样)。
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 会继承来自周围作用域的值。
const obj = {
a: this,
};
console.log(obj.a === window); // true
示例
函数上下文中的 this
this 参数的值取决于函数的调用方式,而不是其定义方式。
// 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 的值。
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 值会替换为对象。null 和 undefined 会变成 globalThis。像 7 或 'foo' 这样的原始值会使用相关的构造函数转换为对象,因此原始数字 7 会转换为 Number 包装类,字符串 'foo' 会转换为 String 包装类。
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 的第一个参数,无论函数如何被调用。
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 值的闭包。在下面的示例中,我们创建了一个 obj,带有一个方法 getThisGetter,该方法返回一个函数,该函数返回 this 的值。返回的函数作为箭头函数创建,因此其 this 会永久绑定到其封闭函数的 this。getThisGetter 内部的 this 值可以在调用中设置,这反过来又设置了返回函数的返回值。我们假设 getThisGetter 是一个非严格函数,这意味着它包含在一个非严格脚本中,并且没有进一步嵌套在类或严格函数中。
const obj = {
getThisGetter() {
const getter = () => this;
return getter;
},
};
我们可以将 getThisGetter 作为 obj 的方法调用,这会将其内部的 this 绑定到 obj。返回的函数被赋值给变量 fn。现在,当调用 fn 时,返回的 this 值仍然是由对 getThisGetter 的调用设置的值,即 obj。如果返回的函数不是箭头函数,则此类调用会导致 this 值为 globalThis,因为 getThisGetter 是非严格的。
const fn = obj.getThisGetter();
console.log(fn() === obj); // true
但如果你在不调用 obj 的方法的情况下解绑它,请务必小心,因为 getThisGetter 仍然是一个具有可变 this 值的方法。在以下示例中调用 fn2()() 返回 globalThis,因为它遵循 fn2() 中的 this,该 this 是 globalThis,因为它在未附加到任何对象的情况下被调用。
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 绑定到正在设置或获取属性的对象。
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() 以外的方法动态添加的监听器不遵循此约定)。
// 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);
}
内联事件处理程序中的 this
当代码从内联事件处理程序属性调用时,其 this 绑定到放置监听器的 DOM 元素上:
<button onclick="alert(this.tagName.toLowerCase());">Show this</button>
上述 alert 显示 button。然而,请注意,只有外部作用域的 this 以这种方式绑定:
<button onclick="alert((function () { return this; })());">
Show inner this
</button>
在这种情况下,内部函数的 this 参数绑定到 globalThis(即,在非严格模式下,如果调用中未传入 this,则为默认对象)。
类中的绑定方法
与普通函数一样,方法中 this 的值取决于它们的调用方式。有时,覆盖此行为以使类中 this 始终引用类实例会很有用。为此,请在构造函数中绑定类方法:
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 调用方法将抛出错误。
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 值绑定到作用域对象,就像存在 obj. 前缀一样。
const obj = {
foo() {
return this;
},
};
with (obj) {
console.log(foo() === obj); // true
}
规范
| 规范 |
|---|
| ECMAScript® 2026 语言规范 # sec-this-keyword |
浏览器兼容性
加载中…