Please support this book: buy it or donate

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

Figure 6: A partial hierarchy of JavaScript’s types. Missing are the classes for errors, the classes associated with primitive types, and more. The diagram hints at the fact that not all objects are instances of Object.

Figure 6: A partial hierarchy of JavaScript’s types. Missing are the classes for errors, the classes associated with primitive types, and more. The diagram hints at the fact that not all objects are instances of 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 of Object is also an object, but not vice versa. However, virtually all objects that you’ll encounter in practice are instances of Object. 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 element undefined.
  • null: with the only element null.
  • boolean: with the elements false and true.
  • 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 from Object, the type of all instances of class Object 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:

  1. let str = 'abc';
  2. assert.equal(str.length, 3);
  3. assert.throws(
  4. () => { str.length = 1 },
  5. /^TypeError: Cannot assign to read only property 'length'/
  6. );
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.

  1. let x = 123;
  2. let y = x;
  3. assert.equal(y, 123);
11.4.1.3. Primitives are compared by value

Primitives are compared by value: When comparing two primitive values, we compare their contents.

  1. assert.equal(123 === 123, true);
  2. assert.equal('abc' === 'abc', true);

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:
  1. const obj = {
  2. first: 'Jane',
  3. last: 'Doe',
  4. };

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:
  1. const arr = ['foo', 'bar'];

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:

  1. const obj = {};
  2. obj.foo = 'abc'; // add a property
  3. assert.equal(obj.foo, 'abc');
  4. obj.foo = 'def'; // change a property
  5. assert.equal(obj.foo, 'def');
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.

  1. const a = {}; // fresh empty object
  2. // Pass the identity in `a` to `b`:
  3. const b = a;
  4. // Now `a` and `b` point to the same object
  5. // (they “share” that object):
  6. assert.equal(a === b, true);
  7. // Changing `a` also changes `b`:
  8. a.foo = 123;
  9. assert.equal(b.foo, 123);

JavaScript uses garbage collection to automatically manage memory:

  1. let obj = { prop: 'value' };
  2. obj = {};

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.

  1. const obj = {}; // fresh empty object
  2. assert.equal(obj === obj, true); // same identity
  3. assert.equal({} === {}, false); // different identities, 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:

  1. if (typeof x === 'string') ···
  2. if (x instanceof Array) ···

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

Table 2: The results of the typeof operator.
xtypeof 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?

  1. x instanceof C

For example:

  1. > (function() {}) instanceof Function
  2. true
  3. > ({}) instanceof Object
  4. true
  5. > [] instanceof Array
  6. true

Primitive values are not instances of anything:

  1. > 123 instanceof Number
  2. false
  3. > '' instanceof String
  4. false
  5. > '' instanceof Object
  6. false

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:

  1. assert.equal(Number('123'), 123);
  • Number.prototype provides the properties for numbers. For example, method .toString():
  1. assert.equal((123).toString, Number.prototype.toString);
  • Number is a namespace/container object for tool functions for numbers. For example:
  1. assert.equal(Number.isInteger(123), true);
  • Lastly, you can also use Number as a class and create number objects. These objects are different from real numbers and should be avoided.
  1. assert.notEqual(new Number(123), 123);
  2. assert.equal(new Number(123).valueOf(), 123);
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.

  1. const prim = true;
  2. assert.equal(typeof prim, 'boolean');
  3. assert.equal(prim instanceof Boolean, false);
  4. const wrapped = Object(prim);
  5. assert.equal(typeof wrapped, 'object');
  6. assert.equal(wrapped instanceof Boolean, true);
  7. assert.equal(wrapped.valueOf(), prim); // unwrap

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:

  1. > Boolean(0)
  2. false
  3. > Number('123')
  4. 123
  5. > String(123)
  6. '123'

You can also use Object() to convert values to objects:

  1. > typeof Object(123)
  2. 'object'

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:

  1. > '7' * '3'
  2. 21

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):

  1. > parseInt(123.45)
  2. 123