Academic Block

The JavaScript this Keyword Get Certified

this is one of the most confusing parts of JavaScript, because its value is not fixed — it depends entirely on how a function is called, not where it is defined.

In this lesson, you will learn how this behaves in regular functions, object methods, classes, and arrow functions, how it is easy to accidentally lose, and how to fix it with bind(), call(), apply(), or arrow functions.

this in a Plain Function Call

When a regular function is called on its own, without being attached to an object, this refers to the global object (in non-strict mode) rather than to anything specific.

What this code does: Shows the default value of this when a regular function is called without an owner object (global / undefined in strict mode).

function showThis() {
    console.log(typeof this);
    console.log(this === self);
}
showThis();

this Inside an Object Method

When a function is called as a method — that is, object.method() — this refers to the object the method was called on.

What this code does: Demonstrates that inside an object method this refers to the object the method was called on.

const person = {
    name: "Michael",
    greet() {
        return "Hello, " + this.name;
    }
};
console.log(person.greet());

Losing this by Extracting a Method

The value of this depends on the call, not on where the function was originally written. If you copy a method out of an object and call it separately, it no longer knows which object it “belongs” to.

What this code does: Extracts a method from an object and calls it as a free function, causing this to be lost.

const person = {
    name: "Michael",
    greet() {
        return "Hello, " + this.name;
    }
};
const greetFn = person.greet;
console.log(greetFn());

Called this way, this no longer refers to person, so this.name is undefined instead of "Michael".

Fixing this with bind()

bind() returns a new function permanently bound to a specific this value, no matter how that new function is later called.

What this code does: Fixes the lost this by using .bind() so the extracted function permanently keeps the original object.

const person = {
    name: "Michael",
    greet() {
        return "Hello, " + this.name;
    }
};
const boundGreet = person.greet.bind(person);
console.log(boundGreet());

Setting this with call() and apply()

call() and apply() invoke a function immediately with a specific this value. call() takes arguments individually; apply() takes them as an array.

What this code does: Uses call() and apply() to invoke a function with an explicit this value.

function introduce(greeting) {
    return greeting + ", " + this.name;
}
const person = { name: "Michael" };
console.log(introduce.call(person, "Hi"));
console.log(introduce.apply(person, ["Hello"]));

Arrow Functions and Lexical this

Arrow functions do not have their own this. Instead, they use the this value from the surrounding (enclosing) scope where they were defined.

What this code does: Shows that an arrow function inside a method inherits this from the surrounding method, not from how it is later called.

const person = {
    name: "Michael",
    greet: function () {
        const arrow = () => {
            return "Hello, " + this.name;
        };
        return arrow();
    }
};
console.log(person.greet());

Because arrow is defined inside greet, it inherits this from greet, which is person.

Pitfall: Arrow Functions as Object Methods

Because arrow functions do not get their own this, defining an object’s method directly as an arrow function usually does not work the way you expect.

What this code does: Uses an arrow function as an object method, which fails because arrows do not get their own this from the object.

const person = {
    name: "Michael",
    greet: () => {
        return "Hello, " + this.name;
    }
};
console.log(person.greet());

Here, this refers to whatever this was outside the object literal, not to person, so this.name is undefined.

this Inside a Class

Inside a class method, this refers to the instance the method was called on, just like a regular object method.

What this code does: Inside a class method, this correctly refers to the instance the method was called on.

class Person {
    constructor(name) {
        this.name = name;
    }
    greet() {
        return "Hello, " + this.name;
    }
}
const michael = new Person("Michael");
console.log(michael.greet());

Fixing It with an Arrow Function

Using an arrow function for the callback keeps this pointing at the instance, because arrow functions inherit this from where they are defined.

What this code does: Uses an arrow function inside setTimeout so this still refers to the class instance.

class Timer {
    constructor(label) {
        this.label = label;
    }

    start() {
        console.log("Timer started →", this.label);

        setTimeout(() => {
            console.log("After delay → Label is still:", this.label);
        }, 30);
    }
}

const t = new Timer("Countdown");
t.start();

console.log("start() called (watch for delayed message)");

this Binding Rules

How the Function Is Called Value of this
Plain function call: fn() Global object (non-strict) or undefined (strict)
Method call: obj.method() The object before the dot (obj)
Constructor call: new Fn() The newly created instance
fn.call(obj) / fn.apply(obj) Whatever object is passed in
fn.bind(obj) Permanently bound to obj
Arrow function Inherited from the enclosing scope

Common Mistakes with this

  • Extracting a method from an object and calling it separately, losing its connection to that object.
  • Passing a regular function as a callback (like to setTimeout) and expecting this to still point at the original object.
  • Defining an object’s method as an arrow function, which prevents it from having its own this.
  • Forgetting that this depends on how a function is called, not on where it was defined (except for arrow functions).
  • Not using bind(), call(), or apply() when a method truly needs to be passed around separately from its object.

Best Practices for this

  • Use regular functions (or class methods) for object methods, since they need their own dynamic this.
  • Use arrow functions for callbacks defined inside a method when you want them to keep the surrounding this.
  • Use bind() when a method must be passed as a callback elsewhere and still needs its original this.
  • Never define an object’s own methods as arrow functions.
  • When in doubt, log this (or check this === expectedObject) to confirm what it actually refers to.

Quick Summary

  • The value of this depends on how a function is called, not where it is written.
  • In a method call (obj.method()), this is the object before the dot.
  • Extracting a method and calling it separately loses that connection.
  • call() and apply() invoke a function with a specific this immediately; bind() creates a new function permanently bound to it.
  • Arrow functions do not have their own this — they inherit it from the surrounding scope.
  • Inside a class, this refers to the instance a method was called on.
  • Regular function callbacks (like in setTimeout) commonly lose this; arrow function callbacks usually do not.

Try It Yourself

Modify the code below. Try removing .bind(counter) to see this break again, or changing the arrow function back into a regular function inside increment().

What this code does: A practice example that keeps this correct inside a timer by combining an arrow function with .bind().

class Counter {
    constructor() {
        this.count = 0;
    }

    increment() {
        console.log("increment() called");

        setTimeout(() => {
            this.count++;
            console.log("Count after delay:", this.count);
        }, 30);
    }
}

const counter = new Counter();
const incrementFn = counter.increment.bind(counter);

incrementFn();
incrementFn();

console.log("Both increment calls have been scheduled");
Tip: Ask “how is this function being called?” rather than “where was this function written?” — except for arrow functions, where the opposite is true: they always use the this from where they were defined.

Understanding this explains behavior you have already seen in object methods, classes, and inheritance, and is essential before moving on to closures and more advanced function behavior in JavaScript.

🧪 Test Your JavaScript Code

Edit the code on the left and click “Run Code” to see the console output on the right.

📝 JavaScript Code
👁 Console Output

Click “Run Code” to see the output here.

Ctrl + Enter to run code Esc to close