In this chapter, we’ll examine what kinds of values JavaScript has.

    For this chapter, I consider types to be sets of values. For example, the type 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.

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

    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;
    11.4.1.3. Primitives are compared by value

    1. assert.equal(123 === 123, 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 . 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. last: 'Doe',

    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:

    1. const obj = {};
    2. assert.equal(obj.foo, 'abc');
    3. obj.foo = 'def'; // change a property
    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. // Pass the identity in `a` to `b`:
    2. // (they “share” that object):
    3. a.foo = 123;

    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.

    1. assert.equal(obj === obj, true); // same identity

    The two operators typeof and instanceof let you determine what type a given value x has:

    1. if (typeof x === 'string') ···

    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

    • 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. > ({}) instanceof Object
    2. true

    Primitive values are not instances of anything:

    1. false
    2. false
    3. 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:

    • Number.prototype provides the properties for numbers. For example, method .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.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. assert.equal(typeof prim, 'boolean');
    2. assert.equal(typeof wrapped, 'object');

    Wrapping rarely matters in practice, but it is used internally in the language specification, to give primitives properties.

    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. false
    2. 123
    3. '123'

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

    1. > typeof Object(123)

    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.

    Many built-in functions coerce, too. For example, coerces its parameter to string (parsing stops at the first character that is not a digit):