Checking Types in Javascript

阅读更多

Have you ever wondered: what is the correct way to check if a Javascript variable is an Array?

 

Do a Google search and you will get a great variety of answers. And, unfortunately, there's is no correct answer. This is one of the sad things about Javascript, not only are there many varying implementations of the language, there are also many varying opinions about how things should be done.

Enough philosophizing and feeling sorry about the state of things. With this article, I will trying to give a comprehensive overview of the different techniques of checking-types in Javascript, the pros and cons of each and why they exist.

typeof operator

In the beginning, there was typeof. This handy operator gives you the "type" of a Javascript value:

typeof 3 // "number"
typeof "abc" // "string"
typeof {} // "object"
typeof true // "boolean"
typeof undefined // "undefined"
typeof function(){} // "function"

 All is fine 'n dandy until

typeof [] // "object"

Huh? An array's type is object? I guess it is, if you want to get technical about it, but still, what the...

typeof null // "object"

Okay, now that's just wrong!

 

Also, typeof will return "object" for Dates, RegExp, user defined objects, DOM elements, and pretty much anything else. So, typeof is pretty good at distinguishing between different kind of primitive values, and distinguish between them and objects, but is completely useless when it comes to distinguishing between different kinds of objects - which includes arrays and nulls(WTF?!).

instanceof operator

The instanceof operator tells you whether a object is an instance of a certain type. The so-called "type" is a constructor. For example

function Animal(){}
var a = new Animal()
a instanceof Animal // true

Alternatively, you could make this check using the constructor property of an object

a.constructor === Animal // true

However, the constructor check has two problems. First, it does not walk up the prototype chain

function Cat(){}
Cat.prototype = new Animal
Cat.prototype.constructor = Cat
var felix = new Cat
felix instanceof Cat // true
felix instanceof Animal // true
felix.constructor === Cat // true
felix.constructor === Animal // false

The second problem is that it will bomb out if the object in question is null or undefined.

felix = null
felix instanceof Animal // true
felix.constructor === Animal // throws TypeError
instanceof  works for  all native types!
[1, 2, 3] instanceof Array // true
/abc/ instanceof RegExp // true
({}) instanceof Object // true
(function(){}) instanceof Function // true

or does it?

3 instanceof Number // false
true instanceof Boolean // false
'abc' instanceof String // false

Okay, what is going on here? It turns out that instanceof does not work with primitive values. The primitive types in Javascript are: stringsnumbersbooleansnull, and undefined - (Oh good! You can count them all on one hand!) Well actually, I should have said it does not work with primitives with the exception of null and undefined, because they are not an instance of anything, and so instanceof correctly returns false when either is used on the left hand side.

null instanceof Boolean // false
undefined instanceof Array // false

To top that off though, checking for the constructor property will work for the primitive types number, string and boolean.

(3).constructor === Number // true
true.constructor === Boolean // true
'abc'.constructor === String // true

This works because whenever you reference a property on a primitive value, Javascript will automatically wrap the value with an object wrapper, like so

var wrapper = new Number(3)

except you don't see this - it happens behind the scenes. The wrapper then will be an instance of - in this case Number - or a Boolean or a String depending on the type of the primitive value, at which point it can walk up the prototype-chain and get at the properties of the Number prototype, etc. So, for example, creating a wrapper manually will make the instanceof operator work

new Number(3) instanceof Number // true
new Boolean(true) instanceof Boolean // true
new String('abc') instanceof String // true

But doing that would be pointless because it requires you to already know the type of the value of which you are asking whether or not it is of the type that you already know it is.

Cross-window Issues of instanceof

It turns out that instanceof has another problem. It breaks down when you try to test an object coming from another window. You know? The ones that are created for each