After writing `Child.prototype = Object.create(Parent.prototype)`, what does `new Child().constructor` return, and how do you repair it correctly?
answer
- the replacement object starts empty
- lookup keeps walking upward
- not the same as breaking instanceof
- assignment changes an attribute you did not intend
- defineProperty defaults to false
basics
~10 sIt returns Parent. Replacing the prototype object throws away the constructor that pointed at Child, so lookup continues up the chain. Repair it with Object.defineProperty, which keeps the property non-enumerable, rather than plain assignment.
solid answer
~40 sAssigning a brand-new object to `Child.prototype` discards the auto-created prototype object and the `constructor` property it carried. The replacement made by `Object.create(Parent.prototype)` has no own `constructor`, so `new Child().constructor` walks up to `Parent.prototype.constructor` and yields `Parent`. If you had used a plain object literal instead, you would get `Object`. The fix is to put the back-reference back, but not with `Child.prototype.constructor = Child` — assignment creates an *enumerable* data property, so `constructor` then shows up in every `for...in` over an instance. Use `Object.defineProperty(Child.prototype, 'constructor', { value: Child, writable: true, configurable: true })`, which defaults `enumerable` to `false` and reproduces the original descriptor. Note that `instanceof` is unaffected either way, and that `class Child extends Parent` handles all of this for you.
code
javascript · 18 linesfunction Parent() {}
function Child() { Parent.call(this); }
Child.prototype = Object.create(Parent.prototype);
console.log(Object.getOwnPropertyNames(Child.prototype)); // []
console.log(new Child().constructor === Parent); // true — link lost
console.log(new Child() instanceof Child); // true — unaffected
Object.defineProperty(Child.prototype, 'constructor', {
value: Child,
writable: true,
configurable: true,
enumerable: false
});
console.log(new Child().constructor === Child); // true
for (const key in new Child()) console.log(key); // logs nothinggo deeper
Recognise the symptom: after replacing a function's prototype object, instances report the wrong constructor, because the replacement object never had one.
Explain where the lookup lands and why, and restore the link with Object.defineProperty so the property stays non-enumerable as the engine originally made it.
Point at the real damage — clone helpers, factories and logging that read obj.constructor — and note that instanceof is untouched, so this class of bug hides from type-shaped tests.
Decide whether hand-rolled prototypal inheritance belongs in the codebase at all, and what you standardise on so metadata like this cannot drift silently between modules.
## What the reassignment actually destroys When `function Child() {}` is evaluated, the engine creates an object for `Child.prototype` and puts one property on it: `constructor`, pointing back at `Child`, with the descriptor `{ writable: true, enumerable: false, configurable: true }`. The hand-rolled inheritance idiom then throws that object away: ```js function Parent() {} function Child() { Parent.call(this); } Child.prototype = Object.create(Parent.prototype); // original object discarded ``` The replacement is a fresh object with **no own properties at all** — `Object.create` only sets the internal prototype. So the back-reference is gone, and reading `constructor` on an instance keeps walking: ```js const c = new Child(); c.constructor === Parent; // true Object.getOwnPropertyNames(Child.prototype); // [] ``` `Parent` is where the search lands because `Child.prototype` inherits from `Parent.prototype`, which still carries its own `constructor`. If you had written `Child.prototype = { greet() {} }`, the chain would go to `Object.prototype` instead and `c.constructor` would be `Object`. ## Why the naive repair is subtly wrong The repair everyone writes first is: ```js Child.prototype.constructor = Child; // works, but enumerable ``` This does restore the value, and for most code that is enough. But `=` on a property that does not exist yet creates a data property with all three attributes `true`, including `enumerable`. The original was non-enumerable, and the difference is observable: ```js for (const key in new Child()) console.log(key); // now logs "constructor" ``` `for...in` walks the prototype chain and visits enumerable string keys, so every such loop over an instance — including old-style shallow-copy helpers built on `for...in` — now sees `constructor`. That can leak the function into serialisation helpers, copy loops, and diffing code that was written assuming instances only expose their own data. The faithful repair defines the property explicitly: ```js Object.defineProperty(Child.prototype, 'constructor', { value: Child, writable: true, configurable: true, enumerable: false }); ``` `Object.defineProperty` defaults every omitted boolean attribute to `false`, so leaving `enumerable` out has the same effect; stating it is just clearer. ## What is and is not affected It matters to be precise about the blast radius, because candidates routinely overstate it. **Not affected: `instanceof`.** The operator never reads `constructor`. It compares `Child.prototype` against the instance's prototype chain, and the reassignment happened *before* any instance existed, so the chain is correct: ```js new Child() instanceof Child; // true, with or without the repair new Child() instanceof Parent; // true ``` **Not affected: method lookup.** Methods you add to the new prototype object work normally. **Affected: anything that reads `constructor`.** That includes code doing `obj.constructor === Child`, generic clone helpers written as `new obj.constructor()`, logging and debugging output that prints the constructor name, and `obj.constructor.name` in error messages. With the link broken, a clone helper silently produces a `Parent` instead of a `Child` — a bug that survives tests written against the base type. ```js function clone(obj) { return Object.assign(new obj.constructor(), obj); } clone(new Child()) instanceof Child; // false without the repair ``` ## Two related mistakes in the same idiom While you are looking at this code, two neighbouring errors usually appear with it. The first is `Child.prototype = Parent.prototype`. This does not create a level of indirection at all — the two functions now share one object, so any method you add for `Child` is immediately visible on every `Parent` instance, and `new Parent() instanceof Child` becomes `true`. `Object.create(Parent.prototype)` exists precisely to insert a fresh, separate object in between. The second is `Child.prototype = new Parent()`. This was the pre-ES5 workaround; it runs the parent's body for its side effects, at a time when no real instance exists, and leaves the parent's own instance fields sitting on the shared prototype where every child sees the same copy. ## The modern answer `class Child extends Parent {}` produces the correct wiring without any of this: the prototype object it creates carries a proper non-enumerable `constructor`, and its internal prototype is set to `Parent.prototype`. The manual idiom is worth understanding because it still appears in older codebases, in transpiled output, and in interview questions — but new code should not be writing it, and the honest closing sentence in an interview is exactly that.
- Does losing the `constructor` link also break `instanceof` for those instances?No. `instanceof` compares `Child.prototype` against the instance's prototype chain and never reads `constructor`. Since the reassignment happens before any instance is created, every instance links to the new prototype object and both `instanceof Child` and `instanceof Parent` are `true`. The two mechanisms are independent, which is why only `constructor`-reading code misbehaves.
- What would `new Child().constructor` return if the prototype had been replaced with a plain object literal instead?`Object`. The literal has no own `constructor` and inherits from `Object.prototype`, so the lookup terminates there and finds `Object.prototype.constructor`. With `Object.create(Parent.prototype)` the search stops one level earlier and yields `Parent` instead — the answer just reports wherever the chain first supplies the property.
- Why prefer `Object.defineProperty` over `Child.prototype.constructor = Child`?Assignment creates the property with `enumerable: true`, so `for...in` over any instance now yields `constructor`, which the original never did. `Object.defineProperty` defaults omitted attributes to `false`, restoring the exact descriptor the engine created: writable, configurable, non-enumerable.
saying these in an interview costs you the question
- Claiming the reassignment also breaks instanceof
- Saying constructor is restored automatically by the engine
- Using Child.prototype = Parent.prototype to inherit
- Assuming plain assignment reproduces the original descriptor
- Thinking class extends still needs a manual constructor fix