JavaScript this Keyword

The this keyword is one of the most confusing parts of JavaScript. Its value depends on how a function is called, not where it's defined. Master this to write more flexible and powerful code.

Try It Yourself

Explore how this behaves in different contexts:

index.js
// The 'this' keyword in different contexts

const person = {
  name: "Alice",

  // Method - 'this' is the object
  greet() {
    console.log("Method this.name:", this.name);
  },

  // Arrow function - 'this' is inherited (global)
  greetArrow: () => {
    console.log("Arrow this.name:", this.name);
  },

  // Callback issue and fix
  delayedGreet() {
    // Problem: regular function loses 'this'
    setTimeout(function() {
      // console.log(this.name); // undefined!
    }, 100);

    // Solution: arrow function preserves 'this'
    setTimeout(() => {
      console.log("Delayed (arrow):", this.name);
    }, 100);
  }
};

console.log("--- Method calls ---");
person.greet();       // "Alice"
person.greetArrow();  // undefined (arrow inherits global this)

console.log("\n--- Delayed call ---");
person.delayedGreet();

this in Global Context

In the global scope, this refers to the global object:

javascript
// In global scope, 'this' refers to the global object

console.log(this === globalThis); // true (in modern JS)

// In browsers: this === window
// In Node.js: this === global

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

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

// In strict mode, 'this' is undefined in regular functions
"use strict";
function strictThis() {
  console.log(this); // undefined
}
strictThis();
Strict Mode
In strict mode, this is undefined in regular functions called without an object. This helps catch bugs where you accidentally use the global object.

this in Methods

When a function is called as a method, this refers to the object that owns the method:

javascript
// In a method, 'this' refers to the object

const user = {
  name: "Alice",
  age: 25,

  introduce() {
    console.log("Hi, I'm " + this.name);
    console.log("I'm " + this.age + " years old");
  },

  getInfo() {
    return {
      name: this.name,
      age: this.age
    };
  }
};

user.introduce();
// "Hi, I'm Alice"
// "I'm 25 years old"

console.log(user.getInfo());
// { name: "Alice", age: 25 }

Implicit Binding

The object "to the left of the dot" becomes this:

javascript
// Implicit binding: object to the left of the dot

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

person.greet(); // "Hello, Alice" - this = person

// Nested objects: look at the immediate caller
const company = {
  name: "TechCorp",
  employee: {
    name: "Bob",
    greet() {
      console.log("Hello, " + this.name);
    }
  }
};

company.employee.greet(); // "Hello, Bob" - this = employee, not company

Losing this Context

It's easy to lose the this context when passing methods around:

javascript
// Common ways to lose 'this' context

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

// 1. Assigning method to a variable
const greetFunc = person.greet;
greetFunc(); // "Hello, undefined" - 'this' is global/undefined

// 2. Passing as callback
function callFunc(fn) {
  fn();
}
callFunc(person.greet); // "Hello, undefined"

// 3. Using in event handlers (conceptual)
// button.onclick = person.greet; // 'this' would be button, not person

// 4. Using in setTimeout
setTimeout(person.greet, 100); // "Hello, undefined"
Common Pitfall
When you extract a method or pass it as a callback, it loses its connection to the original object. Use bind() or arrow functions to fix this.

Explicit Binding: call, apply, bind

You can explicitly set this using these methods:

javascript
// Explicit binding with call, apply, bind

