Computer programs work by manipulating values, such as the number 3.14 or the text
“Hello World.” The kinds of values that can be represented and manipulated in a programming language are known as types, and one of the most fundamental charac-teristics of a programming language is the set of types it supports. When a program needs to retain a value for future use, it assigns the value to (or “stores” the value in) a variable. A variable defines a symbolic name for a value and allows the value to be referred to by name. The way that variables work is another fundamental characteristic of any programming language. This chapter explains types, values, and variables in JavaScript. These introductory paragraphs provide an overview, and you may find it helpful to refer to §1.1 while you read them. The sections that follow cover these topics in depth.
JavaScript types can be divided into two categories: primitive types and object types.
JavaScript’s primitive types include numbers, strings of text (known as strings), and Boolean truth values (known as booleans). A significant portion of this chapter is dedi-cated to a detailed explanation of the numeric (§3.1) and string (§3.2) types in Java-Script. Booleans are covered in §3.3.
The special JavaScript values null and undefined are primitive values, but they are not numbers, strings, or booleans. Each value is typically considered to be the sole member of its own special type. §3.4 has more about null and undefined.
Any JavaScript value that is not a number, a string, a boolean, or null or undefined is an object. An object (that is, a member of the type object) is a collection of properties where each property has a name and a value (either a primitive value, such as a number or string, or an object). One very special object, the global object, is covered in §3.5, but more general and more detailed coverage of objects is in Chapter 6.
An ordinary JavaScript object is an unordered collection of named values. The language also defines a special kind of object, known as an array, that represents an ordered collection of numbered values. The JavaScript language includes special syntax for working with arrays, and arrays have some special behavior that distinguishes them from ordinary objects. Arrays are the subject of Chapter 7.
29
JavaScript defines another special kind of object, known as a function. A function is an object that has executable code associated with it. A function may be invoked to run that executable code and return a computed value. Like arrays, functions behave dif-ferently from other kinds of objects, and JavaScript defines a special language syntax for working with them. The most important thing about functions in JavaScript is that they are true values and that JavaScript programs can treat them like regular objects.
Functions are covered in Chapter 8.
Functions that are written to be used (with the new operator) to initialize a newly created object are known as constructors. Each constructor defines a class of objects—the set of objects initialized by that constructor. Classes can be thought of as subtypes of the object type. In addition to the Array and Function classes, core JavaScript defines three other useful classes. The Date class defines objects that represent dates. The RegExp class defines objects that represent regular expressions (a powerful pattern-matching tool described in Chapter 10). And the Error class defines objects that represent syntax and runtime errors that can occur in a JavaScript program. You can define your own classes of objects by defining appropriate constructor functions. This is explained in Chapter 9.
The JavaScript interpreter performs automatic garbage collection for memory manage-ment. This means that a program can create objects as needed, and the programmer never needs to worry about destruction or deallocation of those objects. When an object is no longer reachable—when a program no longer has any way to refer to it—the interpreter knows it can never be used again and automatically reclaims the memory it was occupying.
JavaScript is an object-oriented language. Loosely, this means that rather than having globally defined functions to operate on values of various types, the types themselves define methods for working with values. To sort the elements of an array a, for example, we don’t pass a to a sort() function. Instead, we invoke the sort() method of a:
a.sort(); // The object-oriented version of sort(a).
Method definition is covered in Chapter 9. Technically, it is only JavaScript objects that have methods. But numbers, strings, and boolean values behave as if they had methods (§3.6 explains how this works). In JavaScript, null and undefined are the only values that methods cannot be invoked on.
JavaScript’s types can be divided into primitive types and object types. And they can be divided into types with methods and types without. They can also be categorized as mutable and immutable types. A value of a mutable type can change. Objects and arrays are mutable: a JavaScript program can change the values of object properties and array elements. Numbers, booleans, null, and undefined are immutable—it doesn’t even make sense to talk about changing the value of a number, for example. Strings can be thought of as arrays of characters, and you might expect them to be mutable. In Java-Script, however, strings are immutable: you can access the text at any index of a string,
but JavaScript provides no way to alter the text of an existing string. The differences between mutable and immutable values are explored further in §3.7.
JavaScript converts values liberally from one type to another. If a program expects a string, for example, and you give it a number, it will automatically convert the number to a string for you. If you use a nonboolean value where a boolean is expected, JavaScript will convert accordingly. The rules for value conversion are explained in §3.8. Java-Script’s liberal value conversion rules affect its definition of equality, and the == equality operator performs type conversions as described in §3.8.1.
JavaScript variables are untyped: you can assign a value of any type to a variable, and you can later assign a value of a different type to the same variable. Variables are declared with the var keyword. JavaScript uses lexical scoping. Variables declared out-side of a function are global variables and are visible everywhere in a JavaScript program.
Variables declared inside a function have function scope and are visible only to code that appears inside that function. Variable declaration and scope are covered in §3.9 and §3.10.
3.1 Numbers
Unlike many languages, JavaScript does not make a distinction between integer values and floating-point values. All numbers in JavaScript are represented as floating-point values. JavaScript represents numbers using the 64-bit floating-point format defined by the IEEE 754 standard,1 which means it can represent numbers as large as
±1.7976931348623157 × 10308 and as small as ±5 × 10−324.
The JavaScript number format allows you to exactly represent all integers between
−9007199254740992 (−253) and 9007199254740992 (253), inclusive. If you use integer values larger than this, you may lose precision in the trailing digits. Note, however, that certain operations in JavaScript (such as array indexing and the bitwise operators de-scribed in Chapter 4) are performed with 32-bit integers.
When a number appears directly in a JavaScript program, it’s called a numeric literal.
JavaScript supports numeric literals in several formats, as described in the following sections. Note that any numeric literal can be preceded by a minus sign (-) to make the number negative. Technically, however, - is the unary negation operator (see Chap-ter 4) and is not part of the numeric liChap-teral syntax.
1. This format should be familiar to Java programmers as the format of the double type. It is also the double format used in almost all modern implementations of C and C++.
3.1 Numbers | 31
Core JavaScript
3.1.1 Integer Literals
In a JavaScript program, a base-10 integer is written as a sequence of digits. For example:
0 3 10000000
In addition to base-10 integer literals, JavaScript recognizes hexadecimal (base-16) val-ues. A hexadecimal literal begins with “0x” or “0X”, followed by a string of hexadecimal digits. A hexadecimal digit is one of the digits 0 through 9 or the letters a (or A) through f (or F), which represent values 10 through 15. Here are examples of hexadecimal in-teger literals:
0xff // 15*16 + 15 = 255 (base 10) 0xCAFE911
Although the ECMAScript standard does not support them, some implementations of JavaScript allow you to specify integer literals in octal (base-8) format. An octal literal begins with the digit 0 and is followed by a sequence of digits, each between 0 and 7.
For example:
0377 // 3*64 + 7*8 + 7 = 255 (base 10)
Since some implementations support octal literals and some do not, you should never write an integer literal with a leading zero; you cannot know in this case whether an implementation will interpret it as an octal or decimal value. In the strict mode of ECMAScript 5 (§5.7.3), octal literals are explicitly forbidden.
3.1.2 Floating-Point Literals
Floating-point literals can have a decimal point; they use the traditional syntax for real numbers. A real value is represented as the integral part of the number, followed by a decimal point and the fractional part of the number.
Floating-point literals may also be represented using exponential notation: a real num-ber followed by the letter e (or E), followed by an optional plus or minus sign, followed by an integer exponent. This notation represents the real number multiplied by 10 to the power of the exponent.
More succinctly, the syntax is:
[digits][.digits][(E|e)[(+|-)]digits]
For example:
3.14 2345.789
.333333333333333333
6.02e23 // 6.02 × 1023 1.4738223E-32 // 1.4738223 × 10−32
3.1.3 Arithmetic in JavaScript
JavaScript programs work with numbers using the arithmetic operators that the lan-guage provides. These include + for addition, - for subtraction, * for multiplica-tion, / for division, and % for modulo (remainder after division). Full details on these and other operators can be found in Chapter 4.
In addition to these basic arithmetic operators, JavaScript supports more complex mathematical operations through a set of functions and constants defined as properties of the Math object:
Math.pow(2,53) // => 9007199254740992: 2 to the power 53 Math.round(.6) // => 1.0: round to the nearest integer Math.ceil(.6) // => 1.0: round up to an integer Math.floor(.6) // => 0.0: round down to an integer Math.abs(-5) // => 5: absolute value
Math.max(x,y,z) // Return the largest argument Math.min(x,y,z) // Return the smallest argument
Math.random() // Pseudo-random number x where 0 <= x < 1.0 Math.PI // π: circumference of a circle / diameter Math.E // e: The base of the natural logarithm Math.sqrt(3) // The square root of 3
Math.pow(3, 1/3) // The cube root of 3
Math.sin(0) // Trigonometry: also Math.cos, Math.atan, etc.
Math.log(10) // Natural logarithm of 10 Math.log(100)/Math.LN10 // Base 10 logarithm of 100 Math.log(512)/Math.LN2 // Base 2 logarithm of 512 Math.exp(3) // Math.E cubed
See the Math object in the reference section for complete details on all the mathematical functions supported by JavaScript.
Arithmetic in JavaScript does not raise errors in cases of overflow, underflow, or divi-sion by zero. When the result of a numeric operation is larger than the largest repre-sentable number (overflow), the result is a special infinity value, which JavaScript prints as Infinity. Similarly, when a negative value becomes larger than the largest repre-sentable negative number, the result is negative infinity, printed as -Infinity. The in-finite values behave as you would expect: adding, subtracting, multiplying, or dividing them by anything results in an infinite value (possibly with the sign reversed).
Underflow occurs when the result of a numeric operation is closer to zero than the smallest representable number. In this case, JavaScript returns 0. If underflow occurs from a negative number, JavaScript returns a special value known as “negative zero.”
This value is almost completely indistinguishable from regular zero and JavaScript programmers rarely need to detect it.
Division by zero is not an error in JavaScript: it simply returns infinity or negative infinity. There is one exception, however: zero divided by zero does not have a well-defined value, and the result of this operation is the special not-a-number value, printed as NaN. NaN also arises if you attempt to divide infinity by infinity, or take the square
3.1 Numbers | 33
Core JavaScript
root of a negative number or use arithmetic operators with non-numeric operands that cannot be converted to numbers.
JavaScript predefines global variables Infinity and NaN to hold the positive infinity and not-a-number value. In ECMAScript 3, these are read/write values and can be changed.
ECMAScript 5 corrects this and makes the values read-only. The Number object defines alternatives that are read-only even in ECMAScript 3. Here are some examples:
Infinity // A read/write variable initialized to Infinity.
Number.POSITIVE_INFINITY // Same value, read-only.
1/0 // This is also the same value.
Number.MAX_VALUE + 1 // This also evaluates to Infinity.
Number.NEGATIVE_INFINITY // These expressions are negative infinity.
-Infinity
-1/0 -Number.MAX_VALUE - 1
NaN // A read/write variable initialized to NaN.
Number.NaN // A read-only property holding the same value.
0/0 // Evaluates to NaN.
Number.MIN_VALUE/2 // Underflow: evaluates to 0 -Number.MIN_VALUE/2 // Negative zero
-1/Infinity // Also negative 0 -0
The not-a-number value has one unusual feature in JavaScript: it does not compare equal to any other value, including itself. This means that you can’t write x == NaN to determine whether the value of a variable x is NaN. Instead, you should write x != x. That expression will be true if, and only if, x is NaN. The function isNaN() is similar. It returns true if its argument is NaN, or if that argument is a non-numeric value such as a string or an object. The related function isFinite() returns true if its argument is a number other than NaN, Infinity, or -Infinity.
The negative zero value is also somewhat unusual. It compares equal (even using Java-Script’s strict equality test) to positive zero, which means that the two values are almost indistinguishable, except when used as a divisor:
var zero = 0; // Regular zero var negz = -0; // Negative zero
zero === negz // => true: zero and negative zero are equal 1/zero === 1/negz // => false: infinity and -infinity are not equal
3.1.4 Binary Floating-Point and Rounding Errors
There are infinitely many real numbers, but only a finite number of them (18437736874454810627, to be exact) can be represented exactly by the JavaScript floating-point format. This means that when you’re working with real numbers in JavaScript, the representation of the number will often be an approximation of the actual number.
The IEEE-754 floating-point representation used by JavaScript (and just about every other modern programming language) is a binary representation, which can exactly represent fractions like 1/2, 1/8, and 1/1024. Unfortunately, the fractions we use most commonly (especially when performing financial calculations) are decimal fractions 1/10, 1/100, and so on. Binary floating-point representations cannot exactly represent numbers as simple as 0.1.
JavaScript numbers have plenty of precision and can approximate 0.1 very closely. But the fact that this number cannot be represented exactly can lead to problems. Consider this code:
var x = .3 - .2; // thirty cents minus 20 cents var y = .2 - .1; // twenty cents minus 10 cents
x == y // => false: the two values are not the same!
x == .1 // => false: .3-.2 is not equal to .1 y == .1 // => true: .2-.1 is equal to .1
Because of rounding error, the difference between the approximations of .3 and .2 is not exactly the same as the difference between the approximations of .2 and .1. It is important to understand that this problem is not specific to JavaScript: it affects any programming language that uses binary floating-point numbers. Also, note that the values x and y in the code above are very close to each other and to the correct value.
The computed values are adequate for almost any purpose: the problem arises when we attempt to compare values for equality.
A future version of JavaScript may support a decimal numeric type that avoids these rounding issues. Until then you might want to perform critical financial calculations using scaled integers. For example, you might manipulate monetary values as integer cents rather than fractional dollars.
3.1.5 Dates and Times
Core JavaScript includes a Date() constructor for creating objects that represent dates and times. These Date objects have methods that provide an API for simple date com-putations. Date objects are not a fundamental type like numbers are. This section presents a quick tutorial on working with dates. Full details can be found in the refer-ence section:
var then = new Date(2010, 0, 1); // The 1st day of the 1st month of 2010 var later = new Date(2010, 0, 1, // Same day, at 5:10:30pm, local time 17, 10, 30);
var now = new Date(); // The current date and time
var elapsed = now - then; // Date subtraction: interval in milliseconds later.getFullYear() // => 2010
later.getMonth() // => 0: zero-based months later.getDate() // => 1: one-based days
later.getDay() // => 5: day of week. 0 is Sunday 5 is Friday.
later.getHours() // => 17: 5pm, local time
later.getUTCHours() // hours in UTC time; depends on timezone
3.1 Numbers | 35
Core JavaScript
later.toString() // => "Fri Jan 01 2010 17:10:30 GMT-0800 (PST)"
later.toUTCString() // => "Sat, 02 Jan 2010 01:10:30 GMT"
later.toLocaleDateString() // => "01/01/2010"
later.toLocaleTimeString() // => "05:10:30 PM"
later.toISOString() // => "2010-01-02T01:10:30.000Z"; ES5 only
3.2 Text
A string is an immutable ordered sequence of 16-bit values, each of which typically represents a Unicode character—strings are JavaScript’s type for representing text. The length of a string is the number of 16-bit values it contains. JavaScript’s strings (and its arrays) use zero-based indexing: the first 16-bit value is at position 0, the second at position 1 and so on. The empty string is the string of length 0. JavaScript does not have a special type that represents a single element of a string. To represent a single 16-bit value, simply use a string that has a length of 1.