Requirements

Prerequisite knowledge

Basic knowledge of JavaScript.

User level

Intermediate

In this article, I'll explain what kinds of values JavaScript has and how you can categorize them. That will help you better understand how the language works. It will also help you with advanced programming tasks, such as writing a library, where you often have to deal with all kinds of values being passed to you. With the knowledge gained here, you will be able to avoid bugs caused by subtle differences between values.
 
I'll show you four ways in which values can be categorized: via the hidden property [[Class]], via the typeof operator, via the instanceof operator, and via the function Array.isArray(). I'll also explain the prototype objects of built-in constructors, which produce unexpected categorization results.
 

 
Reviewing the basics

Before you can get started with the actual topic, you'll need to review some required knowledge.
 
 
Primitives versus objects
All values in JavaScript are either primitives or objects.
 
Primitives. The following values are primitive:
 
  • undefined
  • null
  • Booleans
  • Numbers
  • Strings
Primitives are immutable; you can't add properties to them:
 
> var str = "abc"; > str.foo = 123; // try to add property "foo" 123 > str.foo // no change undefined
Also, primitives are compared by value, meaning they are considered equal if they have the same content:
 
> "abc" === "abc" true
Objects. All non-primitive values are objects. Objects are mutable:
 
> var obj = {}; > obj.foo = 123; // try to add property "foo" 123 > obj.foo // property "foo" has been added 123
Objects are compared by reference. Each object has its own identity and, because of this, two objects are only considered equal if they are, in fact, the same object:
 
> {} === {} false > var obj = {}; > obj === obj true
 
Wrapper object types
The primitive types boolean, number and string have the corresponding wrapper object types Boolean, Number and String. Instances of the latter types are objects and different from the primitives that they are wrapping:
 
 
> typeof new String("abc") 'object' > typeof "abc" 'string' > new String("abc") === "abc" false
Wrapper object types are rarely used directly, but their prototype objects define the methods of primitives. For example, String.prototype is the prototype object of the wrapper type String. All of its methods are also available for strings. Take the wrapper method String.prototype.indexOf. Primitive strings have the same method, not a different method with the same name, but literally the same method:
 
> String.prototype.indexOf === "".indexOf true
 
Internal properties
Internal properties are properties that cannot be directly accessed from JavaScript, but influence how it works. The names of internal properties start with an uppercase letter and are written in double square braces. As an example, [[Extensible]] is an internal property that holds a boolean flag that determines whether or not properties can be added to an object. Its value can only be manipulated indirectly. Object.isExtensible() reads its value, while Object.preventExtensions() sets its value to false. Once it is false, there is no way to change its value to true.
 
 
Terminology: prototypes versus prototype objects
In JavaScript, the term prototype is unfortunately a bit overloaded:
 
  1. On one hand, there is the prototype-of relationship between objects. Each object has a hidden property [[Prototype]] that either points to its prototype or is null. The prototype is a continuation of the object. If a property of an object is accessed and it can't be found in the latter, the search continues in the former. Multiple objects can have the same prototype.
  2. On the other hand, if, for example, a type is implemented by a constructor Foo, then that constructor has a property Foo.prototype that holds the type's prototype object.
To make the distinction clear, developers call (1) "prototypes" and (2) "prototype objects". Three methods help in dealing with prototypes:
 
  • Object.getPrototypeOf(obj) returns the prototype of obj:
    > Object.getPrototypeOf({}) === Object.prototype
    true
  • Object.create(proto) creates an empty object whose prototype is proto:
    > Object.create(Object.prototype)
    {}


    Object.create() can do more, but that is beyond the scope of this article.
  • proto.isPrototypeOf(obj) returns true if proto is a prototype of obj (or a prototype of a prototype, and so on):
    > Object.prototype.isPrototypeOf({})
    true
     
 
The property "constructor"
Given a constructor function Foo, the prototype object Foo.prototype has a property Foo.prototype.constructor that points back to Foo. That property is set up automatically for each function.
 
> function Foo() { } > Foo.prototype.constructor === Foo true > RegExp.prototype.constructor === RegExp true
All instances of a constructor inherit that property from the prototype object. Thus, you can use it to determine which constructor created an instance:
 
