JavaScript this Keyword

The value of this depends entirely on how a function is called, not where it is defined. Learn all the binding rules and how arrow functions permanently changed the game.

Intermediate 11 min read 11 examples

What is this?

this is a special keyword that refers to the object that is currently executing the function. Unlike most languages where this is fixed to the class instance, JavaScript's this is determined at call time by four binding rules.

JavaScript
// Four rules that determine 'this':
// 1. Default binding  - standalone function call -> global/undefined
// 2. Implicit binding - method call on an object -> that object
// 3. Explicit binding - call/apply/bind -> the specified object
// 4. new binding      - constructor call -> new instance

// Arrow functions: inherit this from enclosing lexical scope (not one of the 4 rules)

// Simple demonstration
function whoAmI() {
    return this;
}

const obj = { whoAmI };

console.log(whoAmI());        // global object (or undefined in strict mode)
console.log(obj.whoAmI());    // { whoAmI: [Function] } - the obj

Global Context

In the global scope (outside any function), this refers to the global object (window in browsers, global in Node.js). In strict mode standalone functions, this is undefined.

JavaScript
// In browser global scope
console.log(this === window); // true (browser)

// Standalone function - default binding
function standalone() {
    console.log(this); // window (non-strict) or undefined (strict mode)
}
standalone();

// Strict mode
"use strict";
function strictFn() {
    console.log(this); // undefined (not the global object)
}
strictFn();

// globalThis - unified reference across environments
console.log(globalThis); // window in browser, global in Node.js

Method Context (Implicit Binding)

When a function is called as a method of an object (obj.method()), this refers to the object to the left of the dot.

JavaScript
const user = {
    name: "Alice",
    greet() {
        return `Hello, I am ${this.name}`; // this = user
    },
    getName() {
        return this.name; // this = user
    }
};

console.log(user.greet());    // "Hello, I am Alice"
console.log(user.getName());  // "Alice"

// Nested objects
const company = {
    name: "Ruban Tech",
    ceo: {
        name: "Bob",
        greet() {
            return `${this.name} from ${this.company}`; // this = ceo, not company!
        }
    }
};
console.log(company.ceo.greet()); // "Bob from undefined" (this = ceo object)

// this is lost when method is extracted
const greet = user.greet;    // extract the method
console.log(greet());        // "Hello, I am undefined" - this is not user anymore!

Class Context (new Binding)

When a constructor function or class is called with new, this refers to the newly created instance.

JavaScript
class Counter {
    constructor(start = 0) {
        this.count = start;   // this = new instance
    }

    increment() {
        this.count++;         // this = the instance calling this method
        return this;          // return this for chaining
    }

    decrement() {
        this.count--;
        return this;
    }

    value() {
        return this.count;
    }
}

const c = new Counter(10);
console.log(c.increment().increment().decrement().value()); // 11
// c.count = 11 (10 +1 +1 -1)

// Old-style constructor function
function Person(name) {
    this.name = name;   // this = new Person instance
    this.greet = function() {
        return `Hi, I am ${this.name}`;
    };
}
const alice = new Person("Alice");
console.log(alice.greet()); // "Hi, I am Alice"

Arrow Functions and this

Arrow functions do NOT have their own this. They inherit this from the enclosing lexical scope. This makes them ideal for callbacks inside class methods.

JavaScript
class Timer {
    constructor() {
        this.seconds = 0;
    }

    start() {
        // PROBLEM with regular function - 'this' is undefined in callback
        // setInterval(function() {
        //     this.seconds++; // TypeError! this is not Timer instance
        // }, 1000);

        // SOLUTION: arrow function inherits 'this' from start() method
        setInterval(() => {
            this.seconds++; // this = Timer instance (correct!)
            console.log(this.seconds);
        }, 1000);
    }
}

// Arrow function as class field - permanently bound to instance
class Button {
    constructor(label) {
        this.label = label;
    }

    // Arrow function as a property - no binding issues
    handleClick = () => {
        console.log(`${this.label} clicked`); // this always = instance
    }
}

const btn = new Button("Submit");
document.addEventListener("click", btn.handleClick); // safe - no bind needed

// Cannot change arrow function's this with call/apply/bind
const arrowFn = () => console.log(this);
arrowFn.call({ name: "Alice" }); // still logs outer 'this', not the passed object
Arrow Functions in React

