Home/Learn/JavaScript & TypeScript/`this` — Four Rules and an Arrow Function

`this` — Four Rules and an Arrow Function

Intermediate
Language Core

In JavaScript `this` is decided by how a function is called, not where it is defined — except in arrow functions, which have no `this` of their own and inherit it from the surrounding scope.

Overview

This is where Java intuition fails hardest. In Java, this always means the instance the method belongs to. In JavaScript, a plain function has no fixed this; it is bound at call time by one of four rules, and the same function can have a different this on every call. That is why extracting a method from an object breaks it, and why pre-2015 code is full of `const self = this`. Arrow functions were introduced partly to fix this: they do not get their own this at all, so they see whatever was in scope where they were written.

The Four Rules, In Priority Order

Work down the list; the first that applies wins.

How `this` is chosen, in order
// 1. new binding — this is the newly created object
function User(name) { this.name = name }
const u = new User('Asha')       // this === u

// 2. explicit binding — call, apply, bind
function greet() { return this.name }
greet.call({ name: 'Ravi' })     // 'Ravi'
const bound = greet.bind(user)   // permanently bound

// 3. implicit binding — the object left of the dot
const obj = { name: 'Meera', greet }
obj.greet()                      // 'Meera'

// 4. default — undefined in strict mode / modules,
//    globalThis in sloppy mode
const loose = obj.greet
loose()                          // undefined (strict) — the classic bug

The Lost-Binding Bug

Passing a method as a callback detaches it from its object. This is the single most common this bug, and it appears constantly with event handlers and array callbacks.

Why passing a method as a callback breaks it
class Cart {
  constructor() { this.items = [] }
  add(item) { this.items.push(item) }   // needs this
}

const cart = new Cart()
;['a', 'b'].forEach(cart.add)     // TypeError — this is undefined

// Three fixes, cheapest last:
;['a','b'].forEach(item => cart.add(item))   // arrow wrapper
;['a','b'].forEach(cart.add.bind(cart))      // explicit bind
// or define it as a class field, bound at construction:
class Cart2 {
  items = []
  add = (item) => { this.items.push(item) }  // arrow — lexical this
}

Arrow Functions Have No `this`

An arrow function looks up this lexically, exactly like any other variable. That makes it right for callbacks and wrong for object methods.

Method: regular. Callback: arrow.
const timer = {
  seconds: 0,
  // WRONG: arrow at method level — this is the module, not timer
  startBad: () => { setInterval(() => this.seconds++, 1000) },

  // RIGHT: regular method, arrow callback inherits its this
  start() { setInterval(() => this.seconds++, 1000) },
}

// Pre-2015 you saw this everywhere, doing the same job:
function Timer() {
  const self = this
  setInterval(function () { self.seconds++ }, 1000)
}

Key Points to Remember

  • 1`this` is bound by how a function is called, not where it is defined
  • 2Priority: new > explicit (call/apply/bind) > implicit (object before the dot) > default
  • 3Extracting a method from an object loses its binding — the commonest `this` bug in real code
  • 4Arrow functions have no `this`; they inherit it lexically, which is why they are right for callbacks
  • 5Never use an arrow function as an object method that needs `this` — it will point at the enclosing scope

Interview Questions

Sign in to ask Aria
1

How is the value of `this` determined in a regular function?

Medium
2

What is the difference between call, apply and bind?

Medium
3

Why does an arrow function behave differently as an object method than a regular function?

Hard

Ask Aria about `this` — Four Rules and an Arrow Function

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…