> new Foo().constructor [Function: Foo] > /abc/.constructor [Function: RegExp]

 
Categorizing values

Here are four ways of categorizing values:
 
  • [[Class]] is an internal property with a string that classifies an object.
  • typeof is an operator that categorizes primitives and helps distinguish them from objects.
  • instanceof is an operator that categorizes objects.
  • Array.isArray() is a function that determines whether a value is an array.
 
[[Class]]
[[Class]] is an internal property whose value is one of the following strings:
 
"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"
The only way to access it from JavaScript code is via the default toString() method, which can be invoked generically like this:
 
Object.prototype.toString.call(value)
Such an invocation returns:
 
  • "[object Undefined]" if value is undefined,
  • "[object Null]" if value is null,
  • "[object " + value.[[Class]] + "]" if value is an object.
  • "[object " + value.[[Class]] + "]" if value is a primitive (it is converted to an object and handled like in the previous rule).
Examples:
 
> Object.prototype.toString.call(undefined) '[object Undefined]' > Object.prototype.toString.call(Math) '[object Math]' > Object.prototype.toString.call({}) '[object Object]'
Therefore, the following function can be used to retrieve the [[Class]] of a value x:
 
function getClass(x) { var str = Object.prototype.toString.call(x); return /^\[object (.*)\]$/.exec(str)[1]; }
Here is that function in action:
 
> getClass(null) 'Null' > getClass({}) 'Object' > getClass([]) 'Array' > getClass(JSON) 'JSON' > (function () { return getClass(arguments) }()) 'Arguments' > function Foo() {} > getClass(new Foo()) 'Object'
 
typeof
typeof categorizes primitives and allows us to distinguish between primitives and objects using the following syntax:
 
typeof value
 
It returns one of the strings in Table 1, depending on the operand value:
 
Table 1. typeof return values
 
Operand
Result
undefined
"undefined"
null
"object"
Boolean value
"boolean"
Number value
"number"
String value
"string"
Function
"function"
All other values
"object"
The typeof operator's returning "object" for null is a bug in JavaScript. Unfortunately, it can't be fixed, because that would break existing code. Note that while a function is also an object, typeof makes a distinction. Arrays, on the other hand, are considered objects. All of these quirks make it complicated to check whether a value is an object:
 
function isObject(x) { return x !== null && (typeof x === 'object' || typeof x === 'function'); }
 
instanceof
instanceof checks whether a value is an instance of a type with the syntax:
 
value instanceof Type
The operator looks at Type.prototype and checks to see whether it is in the prototype chain of value. That is, if you were to implement instanceof yourself, it might look something like this (minus some error checks, such as for type being null):
 
function myInstanceof(value, Type) { return Type.prototype.isPrototypeOf(value); }
instanceof always returns false for primitive values:
 
> "" instanceof String false > "" instanceof Object false
 
Array.isArray()
Array.isArray() exists because of one particular problem in browsers: each frame has its own global environment. An example: Given a frame A and a frame B (where either one can be the document), code in frame A can pass a value to code in frame B. The code in frame B cannot use instanceof Array to check whether the value is an array because its B Array is different from the A Array. An example:
 
<html> <head> <script> // test() is called from the iframe function test(arr) { var iframeWin = frames[0]; console.log(arr instanceof Array); // false console.log(arr instanceof iframeWin.Array); // true console.log(Array.isArray(arr)); // true } </script> </head> <body> <iframe></iframe> <script> // Fill the iframe var iframeWin = frames[0]; iframeWin.document.write( '<script>window.parent.test([])</'+'script>'); </script> </body> </html>
Therefore, ECMAScript 5 introduced Array.isArray(), which uses [[Class]] to determine whether a value is an array. Nonetheless, the problem described above in this frames example exists for all types when used in conjunction with instanceof.
 

 
Built-in prototype objects

The prototype objects of built-in types are strange values: They behave like instances of the types, but when examined via instanceof, they are not instances. A few other categorization results for prototype objects are also unexpected. By trying to understand what happens, you can deepen your understanding of categorization.
 
 
 
Object.prototype
 
Object.prototype is an empty object: It is printed as one and does not have any enumerable own properties:
 
