How does the this keyword work in JavaScript?

Swiggy

this keyword

The this keyword in JavaScript is a special keyword that refers to the current context in which code is being executed. Think of it as a reference to the "owner" of the currently executing code.

The this keyword allows you to reuse functions with different contexts.

I'll break down each scenario in more detail to help you understand how this behaves in different contexts.

Global Context & Strict Mode

function showThis() {
console.log(this);
}
// Call site 1: Direct invocation
showThis(); // Output: Window {...}
// WHY: When a function is called without any context (not as a method),
// 'this' defaults to the global object (Window in browsers)
// Call site 2: Strict mode
function strictShowThis() {
'use strict';
console.log(this);
}
strictShowThis(); // Output: undefined
// WHY: Strict mode changes this behavior to prevent accidental global object access.
// It makes 'this' undefined instead of defaulting to global object.

Object Method Context

const person = {
name: 'John',
sayHi: function () {
console.log('Hi, I am ' + this.name);
},
};
// Call site 1: Method invocation
person.sayHi(); // Output: "Hi, I am John"
// WHY: When a method is called directly on an object, 'this' binds to that object.
// The call site shows person.sayHi(), so JavaScript knows to bind 'this' to person.
// Call site 2: Function reference
const greet = person.sayHi;
greet(); // Output: "Hi, I am undefined"
// WHY: We've lost the object context! When we assign the function to a new variable,
// we're just getting the function itself, without its original object binding.
// The call site is now just greet(), so 'this' defaults to global/undefined.

Constructor Functions

function User(name) {
this.name = name;
this.sayName = function () {
console.log('My name is ' + this.name);
};
}
// Call site 1: With 'new' keyword
const user1 = new User('John');
user1.sayName(); // Output: "My name is John"
// WHY: The 'new' keyword creates a new empty object and makes 'this' point to it.
// It's like saying "create a new context for this function"
// Call site 2: Without 'new' (wrong!)
const user2 = User('Jane');
// WHY: Without 'new', it's a regular function call, so 'this' points to global
// This accidentally sets window.name = "Jane" (a common mistake)

With new, JavaScript creates an object whose prototype is User.prototype, runs the function with this set to that object, and returns it. The only exception is when the function returns a different object; a returned primitive is ignored.

Event Listeners

const button = document.createElement('button');
button.addEventListener('click', function () {
console.log(this); // Output: <button> element
});
// WHY: the browser sets 'this' to the element the
// listener is attached to (event.currentTarget). That
// can differ from the element that was clicked
// (event.target), for example a child <span>.
button.addEventListener('click', () => {
console.log(this); // Output: Window or parent scope's 'this'
});
// WHY: Arrow functions are different! They inherit 'this' from their enclosing scope.
// They don't get their own 'this' binding.

Explicit Binding (bind/call/apply)

function introduce() {
console.log('I am ' + this.name);
}
const person1 = { name: 'John' };
const person2 = { name: 'Jane' };
// Call site 1: Using call/apply
introduce.call(person1); // Output: "I am John"
introduce.apply(person2); // Output: "I am Jane"
// WHY: call/apply let you explicitly set what 'this' should be.
// It's like saying "run this function, but pretend it belongs to this object"
// Call site 2: Using bind
const introducePerson1 = introduce.bind(person1);
introducePerson1(); // Output: "I am John"
// WHY: bind creates a new function with 'this' permanently set to the specified object.
// It's like creating a copy of the function that always remembers its context.

call and apply differ only in how they take arguments: call takes them one by one, apply takes them as an array. bind does not call the function right away; it returns a new function to call later.

function introduce(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const user = { name: 'John' };
introduce.call(user, 'Hi', '!'); // "Hi, John!"
introduce.apply(user, ['Hi', '!']); // "Hi, John!"

Arrow functions

Arrow functions do not have their own this. They take this from the surrounding scope where they are defined. This is called lexical this. It is useful for keeping the outer this inside a callback:

const user = {
name: 'John',
friends: ['Jane', 'Bob'],
showFriends() {
this.friends.forEach((friend) => {
console.log(`${this.name} knows ${friend}`);
});
},
};
user.showFriends();
// "John knows Jane"
// "John knows Bob"

With a regular function inside forEach, this would be undefined (or window) because the callback gets default binding.

Common Pitfalls

Do not use an arrow function as an object method and expect this to be the object. Arrow functions take this from where they are defined, not from the object they are attached to. Use regular functions for object methods.

Binding rules and their precedence

The cases above reduce to five rules for how this gets its value:

  1. Default binding: a plain call such as fn(). this is the global object, or undefined in strict mode.
  2. Implicit binding: a method call such as obj.fn(). this is the object to the left of the dot.
  3. Explicit binding: call, apply or bind set this directly.
  4. new binding: new Fn() sets this to the newly created object.
  5. Arrow functions: no own this; it comes from the surrounding scope.

When more than one rule could apply, the order from highest to lowest priority is:

  1. new binding
  2. Explicit binding (call, apply, bind)
  3. Implicit binding (method call)
  4. Default binding

Arrow functions ignore all of the above and always use lexical this.

Key Points

  • JavaScript determines this at runtime based on the call site (how the function is called)
  • The value of this is not determined by where the function is defined, but by how it's called
  • Arrow functions have no own this; they inherit it from their surrounding scope
  • Explicit binding methods (call, apply, bind) let you control what this refers to
  • The new keyword creates a fresh context, making this point to the new object

This design allows for flexible function reuse and method borrowing, but it can also lead to confusion if you're not careful about how functions are called.