The this keyword refers to the execution context of a function call. Its value is not determined by where a function is written — it depends entirely on how the function is invoked. Misunderstanding this is one of the most common sources of bugs in JavaScript.

The Four Binding Rules (Plus Arrow Functions)

Rule Invocation this value
Default fn() globalThis (or undefined in strict mode)
Implicit obj.fn() obj
Explicit fn.call(obj) specified object
new new Fn() newly created instance
Arrow () => {} lexical this from enclosing scope

Default Binding

Calling a standalone function uses default binding:

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

showThis(); // window (browser, non-strict) or undefined (strict mode)
  

In strict mode, plain function calls set this to undefined — preventing accidental global access.

Implicit Binding

When a function is accessed as a property of an object and called with dot notation, this is that object:

  const user = {
    name: 'Alice',
    greet() {
        console.log(`Hello, ${this.name}`);
    }
};

user.greet(); // 'Hello, Alice' — this === user
  

Lost Binding (The Classic Bug)

Extracting a method loses the object context:

  const greet = user.greet;
greet(); // 'Hello, undefined' — this is NOT user

// Common in callbacks:
setTimeout(user.greet, 1000); // also broken
  

Fixes:

  // 1. Bind explicitly
setTimeout(user.greet.bind(user), 1000);

// 2. Wrapper function
setTimeout(() => user.greet(), 1000);

// 3. Arrow method (inherits lexical this — see below)
const timer = {
    count: 0,
    start() {
        setInterval(() => {
            this.count++; // this === timer
        }, 1000);
    }
};
  

Explicit Binding: call, apply, bind

Force this to a specific value:

  function greet(greeting, punctuation) {
    console.log(`${greeting}, ${this.name}${punctuation}`);
}

const user = { name: 'Bob' };

greet.call(user, 'Hi', '!');       // 'Hi, Bob!'
greet.apply(user, ['Hello', '.']); // 'Hello, Bob.'

const boundGreet = greet.bind(user);
boundGreet('Hey', '?');            // 'Hey, Bob?'
  
Method Arguments Returns
call Comma-separated Immediate invocation
apply Array Immediate invocation
bind Comma-separated New function with fixed this

new Binding

When a function is invoked with new, this is the newly created object:

  function Person(name) {
    this.name = name;
}

const p = new Person('Charlie');
console.log(p.name); // 'Charlie'
  

Arrow Functions and Lexical this

Arrow functions do not have their own this. They inherit this from the enclosing scope at write time:

  const counter = {
    count: 0,
    start() {
        setInterval(() => {
            this.count++; // this === counter (lexical)
            console.log(this.count);
        }, 1000);
    }
};

// Broken with regular function:
const broken = {
    count: 0,
    start() {
        setInterval(function() {
            this.count++; // this === window or undefined
        }, 1000);
    }
};
  

Do not use arrow functions as object methods when you need this to refer to the object:

  const bad = {
    name: 'Dave',
    greet: () => console.log(this.name) // this is outer scope, not bad
};
  

this in Event Handlers

  button.addEventListener('click', function() {
    console.log(this); // the button element
    this.classList.toggle('active');
});

button.addEventListener('click', () => {
    console.log(this); // this from enclosing scope (NOT the button)
});
  

Use regular functions when you need the event target as this.

this in Classes

Class methods use implicit binding — this refers to the instance:

  class Calculator {
    constructor(value = 0) {
        this.value = value;
    }
    add(n) {
        this.value += n;
        return this; // enable chaining
    }
    getResult() {
        return this.value;
    }
}

const calc = new Calculator(10);
calc.add(5).add(3);
console.log(calc.getResult()); // 18
  

Best Practices

  • Use arrow functions for callbacks inside methods when you need the outer this.
  • Use .bind() or wrapper functions when passing methods as callbacks.
  • Avoid storing unbound method references — bind in the constructor or use arrow class fields.
  • In strict mode, assume plain functions have this === undefined.
  • Prefer class syntax for object-oriented patterns — this behavior is more predictable.

Troubleshooting

Symptom Cause Fix
this is undefined in method Method passed as callback without binding .bind(), arrow wrapper, or class field arrow
this is window unexpectedly Non-strict plain function call Enable strict mode; check invocation style
Arrow method has wrong this Arrow functions ignore object context Use regular method syntax
this changes between calls Different invocation styles Trace how the function is called

Understanding this binding rules — default, implicit, explicit, new, and lexical — is essential for object-oriented patterns, event handlers, and any code that passes functions as callbacks.