JavaScript Fundamentals: Prototype Chains and How 'this' Is Bound
Company: HubSpot
Role: Frontend Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Onsite
This round of a frontend-focused interview track targets core JavaScript language behaviour rather than any framework: how prototypes and inheritance work, how the value of `this` is decided in different call contexts, and reasoning precisely about what a piece of code does. The snippets used in the interview are not reproduced here; the parts below rebuild those topics as practice. Work through each part without running the code, and explain every prediction.
### Constraints and Clarifications
- Assume modern JavaScript (ES2015 or later). The snippets declare strict mode, and class bodies are always strict.
- Predictions must come with the rule that produces them; a correct output with no reason earns little credit.
### Clarifying Questions
- Is the code running in strict mode or sloppy mode, and as a module or a classic script?
- Is it running in a browser or in Node.js, given that the global object differs?
- May I run the code, or should I reason it through?
- Which language features may I use in rewrites, for example class fields?
### Part 1 — Predict how `this` is bound
```javascript
'use strict';
const counter = {
count: 0,
increment() {
this.count += 1;
return this.count;
},
reader() {
return () => this.count;
},
};
const detached = counter.increment;
const boundToOther = counter.increment.bind({ count: 10 });
const read = counter.reader();
console.log(counter.increment()); // (1)
console.log(boundToOther()); // (2)
console.log(boundToOther.call({ count: 100 })); // (3)
console.log(read.call({ count: 50 })); // (4)
try {
detached();
console.log('no error');
} catch (err) {
console.log(err instanceof TypeError); // (5)
}
```
Predict what each numbered line prints. Then state the rules that decide `this`, in order of precedence.
```hint Read each line as a call
For every numbered line, write down exactly how the function is being invoked at that moment and what, if anything, sits to the left of the dot.
```
#### What This Part Should Cover
- The correct output for every line, each with its reason
- The precedence between construction, explicit binding, method calls and plain calls
- How arrow functions get their `this`
- How strict versus sloppy mode changes the plain-call case
### Part 2 — Trace the prototype chain
```javascript
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return `${this.name} makes a sound`;
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function () {
return `${this.name} barks`;
};
const rex = new Dog('Rex');
console.log(rex.speak()); // (a)
console.log(Object.getPrototypeOf(rex) === Dog.prototype); // (b)
console.log(rex instanceof Animal); // (c)
console.log(rex.hasOwnProperty('speak')); // (d)
delete Dog.prototype.speak;
console.log(rex.speak()); // (e)
Animal.prototype.speak = function () {
return 'changed';
};
console.log(rex.speak()); // (f)
```
Predict each lettered line, then explain step by step what `new Dog('Rex')` does and why the three lines after the `Dog` function are written the way they are.
```hint Walk the links
For each property access, list the objects the engine checks, in order, until it finds the name.
```
#### What This Part Should Cover
- Correct outputs, each with the lookup path that produced it
- The difference between own and inherited properties
- The fact that prototypes are shared objects whose changes reach existing instances
- The steps `new` performs, and the purpose of each setup line
### Part 3 — Rewrite with class syntax and fix a lost `this`
Rewrite `Animal` and `Dog` with `class`, `extends` and `super`, keeping the same behaviour. Explain what stays the same underneath and what genuinely differs from the function-based version.
Then a teammate writes the helper below and calls `runLater(rex.speak)` on an instance of your class-based `Dog`. It throws a `TypeError`.
```javascript
function runLater(fn) {
return fn();
}
```
Explain why it throws, and give two different fixes with the trade-off between them.
```hint Same machinery, new syntax
Compare the objects and links each version creates, then look for behaviour that the class version adds and the function version cannot express.
```
#### What This Part Should Cover
- A correct class rewrite using `extends` and `super`
- The real differences between class and function-based inheritance
- A diagnosis of the `TypeError` and two fixes with their trade-off
### What a Strong Answer Covers
- Predictions derived from rules rather than remembered outputs
- One connected mental model: a method found on a prototype still runs with the receiver of the call as `this`
- Awareness of environment differences such as strict versus sloppy mode and browser versus Node.js
- Precise vocabulary: a function's `prototype` property versus an object's prototype link, own properties, the receiver
- Clear thinking out loud while reasoning about each line
### Follow-up Questions
- What does `new` return when a constructor explicitly returns an object, and when it returns a primitive?
- Implement a simplified `bind` yourself. How should it behave when its result is called with `new`?
- Why can arrow functions not be used as constructors, and what else do they lack compared with regular functions?
- What changes if `speak` is declared as an arrow-function class field instead of a method, in terms of memory use and inheritance?
Overview: A JavaScript fundamentals question that asks you to predict and explain how this is bound in method calls, detached calls, bound functions and arrow functions, trace property lookup through a prototype chain, and rewrite constructor-based inheritance with class syntax. It tests precise reasoning about core language behaviour.
Read the full HubSpot Frontend Engineer interview experience this question came from