function greet(greeting, punctuation) {
  console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

// call() - invoke with this, pass args individually
greet.call(person, "Hello", "!");
// "Hello, Alice!"

// apply() - invoke with this, pass args as array
greet.apply(person, ["Hi", "?"]);
// "Hi, Alice?"

// bind() - returns new function with this bound
const greetAlice = greet.bind(person);
greetAlice("Hey", ".");
// "Hey, Alice."

// bind() with partial application
const sayHi = greet.bind(person, "Hi");
sayHi("!!!");
// "Hi, Alice!!!"

new Binding

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

javascript
// 'new' binding: this refers to the new object

function Person(name, age) {
  // 'this' is automatically created as {}
  this.name = name;
  this.age = age;
  // 'this' is automatically returned
}

const alice = new Person("Alice", 25);
console.log(alice.name); // "Alice"
console.log(alice.age);  // 25

// What 'new' does:
// 1. Creates a new empty object
// 2. Sets 'this' to that object
// 3. Executes the constructor
// 4. Returns 'this' (unless you return a different object)

Arrow Functions and this

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

javascript
// Arrow functions: 'this' is lexically inherited

const person = {
  name: "Alice",

  // Regular method
  regularMethod() {
    console.log("Regular:", this.name); // "Alice"
  },

  // Arrow function as property - inherits global 'this'
  arrowProperty: () => {
    console.log("Arrow property:", this.name); // undefined
  },

  // Arrow in method - inherits method's 'this'
  methodWithArrow() {
    const inner = () => {
      console.log("Arrow in method:", this.name); // "Alice"
    };
    inner();
  }
};

person.regularMethod();    // "Alice"
person.arrowProperty();    // undefined
person.methodWithArrow();  // "Alice"
Arrow Functions for Callbacks
Arrow functions are perfect for callbacks because they preserve the outer this. Don't use them as object methods where you need this to refer to the object.

Fixing this in Callbacks

Multiple ways to preserve this in callback functions:

javascript
// Fixing 'this' in callbacks

const team = {
  name: "Development",
  members: ["Alice", "Bob", "Carol"],

  // PROBLEM: regular function callback loses 'this'
  showMembersBroken() {
    this.members.forEach(function(member) {
      // console.log(this.name + ": " + member); // Error! this is undefined
    });
  },

  // SOLUTION 1: Arrow function (most common)
  showMembersArrow() {
    this.members.forEach((member) => {
      console.log(this.name + ": " + member);
    });
  },

  // SOLUTION 2: bind()
  showMembersBind() {
    this.members.forEach(function(member) {
      console.log(this.name + ": " + member);
    }.bind(this));
  },

  // SOLUTION 3: thisArg parameter
  showMembersThisArg() {
    this.members.forEach(function(member) {
      console.log(this.name + ": " + member);
    }, this); // Second argument sets 'this'
  },

  // SOLUTION 4: Save reference (old pattern)
  showMembersSelf() {
    const self = this;
    this.members.forEach(function(member) {
      console.log(self.name + ": " + member);
    });
  }
};

team.showMembersArrow();

Binding Precedence

When multiple rules could apply, JavaScript uses this precedence:

javascript
// Binding precedence (highest to lowest)

function identify() {
  console.log("Name:", this.name);
}

const person = { name: "Alice" };
const another = { name: "Bob" };

// 1. new binding (highest priority)
function Person(name) {
  this.name = name;
}
const p = new Person("Carol");
console.log(p.name); // "Carol"

// 2. Explicit binding (call/apply/bind)
identify.call(person); // "Alice"

// 3. Implicit binding (method call)
const obj = { name: "Dave", identify };
obj.identify(); // "Dave"

// 4. Default binding (lowest - global or undefined)
identify(); // undefined (strict) or global name

// new > explicit > implicit > default

this Quick Reference

Contextthis Value
Global (non-strict)globalThis / window
Global (strict)undefined
Object methodThe object (left of dot)
call/apply/bindExplicitly specified
new Constructor()The new instance
Arrow functionInherited from enclosing scope
Event handlerThe element (DOM)

this in ES6 Classes

Classes have the same this behavior, with some convenient patterns:

javascript
// 'this' in ES6 classes

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

    // Bind method in constructor (common pattern)
    this.increment = this.increment.bind(this);
  }

  increment() {
    this.count++;
    console.log("Count:", this.count);
  }

  // Arrow function as class field (auto-bound)
  decrement = () => {
    this.count--;
    console.log("Count:", this.count);
  }
}

const counter = new Counter();

// Both work when passed as callbacks
const inc = counter.increment;
const dec = counter.decrement;

inc(); // "Count: 1" - works (bound in constructor)
dec(); // "Count: 0" - works (arrow function)

Test Your Knowledge

JavaScript this Keyword Quiz

5 questions
Question 1

What is "this" in a regular function called without an object?

Question 2

Why don't arrow functions have their own "this"?

Question 3

What does bind() return?

Question 4

What is the binding precedence order (highest first)?

Question 5

How do you fix "this" in a forEach callback?

Coding Challenge

Build a Timer Class
Medium

Create a Timer class with start, stop, reset, and getTime methods. The timer should increment every second. Make sure this works correctly in the setInterval callback!

Starter Code
// Challenge: Create a Timer class
// Requirements:
// - start() begins counting seconds
// - stop() pauses the timer
// - reset() sets count back to 0
// - getTime() returns current count
// The timer should increment every second and log the time
// Make sure 'this' works correctly in the setInterval callback!

class Timer {
  constructor() {
    // Your code here
  }

  start() {
    // Your code here
  }

  stop() {
    // Your code here
  }

  reset() {
    // Your code here
  }

  getTime() {
    // Your code here
  }
}

// Test your implementation
const timer = new Timer();
console.log("Starting timer...");
timer.start();

// After 3 seconds, stop and show time
setTimeout(() => {
  timer.stop();
  console.log("Stopped at:", timer.getTime(), "seconds");

  timer.reset();
  console.log("After reset:", timer.getTime(), "seconds");
}, 3500);

Summary

  • this is determined by how a function is called, not where it's defined
  • In methods, this is the object to the left of the dot
  • In regular functions, this is global (or undefined in strict mode)
  • Arrow functions inherit this from their enclosing scope
  • Use call/apply to invoke with explicit this, bind to create a bound function
  • Precedence: new > explicit > implicit > default
  • Fix callbacks with arrow functions or bind()