Skip to content

typeof vs instanceof - two ways to check a type in JS

JavaScript has two core operators for checking what type a value is, and each answers a different question. typeof returns one of 8 strings and has known gotchas (null, arrays). instanceof walks the prototype chain and fails for objects from an embedded <iframe>. A note on when to use which, and why arrays have a dedicated Array.isArray.

A classic question: how do you check if a variable is an array?

const arr = [1, 2, 3];

typeof arr; // "object" - not "array"
arr instanceof Array; // true - but not always
Array.isArray(arr); // true - the dedicated method for arrays

Why the discrepancy? Because typeof and instanceof answer different questions. typeof tells you the broad category (primitive vs object vs function). instanceof walks the prototype chain. If you know which one you’re using and why, most “type-checking gotchas” stop existing.

#typeof - eight possible results

typeof x is an operator that returns a string describing the type of the value. The spec defines exactly 8 possible returns:

ValueResult
undefined"undefined"
true, false"boolean"
42, NaN, Infinity"number"
42n"bigint"
"hello""string"
Symbol()"symbol"
function (() => {})"function"
anything else (objects)"object"

Three pitfalls worth remembering:

typeof null; // "object" (!) - historical bug, never fixed
typeof []; // "object" - an array is a special kind of object
typeof NaN; // "number" - NaN formally belongs to the Number type

typeof null === "object" has been like that since the first version of JavaScript and won’t be fixed for backward compatibility. If you want to distinguish null from another object, check x === null separately. For arrays use Array.isArray(x), for NaN use Number.isNaN(x) (see the number quirks note).

typeof doesn’t throw even for undeclared variables - it returns "undefined". It’s the only operator in JS that accepts an argument that doesn’t exist. Exception: for let/const used before declaration (the temporal dead zone), typeof throws ReferenceError.

#instanceof - walks the prototype chain

x instanceof Constructor returns true if Constructor.prototype is anywhere in the prototype chain of x. The engine walks up the chain and checks each level (see the prototype chain note):

class Animal {}
class Dog extends Animal {}

const rex = new Dog();
rex instanceof Dog; // true (Dog.prototype is direct)
rex instanceof Animal; // true (Animal.prototype is further up)
rex instanceof Object; // true (Object.prototype at the end of the chain)

In practice you reach for instanceof mainly to check instances of your own classes - inheritance works cleanly there.

Under the hood, the syntax goes through Symbol.hasInstance - a class can override that mechanism and return whatever it wants. You rarely touch it in day-to-day code, but it’s worth knowing that the default prototype-chain walk is just the default behavior, not a hard rule of the language.

The most important pitfall: instanceof doesn’t work well when the object comes from a page other than the one doing the check - e.g. from a page embedded via an <iframe> tag. Each <iframe> has its own independent copy of Array, Date and so on, so an array from an iframe has a different Array.prototype than an array on the main page - and instanceof Array returns false:

const arrFromFrame = window.frames[0].Array.from([1, 2, 3]);

arrFromFrame instanceof Array; // false (!) - the iframe's array has a different Array
Array.isArray(arrFromFrame); // true - works regardless

That’s why you should check arrays with Array.isArray, not instanceof Array. Array.isArray is defined to work regardless of where the array came from.

The other thing: instanceof returns false for primitives ("abc" instanceof String is false), because primitives don’t have a prototype chain. For primitives you use typeof.

#When to use which

In practice it comes down to two rules:

  1. Checking a primitive (string, number, boolean, undefined, symbol, bigint) → typeof.
  2. Checking a specific kind of object → a dedicated method if one exists (Array.isArray, Number.isNaN, Number.isInteger), otherwise instanceof.

Plus two exceptions:

  • Check null separately via x === null (since typeof null is "object").
  • When an object might come from an embedded <iframe>, use a dedicated method or Object.prototype.toString.call(x) - it returns a string like "[object Array]" and works when instanceof fails.

#What to take away

  1. typeof returns one of 8 strings - 6 primitives ("undefined", "boolean", "number", "bigint", "string", "symbol"), "function" for functions, plus "object" for everything else. Remember typeof null === "object" and typeof [] === "object".
  2. instanceof walks the prototype chain. Doesn’t work for primitives or for objects coming from an embedded <iframe>. For arrays use Array.isArray, not instanceof Array.

In day-to-day code you’ll rarely need anything other than typeof and dedicated methods (Array.isArray, Number.isNaN). But when a type check gives an unexpected result, you now have a map: each of the two operators answers a different question.

← Back to notes