Adobe
Products

Top destinations

  • Adobe Creative Cloud
  • Creative Suite
  • Adobe Marketing Cloud
  • Acrobat
  • Photoshop
  • SiteCatalyst
  • Students
  • Elements family

Adobe Creative Cloud

  • What is Adobe Creative Cloud?
  • Design
  • Web
  • Photography
  • Video
  • Students
  • Teams
  • Enterprise
  • Educational institutions

Design and photography

  • Photoshop
  • Illustrator
  • InDesign
  • Adobe Muse
  • Lightroom

Video

  • Adobe Premiere
  • After Effects

Web development and HTML5

  • Edge Tools & Services [opens in a new window]
  • Dreamweaver
  • Gaming [opens in a new window]

Adobe Marketing Cloud

  • What is Adobe Marketing Cloud?
  • Digital analytics
  • Social marketing
  • Web experience management
  • Testing and targeting
  • Media optimization

Analytics

  • SiteCatalyst
  • Adobe Discover
  • Insight

Social

  • Adobe Social

Experience Manager

  • CQ
  • Scene7

Target

  • Test&Target
  • Recommendations
  • Search&Promote

Media Optimizer

  • AdLens
  • AudienceManager
  • AudienceResearch

Document services

  • Acrobat
  • EchoSign [opens in a new window]
  • FormsCentral [opens in a new window]
  • SendNow [opens in a new window]
  • Acrobat.com [opens in a new window]

Publishing

  • Digital Publishing Suite

  • See all products
Business solutions

By business need

  • Digital analytics
  • Digital publishing
  • Document management
  • Media optimization
  • Social marketing
  • Testing and targeting
  • Video editing and serving
  • Web development [opens in a new window]
  • Web experience management
  • See all business needs

By industry

  • Broadcast
  • Education
  • Financial services
  • Government
  • Publishing
  • Retail
  • See all industries
Support & Learning

I need help

  • Products
  • Adobe Creative Cloud
  • Adobe Marketing Cloud
  • Forums [opens in a new window]

I want to learn

  • Training and tutorials
  • Certification [opens in a new window]
  • Adobe Developer Connection
  • Adobe Design Center
  • Adobe TV [opens in a new window]
  • Adobe Marketing Center
  • Adobe Labs [opens in a new window]
Download
  • Product trials
  • Adobe Flash Player
  • Adobe Reader
  • Adobe AIR
  • See all downloads
Company
  • Careers at Adobe
  • Investor Relations
  • Newsroom
  • Privacy
  • Corporate Social Responsibility
  • Customer Showcase
  • Contact us
  • More company info
Buy
  • For personal and professional use
  • For students, educators, and staff
  • For small and medium businesses
  • Volume Licensing
  • Special offers
  • Adobe Marketing Cloud sales [opens in a new window]
Search
 
Info Sign in
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Welcome,
My Adobe
My orders
My information
My preferences
My products and services
Sign out
My cart
Privacy My Adobe
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out Privacy My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Promotions
Estimated shipping
Tax
Calculated at checkout
Total
Review and Checkout
Adobe Developer Connection / HTML5, CSS3, and JavaScript /

Categorizing values in JavaScript

by Dr. Axel Rauschmayer

Dr. Axel Rauschmayer
  • www.2ality.com
  • @rauschma

Content

  • Reviewing the basics
  • Categorizing values
  • Built-in prototype objects
  • Recommendations
  • Where to go from here

Created

8 October 2012

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
ECMAScriptJavaScript
Was this helpful?
Yes   No

By clicking Submit, you accept the Adobe Terms of Use.

 
Thanks for your feedback.

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:

  • Prototypes as classes – an introduction to JavaScript inheritance
  • JavaScript inheritance by example
  • Exemplar patterns in JavaScript
  • Private data for objects in JavaScript

Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License+Adobe Commercial Rights

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

More Like This

  • The pursuit of simplicity
  • An Overview of Brackets' Code Architecture
  • Backbone.js Wine Cellar tutorial – Part 1: Getting started
  • JavaScript design patterns – Part 1: Singleton, composite, and façade
  • Getting started with PhoneGap in Eclipse for Android
  • Using the Geolocation API
  • Unit test JavaScript applications with Jasmine
  • Backbone.js Wine Cellar tutorial – Part 2: CRUD
  • Build a Hangman game with HTML5 Canvas, JavaScript, and CSS – Part 1: Creating the interface
  • JavaScript object creation: Learning to live without "new"

Products

  • Adobe Creative Cloud
  • Creative Suite
  • Adobe Marketing Cloud
  • Acrobat
  • Photoshop
  • Digital Publishing Suite
  • Elements family
  • SiteCatalyst
  • For education

Download

  • Product trials
  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR

Support & Learning

  • Product help
  • Forums

Buy

  • For personal and professional use
  • For students, educators, and staff
  • For small and medium businesses
  • Volume Licensing
  • Special offers

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2013 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy | Cookies

Ad Choices

Reviewed by TRUSTe: site privacy statement