Explicitly setting this in JavaScript
You can force this with the methods call, apply and bind, and also through calling the function as a method of an object, through arrow functions, Reflect.apply and the new operator. The difference between them is when the call happens and whether the context can be changed afterwards.
Theory
TL;DR
call(thisArg, a, b)invokes the function immediately, arguments comma separated.apply(thisArg, [a, b])does the same, but the arguments come as an array.bind(thisArg)does not invoke anything, it returns a new function with a boundthis.obj.method()setsthis = objimplicitly.- Arrow functions have no
thisof their own and take it from the enclosing scope. Reflect.apply(fn, thisArg, args)is the modern counterpart ofapply.new Func()creates a new object and overrides even abindbinding.
Quick example
function greet() {
console.log(`Hello, I am ${this.name}`);
}
const user = { name: 'Maria' };
greet.call(user); // Hello, I am MariaHere this inside greet is forced to point at user.
call and apply: immediate invocation with a given context
Function.prototype.call() invokes the function immediately and lets you set this by hand:
function greet() {
console.log(`Hello, I am ${this.name}`);
}
const user = { name: 'Maria' };
greet.call(user); // Hello, I am Mariaapply() does the same thing as call, but takes the arguments as an array:
function introduce(city, job) {
console.log(`I am ${this.name} from ${city}, working as a ${job}`);
}
const user = { name: 'Maria' };
introduce.apply(user, ['Kyiv', 'Frontend developer']);
// I am Maria from Kyiv, working as a Frontend developerbind: a new function with a bound context
bind() does not invoke the function right away; it creates a new one whose this is already bound forever:
function sayHi() {
console.log(`Hello, I am ${this.name}`);
}
const user = { name: 'Maria' };
const sayHiBound = sayHi.bind(user);
sayHiBound(); // Hello, I am MariaThe difference:
call/applyinvoke immediately;bindcreates a copy with a fixed context.
Implicit context: object methods and arrow functions
If you call a function through the dot on an object, this automatically points at that object:
const user = {
name: 'Maria',
sayHi() {
console.log(`Hello, I am ${this.name}`);
}
};
user.sayHi(); // Hello, I am MariaThis is an implicit assignment of this, but in essence the same thing.
Arrow functions have no this of their own: they inherit it from the enclosing scope:
const user = {
name: 'Maria',
sayHiLater() {
setTimeout(() => {
console.log(`Hello, I am ${this.name}`); // this = user
}, 1000);
}
};
user.sayHiLater(); // Hello, I am MariaHad a regular function been used inside setTimeout, this would have been lost and become window.
Reflect.apply, new and deliberately dropping the context
Reflect.apply() is the modern ES6 counterpart of apply: it does the same, but more safely and declaratively, because it does not depend on whether apply has been overridden on the function itself:
function sayHello() {
console.log(`Hello, ${this.name}`);
}
const user = { name: 'Maria' };
Reflect.apply(sayHello, user, []); // Hello, MariaWhen a function is called with new, a new object is created and this inside the function points at it, even if a different this was bound through bind:
function User(name) {
this.name = name;
}
const Bound = User.bind({ name: 'Maria' });
const u = new Bound('Alice');
console.log(u.name); // Alice, the context from new overrides bindIf instead you want to "unbind" this and call the function without a context, you pass null or undefined:
function showThis() {
console.log(this);
}
showThis.call(null); // under 'use strict' -> undefinedIn sloppy mode null is substituted with the global object (window or global).
Summary of the options
| Way | Invokes immediately | How it sets this | Arguments |
|---|---|---|---|
call(thisArg, a, b) | Yes | Forced | Comma separated |
apply(thisArg, [a, b]) | Yes | Forced | As an array |
bind(thisArg, a, b) | No | Binds forever | Comma separated |
obj.method() | Yes | Implicitly (this = obj) | Comma separated |
Reflect.apply() | Yes | Forced (modern counterpart) | As an array |
new Func() | Yes | A new object inside the function | Constructor arguments |
Common mistakes
- Binding
thisto an arrow function. Arrows have nothisof their own, socall,applyandbindhave no effect on it and the first argument is simply ignored. - Looking for
Reflect.call. No such method exists:Reflectonly hasapply, so the arguments always go in as an array. - Assuming
bindinvokes the function. It only returns a new function, and without parentheses nothing happens. - Counting on
windowin strict mode. In modules and classesthisstaysundefinedwhen givennull, sothis.namethrows. - Losing the context when passing a method around.
setTimeout(user.sayHi, 1000)passes only the function without the object; you needuser.sayHi.bind(user)or an arrow wrapper.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.