Skip to main content

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

  • this is 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(), binds this to obj.
  • A plain call, fn(), gives undefined in strict mode and window in sloppy mode.
  • new creates a fresh object and makes it the value of this.
  • call, apply and bind set this explicitly.
  • Arrow functions have no this of their own, they inherit it from the surrounding scope.

Quick example

javascript
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 lost

How this is determined

Put simply:

  • this is 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:

javascript
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.

javascript
function showThis() { console.log(this); } showThis();

If the function has no "owner", that is, it is not called as obj.method():

  • in sloppy mode this is window;
  • in strict mode ('use strict') this is undefined.

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:

javascript
const user = { name: 'Oleh', sayHi() { console.log(`Hi, I am ${this.name}`); } }; user.sayHi(); // Hi, I am Oleh

But the method itself does not "remember" its object. Store a reference to it in a variable and the link is gone:

javascript
const user = { name: 'Oleh', sayHi() { console.log(this.name); } }; const fn = user.sayHi; fn(); // undefined in sloppy mode, TypeError in strict mode

The 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.

javascript
const boundFn = user.sayHi.bind(user); boundFn(); // Hi, I am Oleh setTimeout(boundFn, 1000); // still works

Option 2: an arrow function. Arrows have no this of their own and take it from the outer scope:

javascript
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:

javascript
const user = { name: 'Oleh', showThis: () => console.log(this) }; user.showThis(); // window in a browser script, undefined in a module

Option 3: explicit control with call, apply, bind.

MethodWhat it doesWhen it runs
call(thisArg, a, b)calls the function with the given this, arguments listed one by oneimmediately
apply(thisArg, [a, b])the same, but arguments come as an arrayimmediately
bind(thisArg)returns a new function with this attachedlater
javascript
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, Oleh

new, classes and event handlers

Calling with new. If a function is called with new, this points to the newly created object:

javascript
function User(name) { this.name = name; } const oleh = new User('Oleh'); console.log(oleh.name); // Oleh

What happens during a new call:

  1. an empty object {} is created;
  2. it is bound to this, and its prototype is set to User.prototype;
  3. the function body runs;
  4. that object is returned, unless the function explicitly returns another object.

In classes this behaves exactly as in ordinary object methods:

javascript
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 Oleh

In event handlers in the browser, this points to the DOM element the listener is attached to:

javascript
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:

javascript
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

ContextWhat this holds
Global (in a browser)window
In a plain functionundefined in strict mode, window in sloppy mode
In an object methodThe object itself
In an arrow functionThe this of the outer scope
In a constructor or classThe new instance
In an event handlerThe DOM element the listener sits on
Through call, apply, bindThe object you passed in

Common mistakes

  • Assuming this depends 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). Use user.sayHi.bind(user) or an arrow, () => user.sayHi().
  • Using an arrow as an object method and then wondering why this.name is undefined.
  • Attaching an arrow with addEventListener and expecting this to be the element. Use a regular function or event.currentTarget.
  • Forgetting new when calling a constructor function. In strict mode you get a TypeError, in sloppy mode the properties land on the global object.
  • Trying to rebind an already bound function. A second bind, or call applied to the result of bind, does not change the context.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.