skip to content

An array built inside an iframe is handed to the parent page, where value instanceof Array evaluates to false even though the value genuinely is an array. Why does instanceof fail here, and what check works instead?

level: middleimportance: must knowfreq 58%

answer

  1. it is a prototype-chain walk
  2. identity comparison, not a name comparison
  3. each frame has its own built-ins
  4. a dedicated predicate reads the value itself
  5. the brand travels, the prototype does not

basics

~20 s

Each iframe is a separate realm with its own Array constructor and Array.prototype, so a foreign array's prototype chain never reaches the parent's Array.prototype and instanceof is false. Array.isArray reads the internal array brand and works across realms.

solid answer

~50 s

`instanceof` is a prototype-chain test: `v instanceof Array` walks `v`'s chain looking for the exact object that `Array.prototype` currently points at in *this* realm. An iframe is a separate realm with a completely separate set of intrinsics — its own `Array`, its own `Array.prototype`. The foreign array's chain ends at the *iframe's* `Array.prototype`, which is a different object, so the walk fails and the test is false even though the value is a real array. The fix is `Array.isArray(value)`, which ignores prototypes entirely and asks whether the value is an array exotic object — a property of the value's internal representation, identical in every realm. The same trap applies to `Date`, `RegExp`, `Map` and `Error`; for those there is no `isX` helper, so a brand read via `Object.prototype.toString.call` or duck typing is the cross-realm option.

code

javascript · 9 lines
javascript
const fake = Object.create(Array.prototype);
console.log(fake instanceof Array); // true — chain says yes
console.log(Array.isArray(fake));   // false — not really an array

const proxied = new Proxy([], {});
console.log(proxied instanceof Array); // true
console.log(Array.isArray(proxied));   // true — isArray follows the proxy target

console.log(Array.isArray(Array.prototype)); // true — it is itself an array

go deeper

for a junior

Know the rule of thumb: use Array.isArray to test for an array, never instanceof Array and never a length check. Be able to say that iframes have their own copies of the built-in constructors.

for a middle

Explain the mechanism: instanceof walks the prototype chain comparing against Constructor.prototype by object identity, and a separate realm has a separate Array.prototype object. Contrast that with isArray reading the value's own internal array brand.

for a senior

Bring the production angle: name the boundaries that create realms versus those that do not, show that a duplicated library copy reproduces the same bug inside one realm, and describe the check you would standardise for values arriving from untrusted or foreign code.

for a principal

Own the tradeoff between nominal checks and brand checks at an architecture boundary — when to eliminate the problem by deduplicating and owning a single instance of a module, versus when to design types that carry a portable brand because multiple realms are unavoidable.

