buy the book to support the author.

Chapter 8. Values

JavaScript has most of the values that we have come to expect from programming languages: booleans, numbers, strings, arrays, and so on. All normal values in JavaScript have properties.[9] Each property has a key (or name) and a value. You can think of properties like fields of a record. You use the dot (.) operator to access properties:

  1. > var obj = {}; // create an empty object
  2. > obj.foo = 123; // write property
  3. 123
  4. > obj.foo // read property
  5. 123
  6. > 'abc'.toUpperCase() // call method
  7. 'ABC'

JavaScript’s Type System

This chapter gives an overview of JavaScript’s type system.

JavaScript’s Types

JavaScript has only six types, according to Chapter 8 of the ECMAScript language specification:

An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language. The ECMAScript language types are:- Undefined, Null- Boolean, String, Number, and- Object

Therefore, constructors technically don’t introduce new types, even though they are said to have instances.

Static Versus Dynamic

In the context of language semantics and type systems, static usually means “at compile time” or “without running a program,” while dynamic means “at runtime.”

Static Typing Versus Dynamic Typing

In a statically typed language, variables, parameters, and members of objects (JavaScript calls them properties) have types that the compiler knows at compile time. The compiler can use that information to perform type checks and to optimize the compiled code.

Even in statically typed languages, a variable also has a dynamic type, the type of the variable’s value at a given point at runtime. The dynamic type can differ from the static type. For example (Java):

  1. Object foo = "abc";

The static type of foo is Object; its dynamic type is String.

JavaScript is dynamically typed; types of variables are generally not known at compile time.

Static Type Checking Versus Dynamic Type Checking

If you have type information, you can check whether a value used in an operation (calling a function, applying an operator, etc.) has the correct type. Statically type-checked languages perform this kind of check at compile time, while dynamically type-checked languages do so at runtime. A language can be both statically type-checked and dynamically type-checked. If a check fails, you usually get some kind of error or exception.

JavaScript performs a very limited kind of dynamic type checking:

  1. > var foo = null;
  2. > foo.prop
  3. TypeError: Cannot read property 'prop' of null

Mostly, however, things silently fail or work. For example, if you access a property that does not exist, you get the value undefined:

  1. > var bar = {};
  2. > bar.prop
  3. undefined

Coercion

In JavaScript, the main way of dealing with a value whose type doesn’t fit is to coerce it to the correct type. Coercion means implicit type conversion. Most operands coerce:

  1. > '3' * '4'
  2. 12

JavaScript’s built-in conversion mechanisms support only the types Boolean, Number, String, and Object. There is no standard way to convert an instance of one constructor to an instance of another constructor.

Warning

The terms strongly typed and weakly typed do not have generally meaningful definitions. They are used, but normally incorrectly. It is better to instead use statically typed, statically type-checked, and so on.

Primitive Values Versus Objects

JavaScript makes a somewhat arbitrary distinction between values:

  • The primitive values are booleans, numbers, strings, null, and undefined.
  • All other values are objects.

A major difference between the two is how they are compared; each object has a unique identity and is only (strictly) equal to itself:

  1. > var obj1 = {}; // an empty object
  2. > var obj2 = {}; // another empty object
  3. > obj1 === obj2
  4. false
  5.  
  6. > var obj3 = obj1;
  7. > obj3 === obj1
  8. true

In contrast, all primitive values encoding the same value are considered the same:

  1. > var prim1 = 123;
  2. > var prim2 = 123;
  3. > prim1 === prim2
  4. true

The following two sections explain primitive values and objects in more detail.

Primitive Values

The following are all of the primitive values (primitives for short):

Primitives have the following characteristics:

  • Compared by value
  • The “content” is compared:
  1. > 3 === 3
  2. true
  3. > 'abc' === 'abc'
  4. true
  • Always immutable
  • Properties can’t be changed, added, or removed:
  1. > var str = 'abc';
  2.  
  3. > str.length = 1; // try to change property `length`
  4. > str.length // ⇒ no effect
  5. 3
  6.  
  7. > str.foo = 3; // try to create property `foo`
  8. > str.foo // ⇒ no effect, unknown property
  9. undefined

(Reading an unknown property always returns undefined.)

  • A fixed set of types
  • You can’t define your own primitive types.

Objects

All nonprimitive values are objects. The most common kinds of objectsare:

  • Plain objects (constructor Object) can be created by object literals (see Chapter 17):
  1. {
  2. firstName: 'Jane',
  3. lastName: 'Doe'
  4. }