In React class components, event handlers defined as arrow function class fields (handleSubmit = () => { this.setState(...) }) avoid the binding issue without needing .bind(this) in the constructor. In React function components with hooks, this is never needed at all.

call and apply

call and apply invoke a function immediately with an explicitly specified this. The only difference is how arguments are passed.

JavaScript
function introduce(greeting, punctuation) {
    return `${greeting}, I am ${this.name}${punctuation}`;
}

const alice = { name: "Alice" };
const bob   = { name: "Bob" };

// call: arguments listed individually
console.log(introduce.call(alice, "Hello", "!"));  // "Hello, I am Alice!"
console.log(introduce.call(bob,   "Hi",    ".")); // "Hi, I am Bob."

// apply: arguments as an array
console.log(introduce.apply(alice, ["Hello", "!"]));
console.log(introduce.apply(bob,   ["Hi", "."]));

// Practical use: borrow methods from one object for another
const arr = [3, 1, 4, 1, 5, 9, 2, 6];
const max = Math.max.apply(null, arr); // spread array as args
console.log(max); // 9
// Modern equivalent: Math.max(...arr)

bind

bind returns a new function with this permanently set. It does NOT call the function - it creates a bound copy.

JavaScript
function greet(greeting) {
    return `${greeting}, ${this.name}!`;
}

const user = { name: "Alice" };

// bind returns a NEW function with 'this' fixed
const greetAlice = greet.bind(user);
console.log(greetAlice("Hello")); // "Hello, Alice!"
console.log(greetAlice("Hi"));    // "Hi, Alice!"

// Partial application: pre-fill some arguments
function multiply(a, b) { return a * b; }
const double = multiply.bind(null, 2); // a = 2, null = no this needed
const triple = multiply.bind(null, 3);
console.log(double(5)); // 10
console.log(triple(5)); // 15

// Binding in class constructor (older pattern)
class Counter {
    constructor() {
        this.count = 0;
        // Bind once in constructor - works for event listeners
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        this.count++;
    }
}

Common this Bugs and Fixes

JavaScript
// Bug 1: Method detached from object
const obj = {
    name: "Alice",
    greet() { return this.name; }
};
const greet = obj.greet;
console.log(greet()); // undefined - 'this' is not obj

// Fix: bind, arrow wrapper, or call with context
const greetBound = obj.greet.bind(obj);
console.log(greetBound()); // "Alice"

// Bug 2: Callback loses this
class Logger {
    constructor() { this.prefix = "[LOG]"; }
    log(msg) { console.log(`${this.prefix} ${msg}`); }
    logAll(items) {
        // BROKEN: items.forEach(this.log) - this is undefined
        items.forEach(item => this.log(item)); // FIXED: arrow
    }
}

// Bug 3: setTimeout with regular function
class Poller {
    constructor() { this.data = []; }
    start() {
        // BROKEN: setTimeout(function() { this.data.push(...) }, 1000)
        setTimeout(() => {        // FIXED: arrow
            this.data.push("new data");
        }, 1000);
    }
}

Frequently Asked Questions

When you pass a method as a callback, it loses its object binding. The function is called without an explicit receiver, so this defaults to undefined in strict mode (or window in non-strict). Fix: use an arrow function wrapper (onClick={() => this.handleClick()}), bind in the constructor (this.handleClick = this.handleClick.bind(this)), or define the method as an arrow function property (handleClick = () => {...}).

All three set this explicitly. call(thisArg, arg1, arg2) invokes the function immediately with arguments listed individually. apply(thisArg, [arg1, arg2]) invokes immediately with arguments as an array. bind(thisArg, arg1) returns a new function with this permanently bound - it does NOT invoke immediately. Use bind to create a reusable bound function; use call/apply for one-off invocations.

No. Arrow functions do not have their own this binding. They inherit this from the enclosing lexical scope at the time they are defined - not where they are called. This makes arrow functions ideal for callbacks inside class methods, where you want this to remain the class instance. The value of this in an arrow function cannot be changed with call, apply, or bind.

Inside a class constructor, this refers to the newly created instance. When you call new MyClass(), JavaScript creates a new empty object, sets this to that object inside the constructor, runs the constructor body, and returns the object. Properties set on this inside the constructor become instance properties on the new object.