Inside a function, the value of this
depends on how the function is called. Think about this
as a hidden parameter of a function — just like the parameters declared in the function definition, this
is a binding that the language creates for you when the function body is evaluated.
For a typical function, the value of this
is the object that the function is accessed on. In other words, if the function call is in the form obj.f()
, then this
refers to obj
. For example:
function getThis() {
return this;
}
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
obj1.getThis = getThis;
obj2.getThis = getThis;
console.log(obj1.getThis());
console.log(obj2.getThis());
Note how the function is the same, but based on how it's invoked, the value of this
is different. This is analogous to how function parameters work.
The value of this
is not the object that has the function as an own property, but the object that is used to call the function. You can prove this by calling a method of an object up in the prototype chain.
const obj3 = {
__proto__: obj1,
name: "obj3",
};
console.log(obj3.getThis());
The value of this
always changes based on how a function is called, even when the function was defined on an object at creation:
const obj4 = {
name: "obj4",
getThis() {
return this;
},
};
const obj5 = { name: "obj5" };
obj5.getThis = obj4.getThis;
console.log(obj5.getThis());
If the value that the method is accessed on is a primitive, this
will be a primitive value as well — but only if the function is in strict mode.
function getThisStrict() {
"use strict";
return this;
}
Number.prototype.getThisStrict = getThisStrict;
console.log(typeof (1).getThisStrict());
If the function is called without being accessed on anything, this
will be undefined
— but only if the function is in strict mode.
console.log(typeof getThisStrict());
In non-strict mode, a special process called this
substitution ensures that the value of this
is always an object. This means:
- If a function is called with
this
set to undefined
or null
, this
gets substituted with globalThis
. - If the function is called with
this
set to a primitive value, this
gets substituted with the primitive value's wrapper object.
function getThis() {
return this;
}
Number.prototype.getThis = getThis;
console.log(typeof (1).getThis());
console.log(getThis() === globalThis);
In typical function calls, this
is implicitly passed like a parameter through the function's prefix (the part before the dot). You can also explicitly set the value of this
using the Function.prototype.call()
, Function.prototype.apply()
, or Reflect.apply()
methods. Using Function.prototype.bind()
, you can create a new function with a specific value of this
that doesn't change regardless of how the function is called. When using these methods, the this
substitution rules above still apply if the function is non-strict.
Callbacks
When a function is passed as a callback, the value of this
depends on how the callback is called, which is determined by the implementor of the API. Callbacks are typically called with a this
value of undefined
(calling it directly without attaching it to any object), which means if the function is non–strict, the value of this
is the global object (globalThis
). This is the case for iterative array methods, the Promise()
constructor, etc.
function logThis() {
"use strict";
console.log(this);
}
[1, 2, 3].forEach(logThis);
Some APIs allow you to set a this
value for invocations of the callback. For example, all iterative array methods and related ones like Set.prototype.forEach()
accept an optional thisArg
parameter.
[1, 2, 3].forEach(logThis, { name: "obj" });
Occasionally, a callback is called with a this
value other than undefined
. For example, the reviver
parameter of JSON.parse()
and the replacer
parameter of JSON.stringify()
are both called with this
set to the object that the property being parsed/serialized belongs to.
Arrow functions
In arrow functions, this
retains the value of the enclosing lexical context's this
. In other words, when evaluating an arrow function's body, the language does not create a new this
binding.
For example, in global code, this
is always globalThis
regardless of strictness, because of the global context binding:
const globalObject = this;
const foo = () => this;
console.log(foo() === globalObject);
Arrow functions create a closure over the this
value of its surrounding scope, which means arrow functions behave as if they are "auto-bound" — no matter how it's invoked, this
is bound to what it was when the function was created (in the example above, the global object). The same applies to arrow functions created inside other functions: their this
remains that of the enclosing lexical context. See example below.
Furthermore, when invoking arrow functions using call()
, bind()
, or apply()
, the thisArg
parameter is ignored. You can still pass other arguments using these methods, though.
const obj = { name: "obj" };
console.log(foo.call(obj) === globalObject);
const boundFoo = foo.bind(obj);
console.log(boundFoo() === globalObject);
Constructors
When a function is used as a constructor (with the new
keyword), its this
is bound to the new object being constructed, no matter which object the constructor function is accessed on. The value of this
becomes the value of the new
expression unless the constructor returns another non–primitive value.
function C() {
this.a = 37;
}
let o = new C();
console.log(o.a);
function C2() {
this.a = 37;
return { a: 38 };
}
o = new C2();
console.log(o.a);
In the second example (C2
), because an object was returned during construction, the new object that this
was bound to gets discarded. (This essentially makes the statement this.a = 37;
dead code. It's not exactly dead because it gets executed, but it can be eliminated with no outside effects.)
super
When a function is invoked in the super.method()
form, the this
inside the method
function is the same value as the this
value around the super.method()
call, and is generally not equal to the object that super
refers to. This is because super.method
is not an object member access like the ones above — it's a special syntax with different binding rules. For examples, see the super
reference.