11.2. By Value Versus by Reference
In JavaScript, as in
all programming languages, there are
three important ways that you can manipulate a data value. First, you
can copy it; for example, by assigning 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 if 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 value 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
datum -- it 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 of manipulating a value is by
reference. With this technique,
there is only one actual copy of the value; references to that value
are manipulated.[42] If a value is manipulated by reference, variables do not
hold that value directly; they hold only references to it. It is
these references that are copied, passed, and compared. So, in an
assignment made by reference, it is the reference to the value that
is assigned, not a copy of the value and not the value itself. After
the assignment, the new variable refers to the same value that the
original variable refers to. Both references are equally valid and
both can be used to manipulate the value -- if the value is
changed through one reference, that change also appears through the
original reference. The situation is similar when a value is passed
to a function by reference. A reference to the value is passed to the
function, and the function can use that reference to modify the value
itself; any such modifications are visible outside the function.
Finally, when a value is compared to another by reference, the two
references are compared to see if they refer to the same unique copy
of a value; references to two distinct values that happen to be
equivalent (i.e., consist of the same bytes) are not treated as
equal.
These are two very different ways of manipulating values, and they
have important implications that you should understand. Table 11-2 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 data types are
manipulated by value and which are manipulated by reference.
Table 11-2. By value versus by reference
|
By value
|
By reference
|
Copy
|
The value is actually
copied; there are two distinct,
independent copies.
|
Only a reference to the value is copied. If the value is modified
through the new reference, that change is also visible through the
original reference.
|
Pass
|
A distinct copy of the value is passed to the function; changes to it
have no effect outside the function.
|
A reference to the value is passed to the function. If the function
modifies the value through the passed reference, the modification is
visible outside the function.
|
Compare
|
Two distinct values are compared (often byte by byte) to see if they
are the same value.
|
Two references are compared to see if they refer to the same value.
Two references to distinct values are not equal, even if the two
values consist of the same bytes.
|
11.2.1. Primitive Types and Reference Types
The 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
JavaScript -- primitive because they consist of nothing more than
a small, fixed number of bytes that are easily manipulated at the low
(primitive) 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 data types 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, as 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 to be primitive types in JavaScript simply because they
are not objects. Strings don't actually fit into the primitive
versus reference type dichotomy. We'll have more to say 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 11-1
copies, passes, and
compares numbers. Since numbers are primitive types, this example
illustrates data manipulation by value.
Example 11-1. Copying, passing, and comparing by value
// First we illustrate copying by value
var n = 1; // Variable n holds the value 1
var m = n; // Copy by value: variable m holds a distinct value 1
// Here's a function we'll use to illustrate passing by value
// As we'll see, the function doesn't work the way we'd like it to
function add_to_total(total, x)
{
total = total + x; // This line changes only the internal copy of total
}
// Now call the function, passing the numbers contained in n and m by value.
// The value of n is copied, and that copied value is named total within the
// function. The function adds a copy of m to that copy of n. But adding
// something to a copy of n doesn't affect the original value of n outside
// of the function. So calling this function doesn't accomplish anything.
add_to_total(n, m);
// Now, we'll look at comparison by value.
// In the following line of code, the literal 1 is clearly a distinct numeric
// value encoded in the program. We compare it to the value held in variable
// n. In comparison by value, the bytes of the two numbers are checked to
// see if they are the same.
if (n == 1) m = 2; // n contains the same value as the literal 1; m is now 2
Now, consider Example 11-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 the core reference section of this book.
Example 11-2. Copying, passing, and comparing by reference
// Here we create an object representing the date of Christmas, 2001
// The variable xmas contains a reference to the object, not the object itself
var xmas = new Date(2001, 11, 25);
// When we copy by reference, we get a new reference to the original object
var solstice = xmas; // Both variables now refer to the same object value
// Here we change the object through our new reference to it
solstice.setDate(21);
// The change is visible through the original reference, as well
xmas.getDate( ); // Returns 21, not the original value of 25
// The same is true when objects and arrays are passed to functions.
// The following function adds a value to each element of an array.
// A reference to the array is passed to the function, not a copy of the array.
// Therefore, the function can change the contents of the array through
// the reference, and those changes will be visible when the function returns.
function add_to_totals(totals, x)
{
totals[0] = totals[0] + x;
totals[1] = totals[1] + x;
totals[2] = totals[2] + x;
}
// Finally, we'll examine comparison by reference.
// When we compare the two variables defined above, we find they are
// equal, because they refer to the same object, even though we were trying
// to make them refer to different dates:
(xmas == solstice) // Evaluates to true
// The two variables defined next refer to two distinct objects, both
// of which represent exactly the same date.
var xmas = new Date(2001, 11, 25);
var solstice_plus_4 = new Date(2001, 11, 25);
// But, by the rules of "compare by reference," distinct objects are not equal!
(xmas != solstice_plus_4) // Evaluates to true
Before we leave the topic of manipulating objects and arrays by
reference, we need to 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, we
mean simply that a reference to an object or
array -- not the object
itself -- is 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 of
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 11-3 illustrates this issue.
11.2.2. Copying and Passing Strings
As mentioned earlier, JavaScript
strings don't fit neatly into
the primitive type 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, suppose we write some
JavaScript code to experiment with string manipulation. If strings
are copied and passed by reference, we should be able to modify the
contents of a string through the reference stored in another variable
or passed to a function.
When
we set out to write the code to perform this experiment, however, we
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
immutable -- that is, there is no JavaScript
syntax, method, or property that allows you to change the characters
in a string.
Since strings are immutable, our original question is moot: there is
no way to tell if strings are passed by value or by reference. We can
assume that, for efficiency, JavaScript is implemented so that
strings are passed by reference, but in actuality it doesn't
matter, since it has no practical bearing on the code we write.
11.2.3. Comparing Strings
Despite the fact that we cannot determine
whether strings are copied and passed by value or by reference, we
can write JavaScript code to determine whether they are compared by
value or by reference. Example 11-4 shows the code we
might use to make this determination.
Example 11-4. Are strings compared by value or by reference?
// Determining whether strings are compared by value or reference is easy.
// We compare two clearly distinct strings that happen to contain the same
// characters. If they are compared by value they will be equal, but if they
// are compared by reference, they will not be equal:
var s1 = "hello";
var s2 = "hell" + "o";
if (s1 == s2) document.write("Strings compared by value");
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.
 |  |  | 11.
Further Topics in JavaScript |  | 11.3. Garbage Collection |
Copyright © 2003 O'Reilly & Associates. All rights reserved.
|