Skip to main content

The this Keyword

this refers to the context where code is running. Its value depends on how a function is called (runtime binding), not where it's defined.

Rules (in priority order)

1. new Binding

When a function is called with new, this is the newly created object.

function User(name) {
this.name = name;
}
const user = new User("Ajay");
console.log(user.name); // "Ajay" — this = new object

If the constructor returns a non-primitive, that returned object replaces this:

function Car() {
this.brand = "Toyota";
return { brand: "Honda" }; // overrides `this`
}
console.log(new Car().brand); // "Honda"

2. Explicit Binding — call, apply, bind

function add(c, d) {
return this.a + this.b + c + d;
}

const obj = { a: 1, b: 3 };

add.call(obj, 5, 7); // 16
add.apply(obj, [10, 20]); // 34

const bound = add.bind(obj, 5);
bound(7); // 16
MethodArgumentsInvokes immediately?
call(thisArg, a, b, ...)Yes
apply(thisArg, [a, b, ...])Yes
bind(thisArg, a, b, ...)No — returns new fn

bind only works once — re-binding a bound function has no effect:

function f() {
return this.a;
}

const g = f.bind({ a: "first" });
const h = g.bind({ a: "second" }); // ignored
console.log(h()); // "first"

3. Implicit Binding — Object method

this = the object to the left of the dot.

const obj = {
name: "Ajay",
greet() {
console.log(this.name);
},
};

obj.greet(); // "Ajay"

The same function can get different this depending on which object calls it:

function getThis() {
return this;
}

const obj1 = { name: "obj1", getThis };
const obj2 = { name: "obj2", getThis };

console.log(obj1.getThis().name); // "obj1"
console.log(obj2.getThis().name); // "obj2"

Lost binding — extracting a method loses its object:

const fn = obj.greet;
fn(); // undefined — this = window/global (or undefined in strict mode)

4. Default Binding — Standalone function

function show() {
console.log(this);
}

show(); // window (browser) / global (Node) — or undefined in strict mode

Strict Mode vs Non-Strict

ModeStandalone thisPrimitive this in call/apply
Non-strictglobalThisAuto-wrapped to object (7Number)
StrictundefinedStays as-is (7 remains 7)
function nonStrict() {
return this;
}
console.log(nonStrict()); // window

function strict() {
"use strict";
return this;
}
console.log(strict()); // undefined

Primitive wrapping in non-strict:

function bar() {
console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]
bar.call("foo"); // [object String]
bar.call(undefined); // [object Window]

Arrow Functions & this

Arrow functions do not have their own this. They capture this from the enclosing lexical scope at definition time. call, apply, bind cannot override it.

const obj = {
name: "Ajay",
// arrow at object level — 'this' is outer (global) scope, NOT obj
greet: () => {
console.log(this.name); // undefined
},
delayedGreet() {
setTimeout(() => {
console.log(this.name); // "Ajay" — arrow inherits from delayedGreet
}, 100);
},
};
const globalObject = this;
const foo = () => this;

console.log(foo() === globalObject); // true
console.log(foo.call({ a: 1 }) === globalObject); // true — call ignored

Class Context

Instance & Static

class C {
instanceField = this; // the instance
static staticField = this; // the class itself
}

const c = new C();
console.log(c.instanceField === c); // true
console.log(C.staticField === C); // true

Derived Class — super() before this

In a subclass constructor, you must call super() before accessing this:

class Base {
constructor() {
this.role = "base";
}
}

class Child extends Base {
constructor() {
super(); // required before using 'this'
this.role = "child";
}
}

Bound Methods in Classes

Binding in constructor prevents this from varying when the method is passed around:

class Car {
constructor() {
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();

bird.sayHi = car.sayHi;
bird.sayHi(); // Hello from Tweety — implicit binding wins

bird.sayBye = car.sayBye;
bird.sayBye(); // Bye from Ferrari — bind locks it

Callbacks

Callbacks lose this because they're called as standalone functions:

function logThis() {
"use strict";
console.log(this);
}

[1, 2, 3].forEach(logThis); // undefined × 3

Some APIs accept a thisArg parameter:

[1, 2, 3].forEach(logThis, { name: "obj" });
// { name: 'obj' } × 3

Getters & Setters

this is the object the property is accessed on:

const stats = {
a: 1,
b: 2,
c: 3,
get average() {
return (this.a + this.b + this.c) / 3;
},
};

console.log(stats.average); // 2

DOM Event Handlers

this is bound to the element that the listener is attached to:

function bluify(e) {
console.log(this === e.currentTarget); // true
this.style.backgroundColor = "#A5D9F3";
}

document.getElementById("btn").addEventListener("click", bluify);

Inline handlers — this = the element:

<button onclick="alert(this.tagName);">Click me</button>
<!-- alerts "BUTTON" -->

Global Context

EnvironmentTop-level this
Browser scriptwindow
ES Moduleundefined
Node CommonJSmodule.exports
// browser script (not a module)
console.log(this === window); // true

this.x = 42;
console.log(window.x); // 42

Quick Reference

Contextthis value
Global (non-strict)window / globalThis
Global (strict)undefined
ES Module (top-level)undefined
Object methodThe calling object
call/apply/bindThe explicitly passed object
newThe new instance
Arrow functionInherited from enclosing scope
Class constructorThe new instance
Static method/fieldThe class itself
Event handler (DOM)The element that fired the event
Callback (standalone)undefined (strict) / globalThis
Getter/SetterThe object the property belongs to

References

Key Takeaways

  • this depends on how the function is called, not where it's written.
  • Priority: new > explicit (call/apply/bind) > implicit (object method) > default.
  • Arrow functions don't have their own this — they inherit it lexically and it cannot be overridden.
  • bind only works once — rebinding has no effect.
  • Strict mode prevents this from silently defaulting to globalThis.
  • In class constructors of derived classes, super() must be called before this.