3.15. By Value Versus by ReferenceIn JavaScript, as in all programming languages, you can manipulate a data value in three important ways.[*] First, you can copy it. For example, you might assign it to a new variable. Second, you can pass it as an argument to a function or method. Third, you can compare it with another value to see whether the two values are equal. To understand any programming language, you must understand how these three operations are performed in that language.
There are two fundamentally distinct ways to manipulate data values. These techniques are called by value and by reference. When a datum is manipulated by value, it is the value of the datum that matters. In an assignment, a copy of the actual value is made, and that copy is stored in a variable, object property, or array element; the copy and the original are two totally independent values that are stored separately. When a datum is passed by value to a function, a copy of the datum is passed to the function; if the function modifies the value, the change affects only the function's copy of the datumit does not affect the original datum. Finally, when a datum is compared by value to another datum, the two distinct pieces of data must represent exactly the same value (which usually means that a byte-by-byte comparison finds them to be equal). The other way to manipulate a value is by reference. With this technique, there is only one actual copy of the value; references to that value are manipulated.[
These are two very different ways of manipulating values, and they have important implications that you should understand. Table 3-4 summarizes these implications. This discussion of manipulating data by value and by reference has been a general one, but the distinctions apply to all programming languages. The sections that follow explain how these distinctions apply specifically to JavaScript; they discuss which datatypes are manipulated by value and which are manipulated by reference.
3.15.1. Primitive Types and Reference TypesThe basic rule in JavaScript is this: primitive types are manipulated by value, and reference types, as the name suggests, are manipulated by reference. Numbers and booleans are primitive types in JavaScriptprimitive because they consist of nothing more than a small, fixed number of bytes that are easily manipulated at the low levels of the JavaScript interpreter. Objects, on the other hand, are reference types. Arrays and functions, which are specialized types of objects, are therefore also reference types. These datatypes can contain arbitrary numbers of properties or elements, so they cannot be manipulated as easily as fixed-size primitive values can. Since object and array values can become quite large, it doesn't make sense to manipulate these types by value, because this could involve the inefficient copying and comparing of large amounts of memory. What about strings? A string can have an arbitrary length, so it would seem that strings should be reference types. In fact, though, they are usually considered primitive types in JavaScript simply because they are not objects. Strings don't actually fit into the primitive-versus-reference type dichotomy. I'll explain more about strings and their behavior a little later. The best way to explore the differences between data manipulation by value and by reference is through example. Study the following examples carefully, paying attention to the comments. Example 3-1 copies, passes, and compares numbers. Since numbers are primitive types, this example illustrates data manipulation by value. Example 3-1. Copying, passing, and comparing by value
Now, consider Example 3-2. This example copies, passes, and compares an object. Since objects are reference types, these manipulations are performed by reference. This example uses Date objects, which you can read more about in Part III. Example 3-2. Copying, passing, and comparing by reference
Before we leave the topic of manipulating objects and arrays by reference, let's clear up a point of nomenclature. The phrase "pass by reference" can have several meanings. To some readers, the phrase refers to a function-invocation technique that allows a function to assign new values to its arguments and to have those modified values visible outside the function. This is not the way the term is used in this book. Here, I mean simply that a reference to an object or arraynot the object itselfis passed to a function. A function can use the reference to modify properties of the object or elements of the array. But if the function overwrites the reference with a reference to a new object or array, that modification is not visible outside the function. Readers familiar with the other meaning of this term may prefer to say that objects and arrays are passed by value, but the value that is passed is actually a reference rather than the object itself. Example 3-3 illustrates this issue. Example 3-3. References themselves are passed by value
3.15.2. Copying and Passing StringsAs mentioned earlier, JavaScript strings don't fit neatly into the primitive-versus-reference type dichotomy. Since strings are not objects, it is natural to assume that they are primitive. If they are primitive types, then by the rules given above, they should be manipulated by value. But since strings can be arbitrarily long, it would seem inefficient to copy, pass, and compare them byte by byte. Therefore, it would also be natural to assume that strings are implemented as reference types. Instead of making assumptions about strings, let's write some JavaScript code to experiment with string manipulation. If strings are copied and passed by reference, you should be able to modify the contents of a string through the reference stored in another variable or passed to a function. When setting out to write the code to perform this experiment, however, you'll run into a major stumbling block: there is no way to modify the contents of a string. The charAt( ) method returns the character at a given position in a string, but there is no corresponding setCharAt( ) method. This is not an oversight. JavaScript strings are intentionally immutablethat is, there is no JavaScript syntax, method, or property that allows you to change the characters in a string. Since strings are immutable, the original question is moot: there is no way to tell whether strings are passed by value or by reference. You can assume that, for efficiency, JavaScript is implemented so that strings are passed by reference, but in actuality it doesn't matter, because it has no practical bearing on the code you write. 3.15.3. Comparing StringsDespite the fact that you cannot determine whether strings are copied and passed by value or by reference, you can write JavaScript code to determine whether they are compared by value or by reference. Example 3-4 shows the code that can make this determination. Example 3-4. Are strings compared by value or by reference?
This experiment demonstrates that strings are compared by value. This may be surprising to some programmers. In C, C++, and Java, strings are reference types and are compared by reference. If you want to compare the actual contents of two strings, you must use a special method or function. JavaScript, however, is a higher-level language and recognizes that when you compare strings, you most often want to compare them by value. Thus, despite the fact that, for efficiency, JavaScript strings are (presumably) copied and passed by reference, they are compared by value. 3.15.4. By Value Versus by Reference: SummaryTable 3-5 summarizes the way that the various JavaScript types are manipulated.
|