The preceding object has two properties: the value of property firstNameis 'Jane', and the value of property lastName is 'Doe'.

  • Arrays (constructor Array) can be created by array literals (see Chapter 18):
  1. [ 'apple', 'banana', 'cherry' ]

The preceding array has three elements that can be accessed via numericindices. For example, the index of 'apple' is 0.

  • Regular expressions (constructor RegExp) can be created by regular expression literals (see Chapter 19):
  1. /^a+b+$/

Objects have the following characteristics:

  • Compared by reference
  • Identities are compared; every object has its own identity:
  1. > ({} === {}) // two different empty objects
  2. false
  3.  
  4. > var obj1 = {};
  5. > var obj2 = obj1;
  6. > obj1 === obj2
  7. true
  1. > var obj = {};
  2. > obj.foo = 123; // add property `foo`
  3. > obj.foo
  4. 123

undefined and null

JavaScript has two “nonvalues” that indicate missing information, undefined and null:

  • undefined means “no value” (neither primitive nor object). Uninitialized variables, missing parameters, and missing properties have that nonvalue. And functions implicitly return it if nothing has been explicitly returned.
  • null means “no object.” It is used as a nonvalue where an object is expected (as a parameter, as a member in a chain of objects, etc.).

undefined and null are the only values for which any kind of property access results in an exception:

  1. > function returnFoo(x) { return x.foo }
  2.  
  3. > returnFoo(true)
  4. undefined
  5. > returnFoo(0)
  6. undefined
  7.  
  8. > returnFoo(null)
  9. TypeError: Cannot read property 'foo' of null
  10. > returnFoo(undefined)
  11. TypeError: Cannot read property 'foo' of undefined

undefined is also sometimes used as more of a metavalue that indicates nonexistence. In contrast, null indicates emptiness. For example, a JSON node visitor (see Transforming Data via Node Visitors) returns:

  • undefined to remove an object property or array element
  • null to set the property or element to null

Occurrences of undefined and null

Here we review the various scenarios where undefined and null occur.

Occurrences of undefined

Uninitialized variables are undefined:

  1. > var foo;
  2. > foo
  3. undefined

Missing parameters are undefined:

  1. > function f(x) { return x }
  2. > f()
  3. undefined

If you read a nonexistent property, you get undefined:

  1. > var obj = {}; // empty object
  2. > obj.foo
  3. undefined

And functions implicitly return undefined if nothing has been explicitly returned:

  1. > function f() {}
  2. > f()
  3. undefined
  4.  
  5. > function g() { return; }
  6. > g()
  7. undefined

Occurrences of null

  1. > Object.getPrototypeOf(Object.prototype)
  2. null
  • null is returned by RegExp.prototype.exec() if there was no match for the regular expression in the string:
  1. > /x/.exec('aaa')
  2. null

Checking for undefined or null

In the following sections we review how to check for undefined and null individually, or to check if either exists.

Checking for null

You check for null via strict equality:

  1. if (x === null) ...

Checking for undefined

Strict equality (===) is the canonical way of checking for undefined:

  1. if (x === undefined) ...

You can also check for undefined via the typeof operator (typeof: Categorizing Primitives), but you should normally use the aforementioned approach.

Checking for either undefined or null

Most functions allow you to indicate a missing value via either undefined or null. One way of checking for both of them is via an explicit comparison:

  1. // Does x have a value?
  2. if (x !== undefined && x !== null) {
  3. ...
  4. }
  5. // Is x a non-value?
  6. if (x === undefined || x === null) {
  7. ...
  8. }

Another way is to exploit the fact that both undefined and null are considered false (see Truthy and Falsy Values):

  1. // Does x have a value (is it truthy)?
  2. if (x) {
  3. ...
  4. }
  5. // Is x falsy?
  6. if (!x) {
  7. ...
  8. }

Warning

false, 0, NaN, and '' are also considered false.

The History of undefined and null

A single nonvalue could play the roles of both undefined and null. Why does JavaScript have two such values? The reason is historical.

JavaScript adopted Java’s approach of partitioning values into primitives and objects. It also used Java’s value for “not an object,” null. Following the precedent set by C (but not Java), null becomes 0 if coerced to a number:

  1. > Number(null)
  2. 0
  3. > 5 + null
  4. 5

Remember that the first version of JavaScript did not have exception handling. Therefore, exceptional cases such as uninitialized variables and missing properties had to be indicated via a value. null would have been a good choice, but Brendan Eich wanted to avoid two things at the time:

  • The value shouldn’t have the connotation of a reference, because it was about more than just objects.
  • The value shouldn’t coerce to 0, because that makes errors harder to spot.

