This chapter discusses in detail the capabilities of JavaScript’s built-in objects, particularly Array, Date, and Math. We will also look into the built-in objects related to the primitive types, such as Boolean, Number, and String, as well as the mysterious Global object. Notably missing from this chapter is the RegExp object, which requires a significant amount of explanation and is the subject of the next chapter. For each object covered in this chapter, the focus will be primarily on those properties most commonly used and supported by the major browsers. The complete list of properties of the built-in objects, including version information, can be found in Appendix B. So let’s start our overview of these built-in objects, proceeding in alphabetical order, starting from Array and ending in String.
Arrays were introduced in Chapter 3 as composite types that store ordered lists of data. Arrays may be declared using the Array() constructor. If arguments are passed to the constructor, they are usually interpreted as specifying the elements of the array. The exception is when the constructor is passed a single numeric value that creates an empty array, but sets the array’s length property to the given value. Three examples of array declaration are
var firstArray = new Array(); var secondArray = new Array("red", "green", "blue"); var thirdArray = new Array(5);
The first declaration creates an empty array called firstArray. The second declaration creates a new array secondArray with the first value equal to “red,” the second value equal to “green,” and the last value equal to “blue.” The third declaration creates a new empty array thirdArray whose length property has value 5. There is no particular advantage to using this last syntax, and it is rarely used in practice.
JavaScript 1.2+ allows you to create arrays using array literals. The following declarations are functionally equivalent to those of the previous example:
var firstArray = []; var secondArray = ["red", "green", "blue"]; var thirdArray = [,,,,];
The first two declarations should not be surprising, but the third looks rather odd. The given literal has four commas, but the values they separate seem to be missing. The interpreter treats this example as specifying five undefined values and sets the array’s length to 5 to reflect this. Sometimes you will see a sparse array with such a syntax:
var fourthArray = [,,35,,,16,,23,];
Fortunately, most programmers stay away from this last array creation method, as it is troublesome to count numerous commas.
The values used to initialize arrays need not be literals. The following example is perfectly legal and in fact very common:
var x = 2.0, y = 3.5, z = 1; var myValues = [x, y, z];
Accessing the elements of an array is done using the array name with square brackets and a value. For example, we can define a three-element array like so:
var myArray = [1,51,68];
Given that arrays in JavaScript are indexed beginning with zero, to access the first element we would specify myArray[0]. The following shows how the various elements in the last array could be accessed:
var x = myArray[0]; var y = myArray[1]; var z = myArray[2];
However, you need to be careful when accessing an element of an array that is not set. For example,
alert(myArray[35]);
results in the display of an undefined value, since this array element is obviously not set. However, if we wanted to set this array element, doing so is quite straightforward.
The nice thing about JavaScript arrays, unlike those in many other programming languages, is that you don’t have to allocate more memory explicitly as the size of the array grows. For example, to add a fourth value to myArray, you would use
myArray[3] = 57;
You do not have to set array values contiguously (one after the other), so
myArray[11] = 28;
is valid as well. However, in this case you start to get a sparsely populated array, as shown by the dialog here that displays the current value of myArray:
Modifying the values of an array is just as easy. To change the second value of the array, just assign it like this:
myArray[1] = 101;
Of course, when setting array values, you must remember the distinction between reference and primitive types made in previous chapters. In particular, recall that when you manipulate a variable that has been set equal to a reference type, it modifies the original value as well. For example, consider the following:
var firstarray = ["Mars", "Jupiter", "Saturn"] var secondarray = firstarray; secondarray[0] = "Neptune"; alert(firstarray);
You’ll notice, as shown here, that the value in firstArray was changed!
This aspect of reference types is very useful, particularly in the case of parameter passing to functions.
Array elements can be removed using the delete operator. This operator sets the array element it is invoked on to undefined but does not change the array’s length (more on this in a moment). For example,
var myColors = ["red", "green", "blue"]; delete myColors[1]; alert("The value of myColors[1] is: " + myColors[1]);
results in
The effect is as if no element had ever been placed at that index. However, the size of the array is actually still three, as shown when you alert the entire array’s contents:
We can also verify the array hasn’t shrunk by accessing its length property, the details of which are discussed next.
The length property retrieves the index of the next available (unfilled) position at the end of the array. Even if some lower indices are unused, length gives the index of the first available slot after the last element. Consider the following:
var myArray = new Array(); myArray[1000] = "This is the only element in the array"; alert(myArray.length);
Even though myArray only has one element at index 1000, as we see by the alert dialog myArray.length, the next available slot is at the end of the array, 1001.
Because of this characteristic of the length property, we suggest using array elements in order. Assigning values in a noncontiguous manner leads to arrays that have “holes” between indices holding defined values—the so-called “sparsely populated array” mentioned earlier. Because JavaScript allocates memory only for those array elements that actually contain data, this is not a problem in terms of wasting memory. It merely means that you have to be careful that the undefined values in the “holes” are not accidentally used.
The length property is automatically updated as new elements are added to the array. For this reason, length is commonly used to iterate through all elements of an array. The following example illustrates array iteration and also a problem that can arise when using an array with “holes”:
// define a variable to hold the result of the multiplication var result = 1; // define an array to hold the value multiplied var myValues = new Array(); // set the values myValues[0] = 2; myValues[2] = 3; // iterate through array multiplying each value for (var index = 0; index << myValues.length; index++) result = result * myValues[index]; alert("The value of result is: " + result);
As you can see from the result,
something went very wrong. The expected result was 6, but we ended up with a value that is not a number (NaN). What happened? The array iteration went as expected, but myValues[1] was never assigned a value and so remained undefined. Attempting to multiply undefined by a number results in NaN by JavaScript’s type conversion rules (see Chapter 3). The single undefined array element clobbered the entire computation.
Although the previous example is obviously contrived, using arrays with holes requires the programmer to exercise extra caution. We now present a “careful” version of the example, which gives the expected result:
var result = 1; var myValues = new Array(); myValues[0] = 2; myValues[2] = 3; for (var index = 0; index << myValues.length; index++) { // check if element is valid or not if (myValues[index] != undefined) result = result * myValues[index]; } alert("The value of result is: " + result);
The only difference with this script is that the multiplication has been placed inside of an if statement. The if statement checks each element for validity and ensures the proper behavior by skipping undefined values.
In addition to providing information, the length property can be set to perform certain functions. Any indices containing data that are greater than the value assigned to length are immediately reset to undefined. So, for example, to remove all elements from an array, you could set length to zero:
var myArray = ["red", "green", "blue"]; myArray.length = 0; alert("myArray="+myArray);
The assignment removes everything from the array by replacing the data at all indices with undefined, as if they had never been set. In this case you really aren’t going to see much:
Setting length to a value greater than the index of the last valid element has no effect on the array contents, though it will increase the number of undefined slots in the array. Consider, for example, the result of the following script,
var myArray = ["red", "green", "blue"]; myArray.length = 20; alert("myArray="+myArray);
which is shown here:
You shouldn’t bother setting the length property directly, since the result of extending an array is usually a sparsely populated array. However, deletion through this method is acceptable. For example, removing the last element in the array with this capability is a bit unwieldy:
myArray.length = myArray.length - 1;
Newer versions of JavaScript provide a better way to remove the last element with methods the Array object provides to simulate stacks and queues.
JavaScript 1.2+ and JScript 5.5+ provide methods for treating arrays like stacks and queues. For those readers unfamiliar with these abstract data types, a stack is used to store data in last-in first-out order, often called LIFO. That is, the first object placed in the stack is the last one retrieved when the stack is read. A queue is an abstract data type used to store data in first-in first-out order, also called FIFO. Data in a queue is retrieved in the order it was added.
A stack in the form of an array is manipulated using the push() and pop() methods. Calling push() appends the given arguments (in order) to the end of the array and increments the length property accordingly. Calling pop() removes the last element from the array, returns it, and decrements the length property by one. An example of using the properties is as follows. The contents of the array and any values returned are indicated in the comments.
var stack = []; // [] stack.push("first"); // ["first"] stack.push(10, 20); // ["first", 10, 20] stack.pop(); // ["first", 10] Returns 20 stack.push(2); // ["first", 10, 2] stack.pop(); // ["first", 10] Returns 2 stack.pop(); // ["first"] Returns 10 stack.pop(); // [] Returns "first"
Of course, you can use push() and pop() to add data to and remove data from the end of an array without thinking of it as an actual stack.
JavaScript also provides unshift() and shift() methods. These methods work as push() and pop() do, except that they add and remove data from the front of the array. Invoking unshift() inserts its arguments (in order) at the beginning of the array, shifts existing elements to higher indices, and increments the array’s length property accordingly. For example,
var myArray = [345, 78, 2]; myArray.unshift(4,"fun"); alert(myArray);
adds two more elements to the front of the array, as shown here:
Calling shift() removes the first element from the array, returns it, shifts the remaining elements down one index, and decrements length. You can think of shift() as shifting each element in the array down one index, causing the first element to be ejected and returned; so, given the previous example, if we called
myArray.shift();
we would end up with an array containing “fun,” 345, 78, and 2. As with pop(), invoking shift() on an array returns a value that can be used. For example, we could save the value shifted off the array into a variable:
var x = myArray.shift();
You can use push() and shift() to simulate a queue. The following example illustrates the principle. We place new data at the end of the array and retrieve data by removing the element at index zero. The contents of the array and any return values are indicated in the comments.
var queue = []; queue.push("first", 10); // ["first", 10] queue.shift(); // [10] Returns "first" queue.push(20); // [10, 20] queue.shift(); // [20] Returns 10 queue.shift(); // [] Returns 20
Even if you never use arrays as stacks or queues, the methods discussed in this section can come in handy to manipulate the contents of arrays. Now let’s look at a few more useful array manipulations.
Note |
As mentioned at the start of the chapter, these methods require JavaScript 1.2 or JScript 5.5 or better. Internet Explorer 5 and earlier will not be able to natively use these features. However, using an Array prototype to add our own pop() and push() methods can fix this problem. See the section entitled “Extending Arrays with Prototypes,” later in this chapter. |
JavaScript provides a wealth of methods for carrying out common operations on arrays. This section provides an overview of these Array methods with a brief discussion of some of their quirks.
The concat() method returns the array resulting from appending its arguments to the array on which it was invoked. Given the script:
var myArray = ["red", "green", "blue"]; alert(myArray.concat("cyan", "yellow"));
the expected larger array is shown here:
Be careful, though; concat() does not modify the array in place. Notice the output of this script,
var myArray = ["red", "green", "blue"]; myArray.concat("cyan", "yellow"); alert(myArray);
which is shown here:
Unlike with the push() and shift() methods discussed earlier, you will need to save the returned value; for example:
var myArray = ["red", "green", "blue"]; myArray = myArray.concat("cyan", "yellow");
If any argument to concat() is itself an array, it is flattened into array elements. This flattening is not recursive, so an array argument that contains an array element has only its outer array flattened. An example illustrates this behavior more clearly:
var myArray = ["red", "green", "blue"]; myArray.concat("pink", ["purple", "black"]); // Returns ["red", "green", "blue", "pink", "purple", "black"] myArray.concat("white", ["gray", ["orange", "magenta"]]); // Returns ["red", "green", "blue", "white", "gray", ["orange", "magenta"]] alert(myArray[myArray.length-1]); // shows orange, magenta
Note |
You may notice that arrays are recursively flattened if you output the entire array with an alert. However, access the length property or the individual elements and it will become apparent that you have nested arrays. |
The join() method of JavaScript 1.1+ and JScript 2.0+ converts the array to a string and allows the programmer to specify how the elements are separated in the resulting string. Typically, when you print an array, the output is a comma-separated list of the array elements. You can use join() to format the list separators as you’d like:
var myArray = ["red", "green", "blue"]; var stringVersion = myArray.join(" / "); alert(stringVersion);
One important thing to note is that the join() method will not destroy the array as a side-effect of returning the joined string of its elements. You could obviously do this, if you like, by overriding the type of the object. For example:
var myArray = ["red", "green", "blue"]; myArray = myArray.join(" / ");
The join() method is the inverse of the split() method of the String object.
JavaScript 1.1+ and JScript 2.0+ also allow you to reverse the elements of the array in place. The reverse() method, as one might expect, reverses the elements of the array it is invoked on:
var myArray = ["red", "green", "blue"]; myArray.reverse(); alert(myArray);
The slice() method of Array (supported since JavaScript 1.2+ and JScript 3.0) returns a “slice” (subarray) of the array on which it is invoked. As it does not operate in place, the original array is unharmed. The method takes two arguments, the start and end index, and returns an array containing the elements from index start up to but not including index end. If only one argument is given, the method returns the array composed of all elements from that index to the end of the array. Note that start and end are allowed to take on negative values. When negative, these values are interpreted as an offset from the end of the array. For example, calling slice (-2) returns an array containing the last two elements of the array. These examples show slice() in action:
var myArray = [1, 2, 3, 4, 5]; myArray.slice(2); // returns [3, 4, 5] myArray.slice(1, 3); // returns [2, 3] myArray.slice(-3); // returns [3, 4, 5] myArray.slice(-3, -1); // returns [3, 4] myArray.slice(-4, 3); // returns [2, 3] myArray.slice(3, 1); // returns []
The splice() method, available in JavaScript 1.2+ and JScript 5.5+, can be used to add, replace, or remove elements of an array in place. Any elements that are removed are returned. It takes a variable number of arguments, the first of which is mandatory. The syntax could be summarized as
splice(start, deleteCount, replacevalues);
The first argument start is the index at which to perform the operation. The second argument is deleteCount, the number of elements to delete beginning with index start. Any further arguments represented by replacevalues (that are comma-separated, if more than one) are inserted in place of the deleted elements.
var myArray = [1, 2, 3, 4, 5]; myArray.splice(3,2,''a'',''b''); // returns 4,5 [1,2,3,''a'',''b''] myArray.splice(1,1,"in","the","middle"); // returns 2 [1,"in","the","middle",3,''a'',''b'']
The toString() method returns a string containing the comma-separated values of the array. This method is invoked automatically when you print an array. It is equivalent to invoking join() without any arguments. It is also possible to return a localized string using toLocaleString() where the separator may be different given the locale of the browser running the script. However, in most cases, this method will return the same value as toString().
Note |
Netscape 4 has an unfortunate bug. When the <<script>> tag has the attribute language="JavaScript1.2", this method includes square brackets in the returned string. Under normal circumstances, the following code var myArray = [1, [2, 3]]; var stringVersion = myArray.toString(); places “1,2,3” in stringVersion. But because of the aforementioned bug, under Netscape 4 with the "JavaScript1.2" language attribute, the value “[1, [2, 3]]” is assigned to stringVersion. |
The creation of a string that preserves square brackets is available through the toSource() method as of JavaScript 1.3. This allows you to create a string representation of an array that can be passed to the eval() function to be used as an array later on. The eval() function is discussed in the section entitled “Global” later in this chapter.
One of the most useful Array methods is sort(). Supported since JavaScript 1.1 and JScript 2.0, the sort() works much like the qsort() function in the standard C library. By default, it sorts the array elements in place according to lexicographic order. It does this by first converting the array elements to string and then sorting them lexiographically. This can cause an unexpected result. Consider the following:
var myArray = [14,52,3,14,45,36]; myArray.sort(); alert(myArray);
If you run this script, you will find that, according to this JavaScript sort, 3 is larger than 14! You can see the result here:
The reason for this result is that, from a string ordering perspective, 14 is smaller than 3. Fortunately, the sort function is very flexible and we can fix this. If you want to sort on a different order, you can pass sort() a comparison function that determines the order of your choosing. This function should accept two arguments and return a negative value if the first argument should come before the second in the ordering. (Think: the first is “less” than the second.) If the two elements are equal in the ordering, it should return zero. If the first argument should come after the second, the function should return a positive value. (Think: the first is “greater” than the second.) For example, if we wished to perform a numerical sort, we might write a function like the following.
function myCompare(x, y) { if (x << y) return -1; else if (x === y) return 0; else return 1; }
Then we could use the function in the previous example:
var myArray = [14,52,3,14,45,36]; myArray.sort(myCompare); alert(myArray);
Here we get the result that we expect:
If you want to be more succinct, you can use an anonymous function, as described in Chapter 5. Consider this example, which sorts odd numbers before evens:
var myArray = [1,2,3,4,5,6]; myArray.sort( function(x, y) { if (x % 2) return -1; if (x % 2 == 0) return 1; } ); alert(myArray);
The result is shown here:
Note that we could make this example more robust by including code that ensures that the even and odd values are each sorted in ascending order.
Although not explicitly included in the language, most JavaScript implementations support a form of multidimensional arrays. A multidimensional array is an array that has arrays as its elements. For example,
var tableOfValues = [[2, 5, 7], [3, 1, 4], [6, 8, 9]];
defines a two-dimensional array. Array elements in multidimensional arrays are accessed as you might expect, by using a set of square brackets to indicate the index of the desired element in each dimension. In the previous example, the number 4 is the third element of the second array and so is addressed as tableOfValues[1][2]. Similarly, 7 is found at tableOfValues[0][2], 6 at tableOfValues[2][0], and 9 at tableOfValues[2][2].
In JavaScript, all non-primitive data is derived from the Object object, which was discussed in the previous chapter. We should recall that because of this fact we could add new methods and properties to any object we like through object prototypes. For example, we could add a special display() method to arrays that alerts the user as to the array contents.
function myDisplay() { if (this.length != 0) alert(this.toString()); else alert("The array is empty"); } Array.prototype.display = myDisplay;
We could then print out the value of arrays using our new display() method, as illustrated here:
var myArray = [4,5,7,32]; myArray.display(); // displays the array values var myArray2 = []; myArray2.display(); // displays the string "The array is empty"
By using prototypes, we can “fix” the lack of pop() and push() methods in pre-Internet Explorer 5.5 browsers. For example, to add the pop() method in older browsers or override safely the built-in pop() in newer browsers, we would use
function myPop() { if (this.length != 0) { var last = this[this.length-1]; this.length--; return last; } } Array.prototype.pop = myPop;
Our own implementation of push() is only slightly more complicated and is shown here:
function myPush() { var numtopush = this.push.arguments.length; var arglist = this.push.arguments; if (numtopush >> 0) { for (var i=0; i << numtopush; i++) { this.length++; this[this.length-1] = arguments[i]; } } } Array.prototype.push = myPush;
We can see that mastery of the ideas from the previous chapter really can come in handy! While our own functions could be used to resolve issues with older browsers, don’t think the use of prototypes will solve all your problems with arrays in early versions of JavaScript. Serious deficiencies in array implementations of JavaScript, such as in Netscape 2, probably can’t be fixed by prototypes since they may also be lacking. However, if you want to add push() and pop() support to Internet Explorer 4 or Netscape 3, you’ll find this code should do the trick.