Output: The 'this' Keyword

⭐ Interview Importance: HIGH
⏱️ Revision Time: 4 min

TL;DR

this output questions test whether you understand the difference between Implicit Binding (object methods), Default Binding (bare function calls), and Lexical Binding (Arrow functions).

Question 1: The Method Trap

const user = {
  name: "Alice",
  greet() {
    return `Hello, ${this.name}`;
  }
};

const sayHello = user.greet;

console.log(user.greet());
console.log(sayHello());

Output:

"Hello, Alice"
"Hello, undefined" // (Or an error in Strict Mode)

Why?
The this keyword is NOT determined by where the function was written. It is determined purely by how it is called.

  • user.greet() is called with a dot. Implicit binding applies. this equals user.
  • sayHello() is called bare. There is no dot to its left. Default binding applies. this equals the global window object. window.name is undefined.

Question 2: Arrow Functions in Objects

const obj = {
  id: 42,
  traditional: function() {
    console.log(this.id);
  },
  arrow: () => {
    console.log(this.id);
  }
};

obj.traditional();
obj.arrow();

Output:

42
undefined

Why?
Arrow functions completely ignore the “dot” rule. They do not bind their own this. They inherit this from their parent Lexical Scope. Object literals {} do not create a scope in JavaScript. So the arrow function looks past the object directly to the Global Scope. window.id is undefined. Never use arrow functions as object methods.


Question 3: The Callback Context

const worker = {
  task: "Build House",
  doWork() {
    setTimeout(function() {
      console.log(`Working on: ${this.task}`);
    }, 100);
  }
};

worker.doWork();

Output:

"Working on: undefined"

Why?
This is a classic bug. worker.doWork() correctly sets this to worker inside the doWork function. However, the setTimeout callback is a completely different function. When the timer finishes, the browser’s Event Loop pulls the callback from the Macrotask Queue and executes it bare. Since it’s called bare, this defaults to window.
(Fix: Use an arrow function for the setTimeout callback () => { ... }. The arrow function will look up the scope chain, find doWork(), and borrow its worker context).


Question 4: Binding Precedence

function show() {
  console.log(this.val);
}

const obj1 = { val: 1 };
const obj2 = { val: 2 };

const boundFunc = show.bind(obj1);
boundFunc.call(obj2);

Output:

1

Why?
Once a function is bound using .bind(), its this context is permanently locked forever. You cannot override it by using .call(), .apply(), or even another .bind(). The JavaScript engine ignores the attempt to bind it to obj2.