## What instanceof actually does `v instanceof C` is not a type check in the nominal sense. Unless `C` supplies its own `Symbol.hasInstance` method, the operator does exactly this: read `C.prototype`, then walk `v`'s prototype chain, and return true if any link is that *same object* — compared by identity, the way `===` compares. If `C` is not callable, it throws a TypeError. If `v` is a primitive, it is simply false. So `instanceof` really answers one narrow question: "does this particular object appear on that value's chain right now?" Everything that goes wrong with it follows from that. ## Realms A *realm* is a complete, isolated set of the language's built-in objects — its own global object, and its own `Object`, `Array`, `Function`, `Error`, `Promise`, each with its own `.prototype`. Two realms' `Array.prototype` objects behave identically but are not the same object. An array created inside an iframe was created with that document's `Array`, so its chain is `theArray -> iframeWindow.Array.prototype -> iframeWindow.Object.prototype -> null`. Evaluate `value instanceof Array` in the parent and you are comparing against `parentWindow.Array.prototype`, which is nowhere on that chain. False. ```js // inside the parent page, with an <iframe> already loaded const frame = document.querySelector("iframe"); const foreign = new frame.contentWindow.Array(1, 2, 3); foreign instanceof Array; // false Array.isArray(foreign); // true ``` Other realm boundaries exist beyond iframes — for example, Node's `vm` module evaluates code in a fresh realm. What is *not* a realm boundary is worth knowing too: a structured-clone transfer, such as a worker `postMessage`, deserialises the data into the receiving realm, so the object you get on the other side is built from the local intrinsics and `instanceof` works normally on it. ## Why Array.isArray is different `Array.isArray` does not look at prototypes at all. Arrays are *exotic objects*: they have a special internal representation with the length-tracking behaviour built into the object itself, and `isArray` asks whether the argument is one. That property travels with the value, not with any realm's intrinsics, so it is correct across every realm. Two useful corollaries: ```js const fake = Object.create(Array.prototype); fake instanceof Array; // true — chain says yes Array.isArray(fake); // false — not an array exotic object Array.isArray(new Proxy([], {})); // true — isArray sees through the proxy to its target ``` The first shows `instanceof` being fooled by a hand-built chain; the second shows `isArray` deliberately following a proxy to its target rather than reporting on the proxy itself. ## The other things instanceof breaks on Realms are only the most famous failure. `instanceof` is also false when: - **`Object.setPrototypeOf` or `__proto__` rewrote the chain.** The relationship is mutable at runtime, so the answer can change during a value's lifetime. - **The prototype chain was severed**, e.g. `Object.create(null)` — a dictionary object is not `instanceof Object`. - **Two copies of the same library are loaded.** Each copy defines its own class object with its own `.prototype`, so instances of one are not `instanceof` the other, all in a single realm. And it can be false-*positive*: any object whose chain you can reach can be made to pass, and `Symbol.hasInstance` lets a class define the answer outright. ## What to reach for - Arrays: `Array.isArray`. Unambiguous, cross-realm, spec-blessed. - Other built-ins (Date, RegExp, Map, Set, Promise, Error): `Object.prototype.toString.call(value)` reads a brand that is likewise realm-independent, giving `"[object Date]"` and friends — with the caveat that it can be spoofed by a value that sets its own string tag. - Your own types crossing a boundary: a shared token, such as a property keyed by a global registry symbol, rather than class identity. - Everything staying inside one realm and one copy of one module: `instanceof` is fine, and is still the most readable check. Its weaknesses are boundary weaknesses. The interview point is not "never use instanceof". It is knowing that it tests object identity of a prototype, and therefore knowing precisely which boundaries invalidate it.

  • Does the same failure happen to values a Web Worker sends back via postMessage?
    No. postMessage transfers data through the structured-clone algorithm, and the receiving side deserialises it into its *own* realm using its own intrinsics. The array you receive has the local `Array.prototype` on its chain, so `instanceof Array` is true. The realm trap needs a live reference to an object built by another realm — an iframe's `contentWindow`, or a separate vm context.
  • Array.isArray is cross-realm safe. What do you do for a Date or a Map coming from another realm?
    There is no `Date.isDate`. `Object.prototype.toString.call(value)` returns `"[object Date]"` or `"[object Map]"` by reading an internal brand that is realm-independent, so it is the usual answer. It is spoofable by a value that defines its own `Symbol.toStringTag`, so for hostile input a stricter option is to call an unforgeable method through a borrowed getter and catch the TypeError.
  • Can instanceof also return true for something that is not really an instance?
    Yes, in two ways. `Object.create(Array.prototype)` produces an object whose chain contains `Array.prototype` while it is not an array at all, so the walk succeeds. And a class can define `static [Symbol.hasInstance]`, replacing the chain walk with arbitrary logic. `instanceof` reports a relationship you can construct, not a fact about how the value was made.

saying these in an interview costs you the question

  • Thinks cross-frame values are converted to plain objects
  • Says instanceof compares constructor names
  • Claims Array.isArray just checks for a length property
  • Believes instanceof cannot be fooled by a handmade prototype
  • Treats a worker message as a cross-realm instanceof failure

context