Vivek
Back to Articles

this Keyword in Javascript

9/13/2025

Demystifying the this Keyword in JavaScript

Welcome! This guide is designed to give you a rock-solid understanding of one of JavaScript's most powerful, yet often misunderstood, concepts: the this keyword. We’ll explore the rules, exceptions, and interview-style puzzles with examples.


📖 What is this?

  • this is a special keyword in JavaScript that refers to an object.
  • The value of this is dynamic: it depends not on where the function is defined, but on how it’s called.

👉 Think of this like the pronoun he/she/they: its meaning changes depending on context.


🔑 The Four Rules of this Binding

We’ll use the following vivek object throughout:

const vivek = {
  name: 'Vivek',
  role: 'Software Engineer',
  languages: ['JavaScript', 'Python', 'Java'],
  introduce: function() {
    console.log(`Hi, I'm ${this.name}, and I'm a ${this.role}.`);
  }
};

1. Implicit Binding (Dot Rule)

When a function is called as a method (via dot), this is the object to the left of the dot.

vivek.introduce();
// Output: Hi, I'm Vivek, and I'm a Software Engineer.

2. Explicit Binding (call, apply, bind)

You can force a function’s this.

function describeLanguages() {
  console.log(`${this.name} codes in: ${this.languages.join(', ')}.`);
}

describeLanguages.call(vivek);
// Vivek codes in: JavaScript, Python, Java.

const boundFn = describeLanguages.bind(vivek);
boundFn(); 
// Vivek codes in: JavaScript, Python, Java.

3. new Binding (Constructor Functions)

When called with new, a function creates an object, and this refers to it.

function SoftwareEngineer(name, company) {
  this.name = name;
  this.company = company;
}

const virat = new SoftwareEngineer('Virat', 'Google');
console.log(virat.name); // Virat

4. Default Binding (Fallback)

If none of the other rules apply:

  • Non-strict → global object (window in browsers).
  • Strict → undefined.
function showMeThis() {
  console.log(this);
}
showMeThis(); 
// window (browser) or global (Node)
'use strict';
function strictFn() {
  console.log(this);
}
strictFn(); 
// undefined

⚡ The Big Exception: Arrow Functions

  • Arrow functions do not bind their own this.
  • Instead, they inherit this from the enclosing scope.
const project = {
  name: 'Project Javasctipt',
  team: ['Vivek', 'Virat'],
  showTeam: function() {
    this.team.forEach(member => {
      console.log(`${member} is on ${this.name}`);
    });
  }
};

project.showTeam();
// Vivek is on Project Javasctipt
// Virat is on Project Javasctipt

If we had used a regular function inside forEach, this would not refer to project.


🎯 Binding Precedence

If multiple rules apply, precedence is:

  1. new binding → this is the new object.
  2. Explicit binding → .call, .apply, .bind.
  3. Implicit binding → object before dot.
  4. Default binding → global or undefined.

👉 Arrow functions are special—they don’t follow these rules.


🧠 Tricky Interview Questions & Output Puzzles

Q1. Losing context

const vivek = {
  name: 'Vivek',
  greet: function() {
    console.log(`Hello, ${this.name}!`);
  }
};

const sayHello = vivek.greet;
sayHello();

Answer: Hello, undefined! (strict mode) or Hello, ! (non-strict global). Context is lost because sayHello is a plain function call → default binding.


Q2. Callback Conundrum

const vivek = {
  name: 'Vivek',
  tasks: ['Code', 'Debug', 'Deploy'],
  printTasks: function() {
    this.tasks.forEach(function(task) {
      console.log(`${this.name} needs to ${task}`);
    });
  }
};
vivek.printTasks();

Answer:

needs to Code
needs to Debug
needs to Deploy

Because the callback function falls back to default binding. Fix: use arrow functions.


Q3. Arrow function as method

const vivek = {
  name: 'Vivek',
  role: 'Software Engineer',
  getDetails: () => {
    console.log(`Name: ${this.name}, Role: ${this.role}`);
  }
};
vivek.getDetails();

Answer: Name: undefined, Role: undefined Arrow functions don’t have their own this → this comes from the global scope.


Q4. Explicitly binding arrow functions

const vivek = { name: 'Vivek' };
const sayName = () => console.log(this.name);
sayName.call(vivek);

Answer: undefined Arrow functions ignore .call/.apply/.bind.


Q5. Precedence puzzle

'use strict';

function Person(name) {
  this.name = name;
  this.speak = function() {
    console.log(`I am ${this.name}`);
  }
}

const vivek = new Person('Vivek');
const virat = { name: 'Virat' };

vivek.speak.call(virat);

Answer: I am Virat Explicit binding (.call) overrides implicit binding.


Q6. Constructor returning an object

function Person(name) {
  this.name = name;
  return { name: 'Overridden' };
}

const vivek = new Person('Vivek');
console.log(vivek.name);

Answer: Overridden Returning an object explicitly replaces the constructed this.


Q7. setTimeout gotcha

const vivek = {
  name: "Vivek",
  greet: function () {
    setTimeout(function () {
      console.log("Hi " + this.name);
    }, 0);
  }
};
vivek.greet();

Answer: Hi undefined Because this inside the callback defaults to global. Fix with an arrow function.


✅ Key Takeaways

  • this is not lexical (except with arrow functions).
  • Context matters: how you call determines this.
  • Binding precedence: new > explicit > implicit > default.
  • Arrow functions "capture" this from their enclosing scope.
  • Common pitfalls: callbacks, event handlers, assigning methods to variables.