The this keyword
this is a reference to the execution context of a function, that is, to the object in whose context the function was called. The value of this is assigned at call time and depends on how the function is called, not on where it is written.
Theory
TL;DR
thisis not fixed when a function is declared, it is assigned at runtime, at the moment of the call.- A call through the dot,
obj.method(), bindsthistoobj. - A plain call,
fn(), givesundefinedin strict mode andwindowin sloppy mode. newcreates a fresh object and makes it the value ofthis.call,applyandbindsetthisexplicitly.- Arrow functions have no
thisof their own, they inherit it from the surrounding scope.
Quick example
function showName() {
console.log(this.name);
}
const user = { name: 'Oleh', showName };
user.showName(); // 'Oleh', the object before the dot
showName.call({ name: 'Maria' }); // 'Maria', this is set explicitly
const detached = user.showName;
detached(); // TypeError in strict mode, the context is lostHow this is determined
Put simply:
thisis not determined when the function is declared;- it is assigned at the moment of the call (runtime);
- and it depends on the way the function is called, not on where it is written.
Global context. Outside any function:
console.log(this);- in a browser this is
window; - in Node.js inside a CommonJS module it is
{}, an empty object (module.exports); - in an ES module it is
undefined.
Inside a plain function.
function showThis() {
console.log(this);
}
showThis();If the function has no "owner", that is, it is not called as obj.method():
- in sloppy mode
thisiswindow; - in strict mode (
'use strict')thisisundefined.
Code inside classes and ES modules is always strict, so the second case applies there.
Object methods and losing the context
When a function is called through the dot, this points to the object before the dot:
const user = {
name: 'Oleh',
sayHi() {
console.log(`Hi, I am ${this.name}`);
}
};
user.sayHi(); // Hi, I am OlehBut the method itself does not "remember" its object. Store a reference to it in a variable and the link is gone:
const user = {
name: 'Oleh',
sayHi() {
console.log(this.name);
}
};
const fn = user.sayHi;
fn(); // undefined in sloppy mode, TypeError in strict modeThe context is lost the same way when a method is passed as a callback: setTimeout(user.sayHi, 1000), arr.map(user.sayHi), element.addEventListener('click', user.sayHi). The function travels on its own, with no object before the dot.
How to keep this
Option 1: bind. It returns a new function with the context attached for good.
const boundFn = user.sayHi.bind(user);
boundFn(); // Hi, I am Oleh
setTimeout(boundFn, 1000); // still worksOption 2: an arrow function. Arrows have no this of their own and take it from the outer scope:
const user = {
name: 'Oleh',
sayHiLater() {
setTimeout(() => {
console.log(this.name); // 'Oleh', this comes from sayHiLater
}, 1000);
}
};
user.sayHiLater();The flip side of the same property: an arrow is a poor object method, because it takes this from the declaration site rather than from the call:
const user = {
name: 'Oleh',
showThis: () => console.log(this)
};
user.showThis(); // window in a browser script, undefined in a moduleOption 3: explicit control with call, apply, bind.
| Method | What it does | When it runs |
|---|---|---|
call(thisArg, a, b) | calls the function with the given this, arguments listed one by one | immediately |
apply(thisArg, [a, b]) | the same, but arguments come as an array | immediately |
bind(thisArg) | returns a new function with this attached | later |
function greet() {
console.log(`Hi, ${this.name}`);
}
const user = { name: 'Oleh' };
greet.call(user); // Hi, Oleh
greet.apply(user); // Hi, Oleh
greet.bind(user)(); // Hi, Olehnew, classes and event handlers
Calling with new. If a function is called with new, this points to the newly created object:
function User(name) {
this.name = name;
}
const oleh = new User('Oleh');
console.log(oleh.name); // OlehWhat happens during a new call:
- an empty object
{}is created; - it is bound to
this, and its prototype is set toUser.prototype; - the function body runs;
- that object is returned, unless the function explicitly returns another object.
In classes this behaves exactly as in ordinary object methods:
class User {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`Hi, I am ${this.name}`);
}
}
const oleh = new User('Oleh');
oleh.sayHi(); // Hi, I am OlehIn event handlers in the browser, this points to the DOM element the listener is attached to:
button.addEventListener('click', function () {
console.log(this); // the button element
});Pass an arrow function instead and this is no longer the button, it is the outer context:
button.addEventListener('click', () => {
console.log(this); // not the button, but the outer context
});In that case use event.currentTarget, which is more reliable than this and behaves the same for both kinds of functions.
Summary table
| Context | What this holds |
|---|---|
| Global (in a browser) | window |
| In a plain function | undefined in strict mode, window in sloppy mode |
| In an object method | The object itself |
| In an arrow function | The this of the outer scope |
| In a constructor or class | The new instance |
| In an event handler | The DOM element the listener sits on |
Through call, apply, bind | The object you passed in |
Common mistakes
- Assuming
thisdepends on where the function is declared. For ordinary functions the call site decides. - Passing a method as a callback without binding:
setTimeout(user.sayHi, 1000). Useuser.sayHi.bind(user)or an arrow,() => user.sayHi(). - Using an arrow as an object method and then wondering why
this.nameisundefined. - Attaching an arrow with
addEventListenerand expectingthisto be the element. Use a regular function orevent.currentTarget. - Forgetting
newwhen calling a constructor function. In strict mode you get aTypeError, in sloppy mode the properties land on the global object. - Trying to rebind an already bound function. A second
bind, orcallapplied to the result ofbind, does not change the context.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.