This chapter collects a variety of advanced ActionScript programming
techniques and issues.
15.1. Copying, Comparing, and Passing Data
There are three fundamental ways to
manipulate data. We may copy it (e.g., assign
the value of variable x to variable
y), we may compare it (e.g.,
check whether x equals y), and
we may 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. The following lines of code
would, hence, cause the string "Dave" to be stored twice
in memory, once in the memory space reserved for
name1 and again in the space 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 space
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.
Composite data is, hence, 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, only one copy
of the data exists and all the variables point to it. If the value of
the data changes, all the variables are updated.
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 in the Output window
trace(y); // Displays 12 in the Output window
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 data 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 x to
y:
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.
Primitive and composite data are also
treated differently when passed to functions, as discussed under
Section 9.8.3, "Primitive Versus Composite Parameter Values" in Chapter 9, "Functions".
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 it within the function
don't affect its original value. 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).