Object.assign
intermediateCopy enumerable own properties across sources, with getter evaluation and null-target errors.
Implement instanceof in JavaScript by walking the prototype chain, understand Symbol.hasInstance customization, and know the cross-realm case where instanceof lies.
Implement
myInstanceof(value, Constructor)without usinginstanceof.
The mechanism is one loop: walk value's prototype chain and look for Constructor.prototype. The question is a five-minute check of prototypal inheritance fluency — and then the interview actually happens in the follow-ups: primitives, Symbol.hasInstance, and the cross-realm failure that motivated Array.isArray.
function myInstanceof(value, Constructor) {
if (typeof Constructor !== "function") {
throw new TypeError("Right-hand side of 'instanceof' is not callable");
}
// Primitives are NEVER instances — no boxing happens here
if (value === null || (typeof value !== "object" && typeof value !== "function")) {
return false;
}
const target = Constructor.prototype;
let proto = Object.getPrototypeOf(value);
while (proto !== null) {
if (proto === target) return true; // found the link
proto = Object.getPrototypeOf(proto); // climb one level
}
return false; // chain ended at null — not an instance
}class Animal {}
class Dog extends Animal {}
const rex = new Dog();
myInstanceof(rex, Dog); // true — first hop
myInstanceof(rex, Animal); // true — second hop: the chain WALK matters
myInstanceof(rex, Object); // true — everything plain ends at Object.prototype
myInstanceof([], Array); // true
myInstanceof([], Object); // true — arrays inherit from Object too
myInstanceof("hi", String); // false — primitives never match…
myInstanceof(new String("hi"), String); // true — …but boxed objects do
myInstanceof(null, Object); // false — null is nobody's instance
myInstanceof(Object.create(null), Object); // false — null-proto chain, zero hopsThe "hi" vs new String("hi") pair is the classic probe: instanceof does no coercion — a string primitive has no prototype chain to walk, even though property access on it temporarily boxes. If you can articulate that asymmetry (reads box, instanceof doesn't), the fundamentals box is checked.
Also worth narrating: Object.getPrototypeOf, not __proto__ — the accessor is legacy (absent on Object.create(null) objects, deprecated in spec annex), while the function form always works.
Symbol.hasInstanceSince ES6, instanceof isn't hardwired to the chain walk — it first asks the right-hand side:
class Even {
static [Symbol.hasInstance](value) {
return typeof value === "number" && value % 2 === 0;
}
}
4 instanceof Even; // true (!) — no prototypes involved at all
"x" instanceof Even; // falseThe real algorithm: if Constructor[Symbol.hasInstance] is a function, call it and coerce to boolean; only otherwise do the prototype walk (the "ordinary" path your implementation reproduces). A spec-faithful myInstanceof adds three lines at the top:
const custom = Constructor[Symbol.hasInstance];
if (typeof custom === "function") {
return Boolean(custom.call(Constructor, value));
}Where this is real: zod-style schema checks, pattern-matching libraries, and — the platform's own use — instanceof working sensibly for Proxy-wrapped classes. It also means instanceof is spoofable, which matters for the security-adjacent follow-up: never gate trust decisions on it. (Same lesson as Symbol.toStringTag spoofing type tags — the language's introspection is cooperative, not authoritative.)
An array from an iframe, worker message, or vm context was built by a different Array constructor with a different Array.prototype:
frameArray instanceof Array; // false ✗ — different realm's prototype
Array.isArray(frameArray); // true ✓ — checks an internal slot insteadYour chain walk is correct and still gives the "wrong" answer — the chain genuinely doesn't contain your Array.prototype. This single case is why Array.isArray exists, why structured data crossing boundaries should be checked structurally (duck typing / schema validation) rather than by constructor identity, and why instanceof Error checks break across realms too (a real bug class in error-handling middleware). Delivering this unprompted is the difference between implementing the operator and understanding it.
rex instanceof Animal requires the loop, not a single comparison; interviewers test with a two-level class hierarchy.Function weirdness — Function instanceof Function is true (functions are objects whose chain includes Function.prototype); Object instanceof Function is also true. Fun chain-tracing warm-downs.Constructor.prototype after construction orphans old instances (old instanceof C → false); the operator checks current linkage, not construction history. This is the inverse view of what new wires up.x instanceof {} throws TypeError; bound functions and proxies as RHS work (and route through Symbol.hasInstance / target's prototype).Object.create(null) — zero-hop chain; false for everything, and any implementation using value.__proto__ instead of getPrototypeOf misses it silently.__proto__.myInstanceof("hi", String) returning true is wrong).Symbol.hasInstance exists — post-2015, the operator is dispatchable, and "implement instanceof" without it is half the algorithm.instanceof for array/plain-object detection in code review — Array.isArray and structural checks are the production answers.new operator to go with it." — the pairing: new builds the link, instanceof queries it.Array.isArray for arrays; Object.prototype.toString.call tags for built-ins (the getType story); structural/schema validation for your own shapes.x instanceof BoundFn do?" — bound functions delegate to the target function's prototype ([[BoundTargetFunction]]) — which is precisely why the bind polyfill's prototype linking matters.isPlainObject." — prototype is Object.prototype or null: const p = Object.getPrototypeOf(x); return p === Object.prototype || p === null — the utility every serializer needs, built from this question's parts.instanceof slow-ish in hot paths?" — potential chain walk + hasInstance lookup per check; engines cache aggressively, but monomorphic tag checks or discriminant fields (kind: "user") beat it in hot code — the discriminated-union pattern TypeScript rewards anyway.Copy enumerable own properties across sources, with getter evaluation and null-target errors.
Implement myNew(Constructor, ...args): prototype linking, this binding, and the return-object override rule.
The equality that fixes === for NaN and -0: tiny implementation, classic trivia follow-ups.
Implement bind including partial application and the new-operator edge case most candidates miss.