Prototypes & Inheritance
The prototype chain, __proto__ vs prototype, prototypal inheritance, extensibility, and Proxy.
10 questions
JuniorTheoryVery commonWhat is the prototype chain and how does property lookup use it?
What is the prototype chain and how does property lookup use it?
Every object has a hidden [[Prototype]] link to another object. When you read a property, the engine checks the object itself, then follows [[Prototype]] up the chain object by object until it finds the property or reaches null, where the search ends and the result is undefined. This is how inheritance and method sharing work in JavaScript.
Common mistakes
- ✗Thinking a missing property returns
undefinedwithout any chain traversal - ✗Believing properties are copied from the prototype rather than looked up live
- ✗Forgetting the chain terminates at
null, not atObject.prototypeitself
Follow-up questions
- →Where does the prototype chain of a plain object literal end?
- →How does writing a property differ from reading one along the chain?
MiddleTheoryVery commonHow does prototypal inheritance work with Object.create and setPrototypeOf?
How does prototypal inheritance work with Object.create and setPrototypeOf?
Objects inherit by linking to a prototype rather than copying. Object.create(parent) makes a new object whose [[Prototype]] is parent, while Object.setPrototypeOf(child, parent) rewires an existing object's link. A property missing on the child is then resolved by walking up to the parent, so the parent's methods are shared live, not duplicated, across all children.
Common mistakes
- ✗Thinking inheritance copies parent members into each child object
- ✗Confusing the direction — child links to parent, not the reverse
- ✗Believing
setPrototypeOfflattens keys instead of setting a live link
Follow-up questions
- →Why is
Object.setPrototypeOfdiscouraged for performance reasons? - →How does a child override a method inherited from its prototype?
JuniorTheoryCommonWhat does Object.freeze do, and why is it called shallow?
What does Object.freeze do, and why is it called shallow?
Object.freeze(obj) makes obj immutable: you cannot add, remove, or change its own properties, and it returns that same object, not a copy. It is shallow — only the top level is frozen, so a nested object stored as a property can still be mutated through its reference. In strict mode a write to a frozen property throws a TypeError.
Common mistakes
- ✗Assuming
freezeis deep and protects nested objects from mutation - ✗Thinking
freezereturns a new copy rather than the same object - ✗Believing a write to a frozen property always throws, even in non-strict mode
Follow-up questions
- →How would you make a deeply nested object fully immutable?
- →How does
Object.freezediffer fromObject.seal?
MiddleTheoryCommonWhat is the difference between __proto__ and .prototype?
What is the difference between __proto__ and .prototype?
__proto__ is an instance's link to its own prototype — the object used in lookup; it is deprecated, so prefer Object.getPrototypeOf. .prototype exists only on constructor functions: it is the object that new assigns as the new instance's [[Prototype]]. So (new Foo).__proto__ === Foo.prototype, and a plain instance has no .prototype of its own.
Common mistakes
- ✗Treating
__proto__and.prototypeas the same property - ✗Expecting an instance to have its own
.prototypeproperty - ✗Using
__proto__in code instead ofObject.getPrototypeOf
Follow-up questions
- →Why is reading
__proto__discouraged in favour ofObject.getPrototypeOf? - →What is the
[[Prototype]]ofFoo.prototypeitself?
MiddleTheoryOccasionalWhat is a Proxy and how do its handler traps work?
What is a Proxy and how do its handler traps work?
new Proxy(target, handler) wraps a target object so operations on the proxy run through the handler first. Each handler method is a trap — get, set, has, deleteProperty, and others — that intercepts the matching fundamental operation, letting you customise or validate it. A trap you omit falls through to the default behaviour on the target unchanged.
Common mistakes
- ✗Thinking a
Proxyclones the target instead of wrapping it live - ✗Believing an omitted trap blocks the operation rather than falling through
- ✗Assuming traps must enumerate property names instead of intercepting operations
Follow-up questions
- →What does the
ReflectAPI offer alongsideProxytraps? - →Name a real-world use case such as reactive state in Vue 3.
MiddleTheoryOccasionalHow do preventExtensions, seal, and freeze differ?
How do preventExtensions, seal, and freeze differ?
They form a ladder. preventExtensions only blocks adding new properties. seal does that and also marks existing ones non-configurable, so none can be deleted, but their values stay writable. freeze adds non-writable on top, so nothing can change at all. The matching tests are isExtensible, isSealed, and isFrozen.
Common mistakes
- ✗Treating
sealandfreezeas equivalent, ignoring writability of values - ✗Reversing the ladder — thinking
freezeis weaker thanseal - ✗Forgetting
sealkeeps values writable while blocking deletion
Follow-up questions
- →After
Object.seal, can you still reassign an existing property's value? - →Are all three operations shallow, and what does that imply for nested objects?
SeniorTheoryOccasionalWhat does new String('hi') produce, and why prefer string literals?
What does new String('hi') produce, and why prefer string literals?
new String('hi') returns a wrapper object — typeof gives object, not string — boxing the primitive in a String instance. Literals like 'hi' stay primitives, and when you call a method the engine boxes them in a short-lived temporary wrapper, reads through its prototype, then discards it. Explicit wrapper objects break ===, typeof, and truthiness, so literals are preferred.
Common mistakes
- ✗Thinking
new String('hi')yields a primitive withtypeofofstring - ✗Expecting
new String('hi') === 'hi'to betrue - ✗Believing primitives cannot call methods without a manually created wrapper
Follow-up questions
- →Why is a wrapper object like
new Boolean(false)always truthy? - →How does the temporary wrapper reach
String.prototypeto resolve a method?
MiddleTheoryRareHow do you emulate an enum in JavaScript, which lacks a native one?
How do you emulate an enum in JavaScript, which lacks a native one?
JavaScript has no enum keyword, so you build one from a frozen object: const Color = Object.freeze({ RED: 'red', GREEN: 'green' }). Freezing blocks reassignment and new members. Using Symbol('RED') as the values guarantees each member is unique and never equal to a stray string, at the cost of not serializing to JSON.
Common mistakes
- ✗Believing JavaScript has a built-in
enumkeyword like TypeScript or C - ✗Skipping
Object.freeze, leaving the enum members mutable at runtime - ✗Expecting two separate
Symbol('RED')calls to produce equal values
Follow-up questions
- →Why might
Symbol-valued enums complicate persistence and debugging? - →How does
Object.freezeinteract with nested objects inside the enum?
SeniorDebuggingRareWhy does the subclass override never run when the method is an arrow class field?
Why does the subclass override never run when the method is an arrow class field?
An arrow class field is an own instance property assigned in the constructor, not a prototype method. So investor.getPayment finds the base field first and the subclass's prototype method on Investor.prototype is shadowed — the override never runs.
Common mistakes
- ✗Thinking a prototype method overrides an own arrow class field of the same name
- ✗Believing class fields and prototype methods live in the same place on lookup
- ✗Mixing an arrow field in the base with a prototype method in the subclass
Follow-up questions
- →Where on the lookup chain does an own class field sit versus a prototype method?
- →What does the base arrow field gain that a plain prototype method loses when detached?
SeniorTheoryRareHow does new wire an instance's prototype, and which objects have none?
How does new wire an instance's prototype, and which objects have none?
new Foo() creates an object whose [[Prototype]] is set to Foo.prototype, which itself links up to Object.prototype, forming the lookup chain. The chain ends at an object with no prototype: Object.prototype, whose [[Prototype]] is null, and objects made with Object.create(null), which have no prototype and so do not inherit Object's methods.
Common mistakes
- ✗Thinking
newsets the instance's prototype to the constructor, not its.prototype - ✗Believing every object inherits from
Object.prototypewith no exceptions - ✗Assuming
Object.create(null)still resolvesObject's methods
Follow-up questions
- →Why might you use
Object.create(null)for a lookup map or dictionary? - →What is the
[[Prototype]]of a function, and how does that chain end?