11. Values
In this chapter, we’ll examine what kinds of values JavaScript has.
11.1. What’s a type?
For this chapter, I consider types to be sets of values. For example, the type boolean
is the set { false
, true
}.
11.2. JavaScript’s type hierarchy
Object
.Fig. 6 shows JavaScript’s type hierarchy. What do we learn from that diagram?
- JavaScript distinguishes two kinds of values: primitive values and objects. We’ll see soon what the difference is.
- The diagram differentiates objects and instances of class
Object
. Each instance ofObject
is also an object, but not vice versa. However, virtually all objects that you’ll encounter in practice are instances ofObject
. For example, objects created via object literals, are. More details on this topic are explained in the chapter on prototype chains and classes.
11.3. The types of the language specification
The ECMAScript specification only knows a total of 7 types. The names of those types are (I’m using TypeScript’s names, not the spec’s names):
undefined
: with the only elementundefined
.null
: with the only elementnull
.boolean
: with the elementsfalse
andtrue
.number
: the type of all numbers (e.g.-123
,3.141
).string
: the type of all strings (e.g.'abc'
).symbol
: the type of all symbols (e.g.Symbol('My Symbol')
).object
: the type of all objects (different fromObject
, the type of all instances of classObject
and its subclasses).
11.4. Primitive values vs. objects
The specification makes an important distinction between values:
- Primitive values are the elements of the types
undefined
,null
,boolean
,number
,string
,symbol
. All other values are objects.
In contrast to Java (that inspired JavaScript here), primitive values are not second-class citizens. The difference between them and objects is more subtle. In a nutshell, it is:Primitive values: are atomic building blocks of data in JavaScript.
- They are passed by value: When primitive values are assigned to variables or passed to functions, their contents are copied.
- They are compared by value: When comparing two primitive values, their contents are compared.
- Objects: are compound pieces of data.
- They are passed by identity (my term): When objects are assigned to variables or passed to functions, their identities (think pointers) are copied.
- They are compared by identity (my term): When comparing two objects, their identities are compared.
Other than that, primitive values and objects are quite similar: They both have properties (key-value entries) and can be used in the same locations.
Next, we’ll look at primitive values and objects in more depth.
11.4.1. Primitive values (short: primitives)
11.4.1.1. Primitives are immutable
You can’t change, add or remove properties of primitives:
11.4.1.2. Primitives are passed by value
Primitives are passed by value: Variables (including parameters) store the contents of the primitives. When assigning a primitive value to a variable or passing it as an argument to a function, its content is copied.
11.4.1.3. Primitives are compared by value
Primitives are compared by value: When comparing two primitive values, we compare their contents.
To see what’s so special about this way of comparing, read on and find out how objects are compared.
11.4.2. Objects
Objects are covered in detail in two chapters on OOP. Here, we mainly focus on how they differ from primitive values.
Let’s first explore two common ways of creating objects:
- Object literal:
The object literal starts and ends with curly braces {}
. It creates an object with two properties. The first property has the key 'first'
(a string) and the value 'Jane'
. The second property has the key 'last'
and the value 'Doe'
. For more information on object literals, consult the chapter on single objects.
- Array literal:
The Array literal starts and ends with square brackets []
. It creates an Array with two elements: 'foo'
and 'bar'
. For more information on Array literals, consult the chapter on Arrays.
11.4.2.1. Objects are mutable by default
By default, you can freely change, add and remove the properties of objects:
11.4.2.2. Objects are passed by identity
Objects are passed by identity (my term): Variables (including parameters) store the identities of objects.
The identity of an object is like a pointer (or a transparent reference) to the object’s actual data on the heap (think shared main memory of a JavaScript engine).
When assigning an object to a variable or passing it as an argument to a function, its identity is copied. Each object literal creates a fresh object on the heap and returns its identity.
JavaScript uses garbage collection to automatically manage memory:
Now the old value { prop: 'value' }
of obj
is garbage (not used anymore). JavaScript will automatically garbage-collect it (remove it from memory), at some point in time (possibly never if there is enough free memory).
11.4.2.3. Objects are compared by identity
Objects are compared by identity (my term): Two variables are only equal if they contain the same object identity. They are not equal if they refer to different objects with the same content.
11.5. The operators typeof and instanceof: what’s the type of a value?
The two operators typeof
and instanceof
let you determine what type a given value x
has:
How do they differ?
typeof
distinguishes the 7 types of the specification (minus one omission, plus one addition).instanceof
tests which class created a given value.
11.5.1. typeof
x | typeof x |
---|---|
undefined | 'undefined' |
null | 'object' |
Boolean | 'boolean' |
Number | 'number' |
String | 'string' |
Symbol | 'symbol' |
Function | 'function' |
All other objects | 'object' |
Tbl. 2 lists all results of typeof
. They roughly correspond to the 7 types of the language specification. Alas, there are two differences and they are language quirks:
typeof null
returns'object'
and not'null'
. That’s a bug. Unfortunately, it can’t be fixed. TC39 tried to do that, but it broke too much code on the web.typeof
of a function should be'object'
(functions are objects). Introducing a separate category for functions is confusing.
11.5.2. instanceof
This operator answers the question: has a value x
been created by a class C
?
For example:
Primitive values are not instances of anything:
11.6. Classes and constructor functions
JavaScript’s original factories for objects are constructor functions: ordinary functions that return “instances” of themselves if you invoke them via the new
operator.
ES6 introduced classes, which are mainly better syntax for constructor functions.
In this book, I’m using the terms constructor function and class interchangeably.
Classes can be seen as partitioning the single type object
of the specification into subtypes – they give us more types than the limited 7 ones of the specification. Each class is the type of the objects that were created by it.
11.6.1. Constructor functions associated with primitive types
Each primitive type (except for the spec-internal types for undefined
and null
) has an associated constructor function (think class):
- The constructor function
Boolean
is associated with booleans. - The constructor function
Number
is associated with numbers. - The constructor function
String
is associated with strings. The constructor function
Symbol
is associated with symbols.
Each of these functions plays several roles. For example,Number
:You can use it as a function and convert values to numbers:
Number.prototype
provides the properties for numbers. For example, method.toString()
:
Number
is a namespace/container object for tool functions for numbers. For example:
- Lastly, you can also use
Number
as a class and create number objects. These objects are different from real numbers and should be avoided.
11.6.1.1. Wrapping primitive values
The constructor functions related to primitive types are also called wrapper types, because they provide the canonical way of converting primitive values to objects. In the process, primitive values are “wrapped” in objects.
Wrapping rarely matters in practice, but it is used internally in the language specification, to give primitives properties.
11.7. Converting between types
There are two ways in which values are converted to other types in JavaScript:
- Explicit conversion: via functions such as
String()
. - Coercion (automatic conversion): happens when an operation receives operands/parameters that it can’t work with.
11.7.1. Explicit conversion between types
The function associated with a primitive type explicitly converts values to that type:
You can also use Object()
to convert values to objects:
11.7.2. Coercion (automatic conversion between types)
For many operations, JavaScript automatically converts the operands/parameters if their types don’t fit. This kind of automatic conversion is called coercion.
For example, the multiplication operator coerces its operands to numbers:
Many built-in functions coerce, too. For example, parseInt()
coerces its parameter to string (parsing stops at the first character that is not a digit):