Free lesson · JavaScript Basic to Advance
Interview Lens: this, Classes, and OO Patterns
The Question
Interview questions around this, classes, and OO design cluster into four patterns:
"Predict what
thisis in this code." Output-prediction puzzles involving methods, callbacks, arrow functions, and detachment."Explain how JavaScript classes work under the hood."
"Implement [some design pattern] in JavaScript." Singleton, factory, observer, decorator.
"When would you use inheritance versus composition?"
These come up in nearly every JavaScript interview at the senior level and frequently at mid-level. The interviewer wants to know whether you understand JavaScript's object model well enough to make good design decisions, not just whether you can write classes.
I (Brandon) covered the technical material in Topics 8.2-8.6. I'll handle the OO design questions here. Arun joins for the interview-psychology framing and the this-prediction puzzles — that's his specialty after surviving fourteen rejections that often opened with these exact questions.
Why They Ask It
this, classes, and OO design questions test multiple things at once. They test understanding of the language runtime model (this binding). They test knowledge of the syntax-vs-mechanism distinction (classes as prototype sugar). And they test design judgment (inheritance vs composition, choosing the right pattern).
Three signals interviewers listen for:
Signal 1: Do you trace this carefully, or do you guess? this-related output-prediction puzzles are designed to reveal whether you've internalized the four rules from Topic 8.1. Candidates who guess get inconsistent results. Candidates who trace through "this function is called as obj.method(), so Rule 1 applies, this is obj" reliably get the right answer.
Signal 2: Do you connect classes to prototypes? A candidate who treats classes as a completely separate feature from prototypes will be lost when something behaves unexpectedly. Senior interviewers look for "classes are syntactic sugar over the prototype mechanism" — said naturally, not recited.
Signal 3: Can you reason about design choices? "Use inheritance" is rarely the only valid answer. The signal of senior-level thinking is being able to talk about trade-offs — when inheritance is clean, when composition is cleaner, when neither is right.
Arun here. The interview that taught me the most about this was actually one of my earlier failures. The interviewer asked me to walk through a code example involving a class method passed as a callback. I rushed through it and got the answer wrong. He then asked me to slow down and trace each step out loud. I got it right on the second pass.
The lesson: with this, never trust your intuition on a first read. Always trace.
The Wrong Answers
Warning: The OO mistakes interviewers see most often.
"Arrow functions are just shorter regular functions." Wrong. The bigger difference is
thisbinding. Arrow functions inheritthisfrom the surrounding scope. Regular functions get their ownthisbased on how they're called. A candidate who doesn't know this signals incomplete understanding ofthis."Classes in JavaScript work just like classes in Java." They don't. JavaScript classes are syntactic sugar over prototypes. Inheritance is prototypal, not classical. Methods are shared via prototype, not duplicated per instance. The behavior is similar at a surface level, but the mechanism is different.
"
thisis determined at function definition time." No. It's determined at call time, based on how the function is called. This is the single most commonthis-related wrong answer."You should always use inheritance for code reuse." No. Inheritance for code reuse alone often creates unwanted type coupling. Composition reuses code without the coupling. The senior answer is "favor composition over inheritance."
The Right Answer
Interactive animation: Four sentences to carry into the interview - one each for this, class, super and design.
Three layers depending on the question and the depth expected.
The 30-Second Answer (for this questions)
"this in JavaScript is determined by how a function is called. There are four rules: method calls (obj.method()) make this the object before the dot; plain function calls make this undefined in strict mode; new calls make this the new instance; call/apply/bind explicitly set this. Arrow functions ignore these rules and inherit this from the surrounding scope."
For class questions:
"Classes in JavaScript are syntactic sugar over the prototype mechanism. class User { method() {} } creates a constructor function User and attaches method to User.prototype. Instances created with new User() have their prototype set to User.prototype, so they can access methods through the prototype chain. The mechanism is identical to the old constructor-function-plus-prototype pattern."
Interactive animation: The class answer written out - a constructor function, greet attached once to User.prototype, and typeof User is "function".
The 2-Minute Answer
Build on the above:
"The reason this rules matter in practice is that methods can lose their this binding when detached. Storing obj.method in a variable, passing it as a callback, or using it as an event handler all strip the this context. The fix is either bind to permanently fix this to the original object, or wrap the call in an arrow function that preserves this from the surrounding scope.
For classes, the syntactic-sugar relationship matters because it explains behaviors that would otherwise seem mysterious. typeof MyClass returns 'function' because classes are functions underneath. instanceof works by walking the prototype chain. Subclasses inherit methods because extends wires up the prototype chains correctly. Once you see classes as 'a clean syntax for prototypal inheritance,' edge cases become predictable.
The composition-vs-inheritance question is about coupling. Inheritance creates strong type coupling — the child class is permanently tied to its parent. Composition creates loose coupling — behaviors are mixed in but classes remain independent. For genuine 'is-a' relationships, inheritance is appropriate. For 'has-a' or behavior reuse, composition is usually cleaner and more flexible."
The Deep Dive
For senior or staff interviews:
"At the engine level, every JavaScript function has a hidden mechanism that determines this when called. The four rules — method call, plain call, constructor call, explicit binding — are implemented at this level. Arrow functions don't have their own binding mechanism; instead, they use the this of their lexical enclosing context, captured at definition time.
The class syntax in ES6 standardized what had been a fragmented set of inheritance patterns. Before classes, every framework had its own way of doing OO: different inheritance helpers, different ways to call super-class constructors, different ways to set up the prototype chain. Classes gave the community one standard. The mechanism didn't change — it's still prototypal — but the syntax converged.
The design question of composition vs inheritance has a long history in OO design. The classic Effective Java guidance — favor composition over inheritance — applies in JavaScript too. The reasons are about coupling: inheritance creates tight relationships between classes that make changes risky. Composition creates looser relationships through interfaces (or in JavaScript, through duck typing and mixins) that are easier to evolve.
For the cases where inheritance is appropriate, JavaScript classes work well. For the cases where composition is appropriate, JavaScript's flexibility — mixins, factory functions, dependency injection — gives you many options. The key is making the choice deliberately, not defaulting to inheritance because the syntax exists."
Walking Through this Puzzles
A pattern interviewers love: code with multiple ways the same method gets called, asking what each prints.
class Counter {
count = 0;
increment() {
this.count++;
return this.count;
}
}
const c = new Counter();
const inc = c.increment;
console.log(c.increment()); // ?
console.log(c.increment()); // ?
try {
console.log(inc()); // ?
} catch (e) {
console.log(`Error: ${e.message}`);
}
console.log(c.increment.call(c)); // ?
Walk through it out loud:
"First call: c.increment(). Method call on c, so Rule 1 applies — this is c. Increments c.count to 1. Returns 1.
Second call: same — Rule 1, this is c. Increments to 2. Returns 2.
Third call: inc(). This is a plain function call — Rule 2 — so this is undefined in strict mode (classes run in strict mode). When the body tries this.count++, it throws because you can't read count on undefined.
Fourth call: c.increment.call(c). Explicit binding via call — Rule 4 — this is c. Increments to 3. Returns 3."
If you can walk through this without hesitation, you've signaled mastery of the four rules.
Interactive animation: A class-flavoured this quiz traced call by call - which rule fires each time, and what it returns.
The Pattern Implementation Questions
A common interview question: "Implement [some design pattern] in JavaScript." A clean version of one of the most-asked, the Observer pattern:
class EventBus {
#listeners = {};
on(event, callback) {
if (!this.#listeners[event]) {
this.#listeners[event] = [];
}
this.#listeners[event].push(callback);
}
off(event, callback) {
if (!this.#listeners[event]) return;
this.#listeners[event] = this.#listeners[event].filter(c => c !== callback);
}
emit(event, data) {
if (!this.#listeners[event]) return;
this.#listeners[event].forEach(callback => callback(data));
}
}
const bus = new EventBus();
const handler = data => console.log(`Got: ${data}`);
bus.on("ping", handler);
bus.emit("ping", "hello"); // "Got: hello"
bus.emit("ping", "world"); // "Got: world"
bus.off("ping", handler);
bus.emit("ping", "nothing here"); // (no output)
Walk through it:
"EventBus stores listeners in a private map, keyed by event name. The on method registers a callback for an event. The off method removes a specific callback. The emit method calls all callbacks registered for an event with the given data.
Private field via #listeners keeps the internal map invisible from outside — clients can only interact through on, off, emit. This is the publisher-subscriber pattern, also called observer."
The interviewer might extend: "Add a once method that registers a callback that only fires once." That requires wrapping the callback in a function that calls the original and then immediately removes the wrapper. Worth practicing.
The Follow-Ups
Interview: What the interviewer asks next.
"What's the difference between
__proto__and.prototype?" Covered in Topic 7.8.__proto__is an instance's prototype..prototypeis a property of functions that becomes the prototype ofnewinstances."How would you implement a Singleton?" Class with a static
getInstancemethod that returns the same instance every time. Or a module that exports an instance directly (modules are singletons in JavaScript anyway)."What's the Liskov Substitution Principle?" Subtypes should be substitutable for their parent types without breaking client code. A function expecting
Animalshould work with anyDog,Cat, etc. Violating LSP usually signals that inheritance was the wrong choice."What's the difference between
class A extends BandObject.setPrototypeOf(A, B)?"extendsdoes multiple things in one syntax: sets up the prototype chain, sets up the constructor chain (for static method inheritance), and handlessupersemantics.setPrototypeOfonly handles one piece."Can you explain
thisin arrow functions vs regular functions?" Regular functions getthisfrom how they're called (the four rules). Arrow functions getthisfrom where they're defined (lexical scoping). This is why arrow functions are good for callbacks but bad for object methods.
Interactive animation: Inheritance without class - call the parent constructor, wire the chain with Object.create, then repair the constructor pointer.
The Founder Moment
Brandon here. The OO design question that taught me the most came up early in my Stripe interviews — the loop that eventually got me to Amazon. The interviewer asked me to design a payment processing system. I started with inheritance: a base
Paymentclass, thenCreditCardPayment,ACHPayment,WirePaymentas subclasses.About fifteen minutes in, he asked: "What if we need a refund? Is that a Payment?" I said yes, made it a subclass. "What about a chargeback?" Subclass. "What about a payment that's also a transfer between accounts?" I started to see the problem. The inheritance tree was branching uncontrollably, and each new requirement broke the abstraction.
He gently suggested composition. I redesigned with
Paymentas a class that had aPaymentMethod, had aTransactionDirection, had aSettlement. Each piece was its own concept. Refunds and chargebacks composed differently than charges. Suddenly the design accommodated everything without growing taller.The interviewer's note in the feedback (I got the offer): "Recovered well from initial over-reliance on inheritance. This is the most common OO mistake. Recognizing it and pivoting is the senior skill."
What I took from that: every senior OO interview is partly testing whether you'll over-rely on inheritance. The first instinct in OO design — "this is similar to that, so subclass it" — is the instinct senior engineers learn to resist. Composition first. Inheritance only when the model genuinely demands it. That's the principle that's served me through fifteen years of design reviews at Amazon.