> Object.prototype {} > Object.keys(Object.prototype) []
Unexpected. Object.prototype is an object, but it is not an instance of Object. On one hand, both typeof and [[Class]] recognize it as an object:
 
> getClass(Object.prototype) 'Object' > typeof Object.prototype 'object'
On the other hand, instanceof does not consider it an instance of Object:
 
> Object.prototype instanceof Object false
For the above result to be true, Object.prototype would have to be in its own prototype chain, causing a cycle starting and ending with Object.prototype. The prototype chain would not be linear anymore, which is not something you want for a data structure that has to be easy to traverse. Therefore, Object.prototype does not have a prototype. It is the only built-in object that doesn't have one.
 
> Object.getPrototypeOf(Object.prototype) null
This kind of paradox holds true for all built-in prototype objects: They are considered instances of their type by all mechanisms except instanceof.
 
Expected. [[Class]], typeof and instanceof agree on most other objects:
 
> getClass({}) 'Object' > typeof {} 'object' > {} instanceof Object true
 
Function.prototype
Function.prototype is itself a function. It accepts any arguments and returns undefined:
 
> Function.prototype("a", "b", 1, 2) undefined
Unexpected. Function.prototype is a function, but not an instance of Function: On one hand, typeof, which checks whether an internal [[Call]] method is present, says that Function.prototype is a function:
 
> typeof Function.prototype 'function'
The [[Class]] property says the same:
 
> getClass(Function.prototype) 'Function'
On the other hand, instanceof says that Function.prototype is not an instance of Function.
 
> Function.prototype instanceof Function false
That's because it doesn't have Function.prototype in its prototype chain. Instead, its prototype is Object.prototype:
 
> Object.getPrototypeOf(Function.prototype) === Object.prototype true
Expected. With all other functions, there are no surprises:
 
> typeof function () {} 'function' > getClass(function () {}) 'Function' > function () {} instanceof Function true
Function is also always considered a function in every case:
 
> typeof Function 'function' > getClass(Function) 'Function' > Function instanceof Function true
 
Array.prototype
Array.prototype is an empty array: It is displayed that way and has a length of 0:
 
> Array.prototype [] > Array.prototype.length 0
[[Class]] also considers it an array:
 
> getClass(Array.prototype) 'Array'
So does Array.isArray(), which is based on [[Class]]:
 
> Array.isArray(Array.prototype) true
Naturally, instanceof doesn't:
 
> Array.prototype instanceof Array false
So as not to be redundant, I won't mention prototype objects not being instances of their type again for the remainder of this section.
 
 
RegExp.prototype
RegExp.prototype is a regular expression that matches everything:
 
> RegExp.prototype.test("abc") true > RegExp.prototype.test("") true
RegExp.prototype is also accepted by String.prototype.match, which checks whether its argument is a regular expression via [[Class]]. That check is positive for both regular expressions and the prototype object:
 
> getClass(/abc/) 'RegExp' > getClass(RegExp.prototype) 'RegExp'
Excursion: the empty regular expression. RegExp.prototype is equivalent to the "empty regular expression". That expression is created in either one of two ways:
 
new RegExp("") // constructor /(?:)/ // literal
You should only use the RegExp constructor if you are dynamically assembling a regular expression. Alas, expressing the empty regular expression via a literal is complicated by the fact that you can't use //, which would start a comment. The empty non-capturing group (?:) behaves the same as the empty regular expression: it matches everything and does not create captures in a match:
 
> new RegExp("").exec("abc") [ '', index: 0, input: 'abc' ] > /(?:)/.exec("abc") [ '', index: 0, input: 'abc' ]
An empty group not only holds the complete match at index 0, but also the capture of that (first) group at index 1:
 