As a result, Eich added undefined as an additional nonvalue to the language. It coerces to NaN:

  1. > Number(undefined)
  2. NaN
  3. > 5 + undefined
  4. NaN

Changing undefined

undefined is a property of the global object (and thus a global variable; see The Global Object). Under ECMAScript 3, you had to take precautions when reading undefined, because it was easy to accidentally change its value. Under ECMAScript 5, that is not necessary, because undefined is read-only.

To protect against a changed undefined, two techniques were popular (they are still relevant for older JavaScript engines):

  • Technique 1
  • Shadow the global undefined (which may have the wrong value):
  1. (function (undefined) {
  2. if (x === undefined) ... // safe now
  3. }()); // don’t hand in a parameter

In the preceding code, undefined is guaranteed to have the right value, because it is a parameter whose value has not been provided by the function call.

  • Technique 2
  • Compare with void 0, which is always (the correct) undefined (see The void Operator):
  1. if (x === void 0) // always safe

Wrapper Objects for Primitives

The three primitive types boolean, number, and string have corresponding constructors: Boolean, Number, String. Their instances (so-called wrapper objects) contain (wrap) primitive values. The constructors can be used in two ways:

  • As constructors, they create objects that are largely incompatible with the primitive values that they wrap:
  1. > typeof new String('abc')
  2. 'object'
  3. > new String('abc') === 'abc'
  4. false
  1. > String(123)
  2. '123'

Tip

It’s considered a best practice to avoid wrapper objects. You normally don’t need them, as there is nothing that objects can do that primitives can’t (with the exception of being mutated). (This is different from Java, from which JavaScript inherited the difference between primitives and objects!)

Wrapper Objects Are Different from Primitives

Primitive values such as 'abc' are fundamentally different from wrapper instances such as new String('abc'):

  1. > typeof 'abc' // a primitive value
  2. 'string'
  3. > typeof new String('abc') // an object
  4. 'object'
  5. > 'abc' instanceof String // never true for primitives
  6. false
  7. > 'abc' === new String('abc')
  8. false

Wrapper instances are objects, and there is no way of comparing objects in JavaScript, not even via lenient equals == (see Equality Operators: === Versus ==):

  1. > var a = new String('abc');
  2. > var b = new String('abc');
  3. > a == b
  4. false

Wrapping and Unwrapping Primitives

There is one use case for wrapper objects: you want to add properties to a primitive value. Then you wrap the primitive and add properties to the wrapper object. You need to unwrap the value before you can work with it.

Wrap a primitive by invoking a wrapper constructor:

  1. new Boolean(true)
  2. new Number(123)
  3. new String('abc')

Unwrap a primitive by invoking the method valueOf(). All objects have this method (as discussed in Conversion to Primitive):

  1. > new Boolean(true).valueOf()
  2. true
  3. > new Number(123).valueOf()
  4. 123
  5. > new String('abc').valueOf()
  6. 'abc'

Converting wrapper objects to primitives properly extracts numbers and strings, but not booleans:

  1. > Boolean(new Boolean(false)) // does not unwrap
  2. true
  3. > Number(new Number(123)) // unwraps
  4. 123
  5. > String(new String('abc')) // unwraps
  6. 'abc'

The reason for this is explained in Converting to Boolean.

Primitives Borrow Their Methods from Wrappers

Primitives don’t have their own methods and borrow them from wrappers:

  1. > 'abc'.charAt === String.prototype.charAt
  2. true

Sloppy mode and strict mode handle this borrowing differently. In sloppy mode, primitives are converted to wrappers on the fly:

  1. String.prototype.sloppyMethod = function () {
  2. console.log(typeof this); // object
  3. console.log(this instanceof String); // true
  4. };
  5. ''.sloppyMethod(); // call the above method

In strict mode, methods from the wrapper prototype are used transparently:

  1. String.prototype.strictMethod = function () {
  2. 'use strict';
  3. console.log(typeof this); // string
  4. console.log(this instanceof String); // false
  5. };
  6. ''.strictMethod(); // call the above method

Type Coercion

Type coercion means the implicit conversion of a value of one type to a value of another type. Most of JavaScript’s operators, functions, and methods coerce operands and arguments to the types that they need. For example, the operands of the multiplication operator (*) are coerced to numbers:

  1. > '3' * '4'
  2. 12

