Skip to main content

Checking a class instance

instanceof is the main way to check that an object is an instance of a class: the operator looks for Class.prototype in the object's prototype chain. That is exactly why it works with inheritance and with built-in classes, and why its limits come down to cases where the prototype chain was changed by hand or the object came from another JavaScript context.

Theory

TL;DR

  • obj instanceof Class returns true if Class.prototype appears in the prototype chain of obj.
  • The operator walks up the chain, so it supports inheritance: dog instanceof Animal is true.
  • obj.constructor === Class checks the constructor, but breaks if prototype.constructor was reassigned.
  • Object.getPrototypeOf(obj) === Class.prototype only works for a direct instance and ignores inheritance.
  • With built-in classes (Array, Date, Error, Map) instanceof behaves the same way, because they are built on prototypes too.
  • Across contexts (iframe, Worker) the constructors differ, so instanceof may return false; for arrays use Array.isArray().

Quick example

javascript
class User {} const u = new User(); console.log(u instanceof User); // true

How instanceof works under the hood

The expression obj instanceof Class is roughly equivalent to this code:

javascript
let proto = Object.getPrototypeOf(obj); while (proto) { if (proto === Class.prototype) return true; proto = Object.getPrototypeOf(proto); } return false;

So instanceof simply checks for the presence of Class.prototype in the __proto__ chain. There is no "type magic" here: it is an ordinary walk over a linked list of prototypes.

Inheritance and the prototype chain

Because the operator walks up the chain, it sees every ancestor of the class:

javascript
class Animal {} class Dog extends Animal {} const dog = new Dog(); console.log(dog instanceof Dog); // true console.log(dog instanceof Animal); // true, inheritance works console.log(dog instanceof Object); // true, every object inherits from Object

Deeper inheritance behaves the same way:

javascript
class A {} class B extends A {} class C extends B {} const c = new C(); console.log(c instanceof A); // true console.log(c instanceof B); // true console.log(c instanceof C); // true

The prototype chain here looks like this:

javascript
c -> C.prototype -> B.prototype -> A.prototype -> Object.prototype -> null

instanceof climbs this chain step by step until it finds a match or reaches null.

Alternatives: constructor, getPrototypeOf and a static method

You can check whether the object's constructor matches the class:

javascript
class User {} const u = new User(); console.log(u.constructor === User); // true

But this approach breaks in two cases: if the object was created through Object.create() with a different prototype, or if someone changed prototype.constructor. Here is an example where both checks still agree, because the prototype chain is intact:

javascript
const fake = Object.create(User.prototype); console.log(fake.constructor === User); // true console.log(fake instanceof User); // true

Both approaches work as long as the prototype chain is not broken.

The third option is a direct prototype comparison:

javascript
class User {} const u = new User(); console.log(Object.getPrototypeOf(u) === User.prototype); // true

It works only for a direct instance: if inheritance is involved, the method will not climb further up the chain, unlike instanceof.

In large codebases the check is often moved into a static method on the class itself, so the logic lives in one place:

javascript
class User { static isUser(obj) { return obj instanceof User; } } const u = new User(); console.log(User.isUser(u)); // true

Built-in classes and different contexts

With built-in classes the operator behaves exactly the same, since they are built on prototypes too:

javascript
console.log([] instanceof Array); // true console.log([] instanceof Object); // true console.log(new Date() instanceof Date); // true console.log(new Date() instanceof Object); // true

The problem shows up when the object was created in another "reality", for example in another iframe or in a Worker. Those have their own globals, so Array or Object is a different constructor and the check may return false:

javascript
// arr came from another iframe console.log(arr instanceof window.Array); // false

The fix for arrays is the built-in check that does not depend on the context:

javascript
Array.isArray(arr); // safe even across contexts

Summary table

ApproachExampleWhat it checksSupports inheritanceNote
instanceofobj instanceof ClassThe __proto__ chainYesReliable default
constructorobj.constructor === ClassThe object's constructorPartlyBreaks with a modified prototype
Object.getPrototypeOfObject.getPrototypeOf(obj) === Class.prototypeThe direct prototypeNoOnly for the exact class
Static methodClass.isInstance(obj)Any logic you wantYesConvenient in large projects

Common mistakes

  • Treating instanceof as a type check. It is a prototype chain check, not a check of a value's type. For primitives it always returns false: 'text' instanceof String is false.
  • Using instanceof Array for data from a foreign context. Constructors differ between an iframe and a Worker, so Array.isArray() is required there.
  • Relying on obj.constructor when the class may have reassigned prototype. After a manual Class.prototype = {} the constructor property is lost and the check gives a wrong result.
  • Confusing Object.getPrototypeOf(obj) === Class.prototype with instanceof. The former does not see ancestors, so an instance of a subclass fails that check.
  • Forgetting that instanceof does not work with null and undefined. Such values have no prototype, so the result is always false.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.