How does the JavaScript `instanceof` operator decide its answer, and what exactly does it compare?
answer
- not a check on constructor
- identity comparison along a chain
- the right-hand side is read live
- primitives answer false, never throw
- bound functions defer to their target
basics
~20 sx instanceof F reads the current value of F.prototype and walks the prototype chain of x, returning true if that exact object appears anywhere in the chain. It compares object identity along a chain, not names, not x.constructor.
solid answer
~40 s`x instanceof F` is a two-step operation. First it looks for a `Symbol.hasInstance` method on `F`; every ordinary function inherits one from `Function.prototype`, and that default implements the classic behaviour. The default reads `F.prototype` at the moment of evaluation, then walks `x`'s internal prototype chain — the links you would follow with repeated `Object.getPrototypeOf` — and returns `true` as soon as one link is the very same object as `F.prototype`. Three consequences follow. If `x` is a primitive it returns `false` immediately, never throws. If `F` is not callable, or `F.prototype` is not an object, it throws a `TypeError`. And because `F.prototype` is read live, reassigning it after objects were built makes those objects stop being instances. For a bound function it defers to the original target function.
code
javascript · 16 linesfunction A() {}
const a = new A();
console.log(a instanceof A); // true
console.log('abc' instanceof String); // false — primitives are never instances
const BoundA = A.bind(null);
console.log(BoundA.prototype); // undefined
console.log(a instanceof BoundA); // true — checked against A.prototype
A.prototype = {}; // fresh prototype object
console.log(a instanceof A); // false — a still links to the old one
console.log(new A() instanceof A); // true — new objects link to the new one
const fake = Object.create(A.prototype);
console.log(fake instanceof A); // true — never went through new A()go deeper
Know the syntax and the everyday meaning: x instanceof Dog is true for objects that inherit from Dog.prototype, including subclass instances, and false for primitives.
Walk the algorithm out loud: read F.prototype now, follow x's prototype chain, compare by identity, stop at null. Name the TypeError cases on the right-hand side.
Show where the live read bites in real code — a library that reassigns a prototype, or objects that pass the check without ever being constructed — and pick checks that do not depend on mutable state.
Argue when structural chain checks belong in an API contract at all, versus explicit capability tags or discriminated data, given that any consumer can synthesise an object that passes.
## The operator is a chain search, not a label check The most common wrong mental model is that `instanceof` asks "was this object made by this function?" It does not. It asks a purely structural question: *is the object currently referenced by `F.prototype` somewhere in `x`'s prototype chain?* Both halves of that sentence matter — it reads `F.prototype` now, and it compares by object identity, not by name or shape. ```js function A() {} const a = new A(); let p = Object.getPrototypeOf(a); while (p !== null) { if (p === A.prototype) console.log('found it'); p = Object.getPrototypeOf(p); } ``` That loop is essentially the whole operator. ## The spec path Evaluating `x instanceof F` runs roughly these steps: 1. If `F` is not an object, throw a `TypeError`. 2. Look up `F[Symbol.hasInstance]`. If it is not `undefined` or `null`, call it with `x` and coerce the result to a boolean. Ordinary functions inherit `Function.prototype[Symbol.hasInstance]`, so this branch is almost always taken and simply performs step 3. 3. Otherwise (a callable without that method), run the ordinary algorithm: if `F` is not callable, throw a `TypeError`; if `F` is a bound function, redo the check against its bound target function; read `F.prototype`; if that is not an object, throw a `TypeError`; then walk `x`'s prototype chain comparing each link with `===` against `F.prototype`; return `false` at the end of the chain. Before any of that, if `x` is not an object the ordinary algorithm returns `false`. So `'abc' instanceof String` is `false`, and `42 instanceof Number` is `false`, without an error. A primitive string is not a `String` object, and no autoboxing happens for this operator. ## What throws and what does not The error cases are worth memorising because they surprise people: ```js 'abc' instanceof String; // false — primitives are simply not instances ({}) instanceof 42; // TypeError: right-hand side is not an object ({}) instanceof {}; // TypeError: right-hand side is not callable function F() {} F.prototype = 5; // legal for an ordinary function ({}) instanceof F; // TypeError: F.prototype is not an object ``` A `TypeError` from `instanceof` therefore always points at the right-hand side, never at the left. ## Live reads of F.prototype Because the operator reads `F.prototype` when it runs, the relationship between an object and a function is not frozen at construction: ```js function A() {} const a = new A(); a instanceof A; // true A.prototype = {}; // brand-new object a instanceof A; // false — a still links to the OLD prototype object const b = new A(); b instanceof A; // true — b links to the new one a instanceof A; // still false ``` That pair of objects, both produced by calling `new A()`, now disagree about whether they are instances of `A`. This is the crispest demonstration that the operator is about the current chain, not about provenance. The same mechanism runs the other way: since `Object.setPrototypeOf` can splice any object into any chain, an object never touched by `A` can be made to pass: ```js const fake = Object.create(A.prototype); fake instanceof A; // true — never went through new A() ``` ## Inheritance chains and built-ins Because the search walks the entire chain, `instanceof` is naturally polymorphic: ```js class Animal {} class Dog extends Animal {} const d = new Dog(); d instanceof Dog; // true d instanceof Animal; // true — Animal.prototype is further up the chain d instanceof Object; // true — Object.prototype terminates most chains ``` An object created with `Object.create(null)` has an empty chain, so `obj instanceof Object` is `false` — a useful reminder that "everything is an object" is a simplification. ## Bound functions `f.bind(...)` produces a function with no own `prototype` property. If `instanceof` naively read `Bound.prototype` it would find `undefined` and throw. Instead the algorithm unwraps the binding and checks against the target function's prototype: ```js function A() {} const a = new A(); const BoundA = A.bind(null); BoundA.prototype; // undefined a instanceof BoundA; // true — checked against A.prototype ``` ## Practical reading Use `instanceof` when you control both the objects and the constructor in the same program and the same realm — checking your own error subclasses in a `catch` block is the classic good use. Be aware of its two structural weaknesses: the answer depends on a mutable `F.prototype`, and identity comparison means two structurally identical functions from different sources never recognise each other's objects. Neither is a bug; both fall directly out of "walk the chain and compare by identity".
- Can an object pass `instanceof F` without ever having been created by `F`?Yes. `Object.create(F.prototype)` produces an object whose chain contains `F.prototype`, so it passes, even though `F` never ran. `Object.setPrototypeOf` can do the same to an existing object. The operator inspects the current chain, so any way of putting `F.prototype` into that chain is enough.
- Why does `instanceof` throw a `TypeError` in some cases but simply return `false` for a primitive on the left?The error cases are all about the right-hand side: it must be an object, it must be callable, and its `prototype` must itself be an object, or there is nothing coherent to compare against. A primitive on the left is a well-defined question with a well-defined answer — it has no prototype chain of its own to search, so the result is `false`.
- How does `instanceof` behave for a class hierarchy several levels deep, and what does that cost?It returns `true` for every ancestor whose prototype is on the chain, because the search continues upward until it hits `null`. The cost is a walk proportional to chain depth, which is negligible in practice; the real cost is design-level, since a deep chain means a lot of code implicitly answers `true` to broad type questions.
instanceof is a question about ancestry, not about a birth certificate: it checks whether a particular ancestor appears in the object's family line right now, not who was present when the object was made.
saying these in an interview costs you the question
- Saying instanceof compares obj.constructor to the function
- Claiming instanceof throws when the left side is a primitive
- Believing the relationship is fixed at construction time
- Thinking instanceof compares function names or shapes
- Assuming any value is legal on the right-hand side