The 'this' Keyword

How 'this' works in different contexts: global, object, arrow functions, bind/call/apply

Understanding 'this' in JavaScript

The this keyword is one of the most confusing aspects of JavaScript. Unlike other languages where this always refers to the current instance, in JavaScript this depends on how a function is called, not where it's defined.

The 4 Rules of 'this'

  1. Default Binding — Global object (or undefined in strict mode)
  2. Implicit Binding — The object calling the method
  3. Explicit Binding — Manually set via call, apply, or bind
  4. new Binding — The newly created object

1. Default Binding

When a function is called standalone (not as a method), this refers to the global object (or undefined in strict mode):

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

showThis(); // Window (browser) or global (Node)

// In strict mode
"use strict";
function showThisStrict() {
  console.log(this);
}

showThisStrict(); // undefined

2. Implicit Binding

When a function is called as a method of an object, this refers to that object:

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

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

// ⚠️ Losing implicit binding
const greetFn = user.greet;
greetFn(); // "Hello, I'm undefined" — this = global/undefined

// Common in callbacks
setTimeout(user.greet, 1000); // "Hello, I'm undefined"

3. Explicit Binding: call, apply, bind

Force this to be a specific value:

function introduce(greeting, punctuation) {
  console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}

const person = { name: "Bob" };

// call — arguments passed individually
introduce.call(person, "Hi", "!");  // "Hi, I'm Bob!"

// apply — arguments passed as array
introduce.apply(person, ["Hello", "."]);  // "Hello, I'm Bob."

// bind — returns a new function with fixed 'this'
const boundIntro = introduce.bind(person);
boundIntro("Hey", "?");  // "Hey, I'm Bob?"

// Partial application with bind
const sayHiToBob = introduce.bind(person, "Hi");
sayHiToBob("!");  // "Hi, I'm Bob!"

4. new Binding

When using new with a constructor, this refers to the newly created object:

function Person(name) {
  // 'this' is the new object being created
  this.name = name;
  this.sayHi = function() {
    console.log(`Hi, I'm ${this.name}`);
  };
}

const alice = new Person("Alice");
alice.sayHi(); // "Hi, I'm Alice"

// What 'new' does:
// 1. Creates new empty object
// 2. Links it to Person.prototype
// 3. Binds 'this' to new object
// 4. Returns the object (if no explicit return)

Arrow Functions: Lexical 'this'

Arrow functions don't have their own this. They inherit this from their enclosing scope:

const obj = {
  name: "Object",
  
  // Regular function — has its own 'this'
  regularMethod() {
    console.log(this.name); // "Object"
    
    // Problem: nested function loses 'this'
    setTimeout(function() {
      console.log(this.name); // undefined
    }, 100);
  },
  
  // Arrow function — inherits 'this'
  arrowMethod() {
    console.log(this.name); // "Object"
    
    // Solution: arrow inherits 'this' from arrowMethod
    setTimeout(() => {
      console.log(this.name); // "Object" ✓
    }, 100);
  }
};

obj.regularMethod();
obj.arrowMethod();

⚠️ Note: Arrow functions cannot be used as constructors and cannot be bound with call/apply/bind.

'this' in Classes

class Counter {
  constructor() {
    this.count = 0;
    
    // Method 1: Bind in constructor
    this.incrementBound = this.increment.bind(this);
  }
  
  increment() {
    this.count++;
    console.log(this.count);
  }
  
  // Method 2: Arrow function property
  incrementArrow = () => {
    this.count++;
    console.log(this.count);
  };
}

const counter = new Counter();

// Regular method loses 'this' when extracted
const inc = counter.increment;
inc(); // Error: Cannot read property 'count' of undefined

// Bound method works
const boundInc = counter.incrementBound;
boundInc(); // 1

// Arrow property works
const arrowInc = counter.incrementArrow;
arrowInc(); // 2

Binding Priority

When multiple rules apply, priority order (highest to lowest):

Priority Binding Type Example
1 (highest) new binding new Fn()
2 Explicit binding fn.call(obj)
3 Implicit binding obj.fn()
4 (lowest) Default binding fn()

💡 Key Takeaways

  • this is determined by how a function is called, not where it's defined
  • • Use bind, call, or apply to explicitly set this
  • • Arrow functions inherit this from their enclosing scope
  • • In classes, use arrow properties or bind methods to preserve this
  • • When in doubt, console.log(this) to see what it refers to