⚑IntermediateObject-Oriented JavaScript

The this Keyword

How JavaScript determines what this refers to based on how a function is called

Overview

The `this` keyword in JavaScript refers to the object that is currently executing a function. Unlike most object-oriented languages where `this` always points to the instance that owns the method, JavaScript determines `this` at call time β€” meaning the same function can have a different `this` depending on how it is invoked. This runtime binding makes `this` one of the most misunderstood features in JavaScript, but once you understand the four rules that govern it, the behavior becomes completely predictable. The first rule is default binding: when a function is called standalone with no object before the dot, `this` is the global object (`window` in browsers, `global` in Node.js) in sloppy mode, or `undefined` in strict mode. The second rule is implicit binding: when a function is called as a method on an object (`obj.greet()`), `this` is the object immediately to the left of the dot at call time. The third rule is explicit binding: `call()`, `apply()`, and `bind()` let you supply the `this` value directly regardless of how the function is called. The fourth rule is new binding: when a constructor is invoked with `new`, a brand new object is created and assigned to `this` inside the constructor. Arrow functions are an exception to all four rules. They do not have their own `this` at all β€” instead they capture `this` from the lexical scope where they are defined, the same way a regular variable would be captured by a closure. This makes arrow functions the natural solution to the classic "losing `this`" bug that occurs when passing object methods as callbacks. Understanding when to choose a regular function versus an arrow function is largely about understanding what you want `this` to be.

Key Points

  • β€’`this` is determined at call time, not at define time β€” the same function can have a different `this` on each call
  • β€’Implicit binding: `obj.method()` sets `this` to `obj`; the object before the dot wins
  • β€’Default binding: a standalone function call sets `this` to the global object (sloppy) or `undefined` (strict mode)
  • β€’Arrow functions have no own `this` β€” they inherit `this` from the enclosing lexical scope
  • β€’`call(thisArg, arg1, arg2)` and `apply(thisArg, [args])` invoke a function with an explicit `this`
  • β€’`bind(thisArg)` returns a new permanently-bound function without calling it immediately
  • β€’`new SomeFunction()` creates a fresh object, sets `this` to it inside the constructor, and returns it

Examples

Implicit binding β€” this is the object before the dot

const user = {
  name: 'Alice',
  greet() {
    console.log('Hello, I am ' + this.name);
  },
};

user.greet(); // 'Hello, I am Alice'
// this === user because greet() was called on user

Losing this β€” assigning a method to a variable strips the object context

const user = {
  name: 'Alice',
  greet() {
    console.log('Hello, I am ' + this.name);
  },
};

const fn = user.greet;
fn(); // 'Hello, I am undefined'  (or error in strict mode)
// this is now the global object, not user

This is the most common source of this-related bugs β€” extracting a method and calling it later.

Arrow function captures this from enclosing scope β€” fixes the callback bug

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

  start() {
    // Arrow function inherits this from start()'s scope (the Timer instance)
    setInterval(() => {
      this.seconds++;
      console.log(this.seconds);
    }, 1000);
  }
}

const t = new Timer();
t.start(); // logs 1, 2, 3...

// If we used function() instead of () =>, this.seconds would be undefined

call, apply, and bind β€” setting this explicitly

function introduce(greeting, punctuation) {
  console.log(greeting + ', I am ' + this.name + punctuation);
}

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

introduce.call(alice, 'Hi', '!');    // 'Hi, I am Alice!'
introduce.apply(bob, ['Hey', '?']);  // 'Hey, I am Bob?'

const greetAlice = introduce.bind(alice, 'Hello');
greetAlice('.');  // 'Hello, I am Alice.'
// bind returns a new function β€” useful for event handlers

new binding β€” this is the freshly created instance

function Person(name, age) {
  this.name = name; // this is the new object
  this.age  = age;
}

Person.prototype.describe = function () {
  return `${this.name} is ${this.age} years old`;
};

const alice = new Person('Alice', 30);
console.log(alice.describe()); // 'Alice is 30 years old'
// new creates an empty object, sets this to it, runs the function, returns the object

Common Mistakes

  • Passing a method as a callback without binding it β€” the callback fires in the wrong context and this.property is undefined
  • Using an arrow function as an object method expecting this to be the object β€” arrow functions capture this from outside the object literal, usually the global scope or undefined
  • Forgetting that the rule is "object before the dot at call time" β€” even if greet lives on user, calling const g = user.greet; g() removes the implicit binding
  • Expecting this inside a nested regular function to still refer to the outer object β€” only arrow functions share the outer this; regular nested functions get their own binding

Note

When in doubt, ask yourself: what is to the left of the dot when this function is actually called? That object is `this`. If nothing is to the left of the dot, default binding applies. Arrow functions skip this question entirely β€” they always answer "whatever `this` was where I was written."

Exercises

Write JavaScript code to solve each exercise. Run it and check your output.

Loading editor…

Completed: 0 / 6 exercises

Knowledge Check

Question 1 of 4

What does `this` refer to when a function is called as a method on an object, e.g., `obj.greet()`?