DEV Community

Cover image for My Friend Failed an Interview at a Top Company Because of One JavaScript Question: this
Kelvyn Thai
Kelvyn Thai

Posted on

My Friend Failed an Interview at a Top Company Because of One JavaScript Question: this

Recently my friend failed an interview at a top tech company.

The interviewer didn't ask about algorithms.
Not about system design.

Just one topic:

The this keyword in JavaScript.

At first glance the questions looked simple.

But they were designed to reveal whether the candidate truly understands runtime binding in JavaScript.

Let's walk through the same interview questions.


The Golden Rule of this

Before looking at the questions, remember this rule:

this is determined by how a function is called, not where it is defined.

This single rule explains most this bugs in JavaScript.

Another important rule:

Arrow functions do not have their own this. They capture it from the surrounding scope.

Understanding these two rules is key to solving the following interview questions.


Interview Question 1 — Object Method vs Unbound Function

const module = {
  x: 42,
  getX() {
    return this.x;
  },
  getXLambda: () => this.x
};

console.log(module.getX());
console.log(module.getXLambda());

const unboundGetX = module.getX;
const unboundGetXLambda = module.getXLambda;

console.log(unboundGetX(), unboundGetXLambda());
Enter fullscreen mode Exit fullscreen mode

What many developers answer

42
42
42
42
Enter fullscreen mode Exit fullscreen mode

Actual result (typical browser environment)

42
undefined
undefined
undefined
Enter fullscreen mode Exit fullscreen mode

Why?

Case 1

module.getX()
Enter fullscreen mode Exit fullscreen mode

This is a method call, so:

this === module
Enter fullscreen mode Exit fullscreen mode

Result:

42
Enter fullscreen mode Exit fullscreen mode

Case 2

module.getXLambda()
Enter fullscreen mode Exit fullscreen mode

Arrow functions do not bind this to the object.

They capture this from the surrounding scope (usually the global scope).

So:

this !== module
Enter fullscreen mode Exit fullscreen mode

Result:

undefined
Enter fullscreen mode Exit fullscreen mode

Case 3

const unboundGetX = module.getX;
unboundGetX();
Enter fullscreen mode Exit fullscreen mode

Now the function is extracted from the object and called as a plain function.

So:

this === window (browser) or undefined (strict mode)
Enter fullscreen mode Exit fullscreen mode

Result:

undefined
Enter fullscreen mode Exit fullscreen mode

Case 4

Arrow functions still reference the outer scope this, so extracting them does not change behavior.

Result:

undefined
Enter fullscreen mode Exit fullscreen mode

Fix

const boundGetX = module.getX.bind(module);
console.log(boundGetX()); // 42
Enter fullscreen mode Exit fullscreen mode

Interview Question 2 — Callable Function Object

Now the interviewer moved to a slightly trickier example.

type MyNum = {
  scale: number;
  defaultValue: number;
  (): number;
};

const MyNum = function () {
  return this.defaultValue * this.scale;
} as unknown as MyNum;

MyNum.defaultValue = 1;
MyNum.scale = 5;

console.log(MyNum());
Enter fullscreen mode Exit fullscreen mode

What developers expect

5
Enter fullscreen mode Exit fullscreen mode

Actual result

NaN
Enter fullscreen mode Exit fullscreen mode

Why?

Calling:

MyNum()
Enter fullscreen mode Exit fullscreen mode

is a plain function call, so this becomes the global object (or undefined in strict mode).

The function effectively evaluates:

window.defaultValue * window.scale
Enter fullscreen mode Exit fullscreen mode

Both are undefined.

undefined * undefined → NaN
Enter fullscreen mode Exit fullscreen mode

Arrow Function Version

Some developers try to fix it using an arrow function.

const MyNum = () => {
  return this.defaultValue * this.scale;
};
Enter fullscreen mode Exit fullscreen mode

This still does not work.

Arrow functions capture this from the surrounding scope, not from the function object.

Fix

Bind the function to itself:

