Table of Contents

3.6 Copying, Comparing, and Passing Data

There are three fundamental ways to manipulate data; we can copy it (e.g., assign the value of variable x to variable y), compare it (e.g., check whether x equals y), and pass it (e.g., supply a variable to a function as an argument). Primitive data values are copied, compared, and passed quite differently than composite data. When primitive data is copied to a variable, that variable gets its own unique and private copy of the data, stored separately in memory. Hence, the following lines of code cause the string "Dave" to be stored twice in memory, once in the memory location reserved for name1 and again in the location reserved for name2:

name1 = "Dave";
name2 = name1;

We say that primitive data is copied by value because the data's literal value is stored in the memory location allotted to the variable. In contrast, when composite data is copied to a variable, only a reference to the data (and not the actual data) is stored in the variable's memory slot. That reference tells the interpreter where the actual data is kept (i.e., its address in memory). When a variable that contains composite data is copied to another variable, it is the reference (often called a pointer) and not the data itself that is copied. Hence, composite data is said to be copied by reference.

This makes good design sense, because it would be grossly inefficient to duplicate large arrays and other composite datatypes, but it has important consequences for our code. When multiple variables are assigned the same piece of composite data as their value, each variable does not store a unique copy of the data (as it would if the data were primitive). Rather, multiple variables can point to one copy of the composite data. If the value of the data changes, all the variables that point to it reflect the updated value.

Let's see how this affects a practical application. When two variables refer to the same primitive data, each variable gets its own copy of the data. Here we assign the value 12 to the variable x:

var x = 12;

Now let's assign the value of x to a new variable, y:

var y = x;

As you can guess, y is now equal to 12. But y has its own copy of the value 12, distinct from the copy in x. If we change the value of x, the value of y is unaffected:

x = 15;
trace(x); // Displays: 15
trace(y); // Displays: 12

The value of y did not change when x changed because when we assigned x to y, y received its own copy of the number 12 (i.e., the primitive datum contained by x).

Now let's try the same thing with composite data. We'll create a new array with three elements and then assign that array to the variable x:

var x = ["first element", 234, 18.5];

Now, just as we did before, we'll assign the value of x to y:

var y = x;

The value of y is now the same as the value of x. But what is the value of x ? Remember that because x refers to an array, which is a composite datum, the value of x is not literally the array ["first element", 234, 18.5] but merely a reference to that datum. Hence, when we assign x to y, what's copied to y is not the array itself but the reference contained in x that points to the array. So, both x and y point to the same array, stored somewhere in memory.

If we change the array through the variable x, like this:

x[0] = "1st element";

the change is also reflected in y:

trace(y[0]);  // Displays: "1st element"

Similarly, if we modify the array through y, the change can be seen via x:

y[1] = "second element";
trace (x[1]);  // Displays: "second element"

To break the association, use the slice( ) function to create an entirely new array:

var x = ["first element", 234, 18.5];
// Copy each element of x to a new array stored in y
var y = x.slice(0);
y[0] = "hi there";
trace(x[0]);  // Displays: "first element" (not "hi there")
trace(y[0]);  // Displays: "hi there" (not "first element")

Let's extend our example to see how primitive and composite data values are compared. Here we assign x and y an identical primitive value; then we compare the two variables:

x = 10;
y = 10;
trace(x =  = y);  // Displays: true

Because x and y contain primitive data, they are compared by value. In a value-based comparison, data is compared literally. The number 10 in x is considered equal to the number 10 in y because the numbers are made up of the same bytes.

Now, let's assign x and y identical versions of the same composite data and compare the two variables again:

x = [10, "hi", 5];
y = [10, "hi", 5];
trace(x =  = y);  // Displays: false

This time, x and y contain composite data, so they are compared by reference. The arrays we assigned to x and y have the same byte values, but the variables x and y are not equal because they do not store a reference to the same composite datum. However, watch what happens when we copy the reference in y to x:

x = y;
trace(x =  = y);  // Displays: true

Now that the references are the same, the values are considered equal. Thus, the result of the comparison depends on the references in the variables, not the actual byte values of the arrays to which they point.

Primitive and composite data are also treated differently when passed to functions, as discussed under Section 9.8.3 in Chapter 9. Most notably, when a primitive variable is passed as an argument to a function, any changes to the datum within the function are not reflected in the original variable. However, when passing a composite variable, changes within the function do affect the original variable. That is, if you pass an integer variable x to a function, changes to the parameter within the function don't affect its original value in the calling routine. But if you pass an array y to a function, any changes to that array within the function will alter the original value of y outside the function (because changes to the array affect the data to which y points).


Table of Contents