As another example, if one of the operands is a string, the plus operator (+) converts the other one to a string:

  1. > 3 + ' times'
  2. '3 times'

Type Coercion Can Hide Bugs

Therefore, JavaScript rarely complains about a value having the wrong type. For example, programs normally receive user input (from online forms or GUI widgets) as strings, even if the user has entered a number. If you treat a number-as-string like a number, you will not get a warning, just unexpected results. For example:

  1. var formData = { width: '100' };
  2.  
  3. // You think formData.width is a number
  4. // and get unexpected results
  5. var w = formData.width;
  6. var outer = w + 20;
  7.  
  8. // You expect outer to be 120, but it’s not
  9. console.log(outer === 120); // false
  10. console.log(outer === '10020'); // true

In cases such as the preceding one, you should convert to the appropriate type early on:

  1. var w = Number(formData.width);

Functions for Converting to Boolean, Number, String, and Object

The following functions are the preferred way of converting a value to a boolean, number, string, or object:

  • Boolean() (see Converting to Boolean)
  • Converts a value to a boolean. The following values are converted to false; they are the so-called “falsy” values:
  • undefined, null
  • false
  • 0, NaN
  • ''

All other values are considered “truthy” and converted to true (including all objects!).

  • undefined becomes NaN.
  • null becomes 0.
  • false becomes 0, true becomes 1.
  • Strings are parsed.
  • Objects are first converted to primitives (discussed shortly), which are then converted to numbers.
  • String() (see Converting to String)
  • Converts a value to a string. It has the obvious results for all primitives. For example:
  1. > String(null)
  2. 'null'
  3. > String(123.45)
  4. '123.45'
  5. > String(false)
  6. 'false'

Objects are first converted to primitives (discussed shortly), which are then converted to strings.

  • Object() (see Converting Any Value to an Object)
  • Converts objects to themselves, undefined and null to empty objects, and primitives to wrapped primitives. For example:
  1. > var obj = { foo: 123 };
  2. > Object(obj) === obj
  3. true
  4.  
  5. > Object(undefined)
  6. {}
  7. > Object('abc') instanceof String
  8. true

Note that Boolean(), Number(), String(), and Object() are called as functions. You normally don’t use them as constructors. Then they create instances of themselves (see Wrapper Objects for Primitives).

Algorithm: ToPrimitive()—Converting a Value to a Primitive

To convert a value to either a number or a string, it is first converted to an arbitrary primitive value, which is then converted to the final type (as discussed in Functions for Converting to Boolean, Number, String, and Object).

The ECMAScript specification has an internal function, ToPrimitive() (which is not accessible from JavaScript), that performs this conversion.Understanding ToPrimitive() enables you to configure how objects are converted to numbers and strings. It has the following signature:

  1. ToPrimitive(input, PreferredType?)

The optional parameter PreferredType indicates the final type of the conversion: it is either Number or String, depending on whether the result of ToPrimitive() will be converted to a number or a string.

If PreferredType is Number, then you perform the following steps:

  • If input is primitive, return it (there is nothing more to do).
  • Otherwise, input is an object. Call input.valueOf(). If the result is primitive, return it.
  • Otherwise, call input.toString(). If the result is primitive, return it.
  • Otherwise, throw a TypeError (indicating the failure to convert input to a primitive).

If PreferredType is String, steps 2 and 3 are swapped. The PreferredType can also be omitted; it is then considered to be String for dates and Number for all other values. This is how the operators + and == call ToPrimitive().

Examples: ToPrimitive() in action

The default implementation of valueOf() returns this, while the default implementation of toString() returns type information:

  1. > var empty = {};
  2. > empty.valueOf() === empty
  3. true
  4. > empty.toString()
  5. '[object Object]'

Therefore, Number() skips valueOf() and converts the result of toString() to a number; that is, it converts '[object Object]' to NaN:

  1. > Number({})
  2. NaN

The following object customizes valueOf(), which influences Number(), but doesn’t change anything for String():

  1. > var n = { valueOf: function () { return 123 } };
  2. > Number(n)
  3. 123
  4. > String(n)
  5. '[object Object]'

The following object customizes toString(). Because the result can be converted to a number, Number() can return a number:

  1. > var s = { toString: function () { return '7'; } };
  2. > String(s)
  3. '7'
  4. > Number(s)
  5. 7


[9] Technically, primitive values do not have their own properties, they borrow them from wrapper constructors. But that is something that goes on behind the scenes, so you don’t normally see it.