const boundMyNum = MyNum.bind(MyNum);
console.log(boundMyNum()); // 5
Enter fullscreen mode Exit fullscreen mode

Interview Question 3 — Old React Class Component Bug

This problem appeared frequently in older React class components.

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  handleClickLambda = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          Normal Function
        </button>

        <button onClick={this.handleClickLambda}>
          Arrow Function
        </button>
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

What happens?

For the normal function:

this.handleClick
Enter fullscreen mode Exit fullscreen mode

React extracts the function and executes it like:

callback()
Enter fullscreen mode Exit fullscreen mode

So:

this === undefined
Enter fullscreen mode Exit fullscreen mode

Result:

Cannot read property 'setState' of undefined
Enter fullscreen mode Exit fullscreen mode

Fix

Bind it in the constructor:

this.handleClick = this.handleClick.bind(this);
Enter fullscreen mode Exit fullscreen mode

Why Arrow Functions Work

Arrow functions capture this from the class instance, so:

this === Counter instance
Enter fullscreen mode Exit fullscreen mode

This is why arrow functions became popular in React class components.


Why These Questions Matter

These questions are not about syntax.

They test whether you understand:

  • runtime binding
  • callback execution
  • arrow function lexical this
  • how JavaScript functions behave when extracted from objects

Frameworks like React, Node.js, and Express rely heavily on callbacks.

If you misunderstand this, subtle bugs appear everywhere.


Final Takeaway

The most important rule to remember:

this depends on how a function is called, not where it is written.

And:

Arrow functions capture this from their surrounding scope.

If you remember these two rules, debugging most this issues becomes much easier.

And in my friend's case, understanding that rule would have made the difference between passing and failing the interview.

Top comments (4)

Collapse
 
pengeszikra profile image
Peter Vivo • Edited

My answer for js this is skip it.
Do not use OPP and functions just arrow function def by const.

Object function this is root of evil.

Collapse
 
kelvynthai profile image
Kelvyn Thai

Object function this is the root of evil. → Not really. If you compile class syntax, you will receive a function constructor behind the scenes of classes today. Basically, we still need OOP because we still need inheritance, polymorphism, abstraction, etc. To achieve this, we still need function prototypes, which only exist inside normal functions, not lambda/arrow functions. I have many articles regarding prototypes, function constructors,... and why we need them. Feel free to reference them.

Collapse
 
pengeszikra profile image
Peter Vivo

Yes I worked with

function foo (config) { /** make a jungle */ };
// then two whole different story was:

var niceWood = foo(niceWoodConfig);

// .vs

var niceWood = new foo(niceWoodConfig);
Enter fullscreen mode Exit fullscreen mode

I try avoid the second way, because whole of OOP concept is seems too rigid for me. This is of course just my personal taste.

Thread Thread
 
kelvynthai profile image
Kelvyn Thai • Edited

Sure, I believe if you plan to work with OOP, it’s better to use the class syntax because it is very simple and straightforward to understand. It is terrible for developers to use OOP through constructor inheritance. Besides that, adopting OOP in this case does not seem natural.

function Animal() {}

Animal.prototype.eat = function () {
  console.log("Nhoammmm");
};

function inheritPrototype(proto: unknown) {
  function ChainLink() {}
  ChainLink.prototype = proto;
  return new ChainLink();
}

function Human() {}

Human.prototype = inheritPrototype(Animal.prototype);

Human.prototype.work = function () {
  console.log("Coding and earning money!");
};

const kelvynThai = new Human();
console.log(typeof Human, typeof Human.prototype); // function, object
console.log(kelvynThai instanceof Human); // true
console.log(typeof kelvynThai); // object
console.log(Object.getPrototypeOf(kelvynThai) === Human.prototype); // true
console.log(Object.getPrototypeOf(Human.prototype) === Animal.prototype); // true
kelvynThai.work(); // Coding and earning money!
kelvynThai.eat(); // Nhoammmm

Enter fullscreen mode Exit fullscreen mode