> /()/.exec("abc") [ '', // index 0 '', // index 1 index: 0, input: 'abc' ]
Interestingly, both an empty regular expression created via the constructor and RegExp.prototype are displayed as the empty literal:
 
> new RegExp("") /(?:)/ > RegExp.prototype /(?:)/
 
Date.prototype
Date.prototype is also a date:
 
> getClass(new Date()) 'Date' > getClass(Date.prototype) 'Date'
Dates wrap numbers. Quoting the ECMAScript 5.1 specification:
 
A Date object contains a Number indicating a particular instant in time to within a millisecond. Such a Number is called a time value. A time value may also be NaN, indicating that the Date object does not represent a specific instant of time.
 
Time is measured in ECMAScript in milliseconds since 01 January, 1970 UTC.
 
Two common ways of accessing the time value are by calling valueOf or by coercing a date to a number:
 
> var d = new Date(); // now > d.valueOf() 1347035199049 > Number(d) 1347035199049
The time value of Date.prototype is NaN:
 
> Date.prototype.valueOf() NaN > Number(Date.prototype) NaN
Date.prototype is displayed as an invalid date, the same as dates that have been created via NaN:
 
> Date.prototype Invalid Date > new Date(NaN) Invalid Date
 
Number.prototype
Number.prototype is roughly the same as new Number(0):
 
> Number.prototype.valueOf() 0`
The conversion to number returns the wrapped primitive value:
 
> +Number.prototype 0
Compare that to:
 
> +new Number(0) 0
 
String.prototype
Similarly, String.prototype is roughly the same as new String(""):
 
> String.prototype.valueOf() ''
The conversion to string returns the wrapped primitive value:
 
> "" + String.prototype ''
Compare that to:
 
> "" + new String("") ''
 
Boolean.prototype
Boolean.prototype is roughly the same as new Boolean(false):
 
> Boolean.prototype.valueOf() false
Boolean objects can be coerced to boolean (primitive) values, but the result of that coercion is always true, because converting any object to boolean is always true.
 
> !!Boolean.prototype true > !!new Boolean(false) true > !!new Boolean(true) true
That is different from how objects are converted to numbers or strings. If an object wraps these primitives, the result of a conversion is the wrapped primitive.
 

 
Recommendations

The following are recommendations for how to best categorize values in JavaScript.
 
 
Treating prototype objects as primal members of their types
Is a prototype object always a primal member of a type? No, that only holds true for the built-in types. In general, that behavior of prototype objects is merely a curiosity. It is better to think of them as analogs to classes: they contain properties that are shared by all instances (usually methods).
 
 
Which categorization mechanisms to use
When deciding on how to best use JavaScript's quirky categorization mechanisms, you have to distinguish between normal code and code that might encounter values from other frames.
 
Normal code. For normal code, use typeof and instanceof and forget about [[Class]] and Array.isArray(). You have to be aware of typeof's quirks: that null is considered an "object" and that there are two non-primitive categories, "object" and "function". For example, a function for determining whether a value is an object could be implemented as follows.
 
function isObject(v) { return (typeof v === "object" && v !== null) || typeof v === "function"; }
Trying this method out would look like:
 
> isObject({}) true > isObject([]) true > isObject("") false > isObject(undefined) false
Code that works with values from other frames. If you expect to receive values from other frames, then instanceof is not reliable. You have to consider [[Class]] and Array.isArray(). An alternative is to work with the name of an object's constructor, but that is a brittle solution because not all objects record their constructor, not all constructors have a name, and there is the risk of name clashes. The following function shows how to retrieve the name of the constructor of an object:
 
function getConstructorName(obj) { if (obj.constructor && obj.constructor.name) { return obj.constructor.name; } else { return ""; } }
Another thing worth pointing out is that the name property of functions (such as obj.constructor) is nonstandard and, for example, not supported by Internet Explorer. Trying it out:
 
> getConstructorName({}) 'Object' > getConstructorName([]) 'Array' > getConstructorName(/abc/) 'RegExp' > function Foo() {} > getConstructorName(new Foo()) 'Foo'
If you apply getConstructorName() to a primitive value, you get the name of the associated wrapper type:
 
> getConstructorName("") 'String'
That's because the primitive value gets the property constructor from the wrapper type:
 
> "".constructor === String.prototype.constructor true

 
Where to go from here

In this article, you learned how to categorize values in JavaScript. It is unfortunate that one needs detailed knowledge in order to perform this task properly, as the two primary categorization operators are flawed: typeof has quirks (such as returning "object" for null) and instanceof cannot handle objects from other frames. The article included recommendations for working around those flaws.
 
As a next step, you can learn more about JavaScript inheritance. The following four blog posts will get you started: