2.5 Variable Scope
Earlier we learned how to create variables and retrieve their values. But all our variables were attached to a single frame of the main timeline of a Flash document. When a document contains multiple
frames
and multiple movie clip
timelines
, variable creation and value retrieval becomes a little more complicated.
2.5.1 Timeline Variables
To
illustrate
the use of timeline variables, let's consider two simple scenarios.
2.5.1.1 Scenario 1: Accessing a value defined earlier on the same timeline
What happens when we define a variable in one frame of a timeline and try to access it later?
Suppose we create a variable,
x
, in frame 1 of the main timeline. After creating
x
, we set its value to 10:
var x;
x = 10;
Then, in the
next
frame (frame 2), we attach the following code:
trace(x);
When we play our movie, does anything appear in the Output window? We created our variable in frame 1, but we're attempting to retrieve its value in frame 2; does our variable still exist? Yes. The Output window displays 10.
|
When you define a variable on a timeline, that variable is accessible from all the other frames of that timeline.
|
|
2.5.1.2 Scenario 2: Accessing a value defined later on the same timeline
What happens if we try instead to access a variable before the frame in which it is assigned a value?
Suppose we add the following code to frame 30 of a movie's main timeline:
password = "let_me_in";
gotoAndStop(15);
and on frame 15 we add:
trace(
password
);
When the movie plays, we see the following in the Output window:
undefined
let_me_in
Here's why: when the movie's playhead
reaches
frame 15, Flash displays the value of
password
in the Output window. The first time through,
password
does not exist, so Flash displays
undefined
. However, when the playhead reaches frame 30,
password
is created and assigned the value "let_me_in". Then the playhead is sent back to frame 15 (
gotoAndStop(15)
),
causing
frame 15's code to execute again. This time, even though
password
is defined on a later frame than our
trace(
password
)
statement, it is still part of the same timeline; so, its value exists, and Flash displays it in the Output window.
|
Any variable declared on a timeline is available to all the scripts of its timeline for as long as that timeline exists. However, a variable's value is not defined until the script that
assigns
it a value is reached. Once the value of a variable is set, the value is
maintained
, even if the playhead
jumps
backward in the timeline. In other words, the value of a variable is determined by the order in which scripts are executed, as determined by the movement of the playhead, not the order of the frames to which scripts are attached on the timeline.
|
|
2.5.2 Variable Accessibility (Scope)
The two scenarios we just presented explore issues of
scope
. A variable's scope describes when and where the variable can be manipulated by the code in a movie. Scope defines a variable's life span and its accessibility to other blocks of code in our scripts. To determine a variable's scope, we must answer two questions: (a) how long does the variable exist? and (b) from where in our code can we set or retrieve the variable's value?
In traditional programming, variables are often broken into two general scope categories:
global
and
local
. Variables that are accessible throughout an entire program are called
global variables
. Variables that are accessible only to limited sections of a program are called
local variables
. In addition to these two conventional variable types, Flash adds
timeline variables
that are scoped to individual movie clip instances. Flash 5 supported conventional local variables but did not support true global variables; Flash MX introduces support for true global variables.
In Flash, all variables are scoped to one of the following:
-
A function (local variable)
-
The main timeline or a movie clip (timeline variable)
-
In Flash MX, the
_global
object (global variable)
2.5.3 Movie Clip Variables and Global Variables
As shown in the two earlier scenarios, a variable defined on a timeline is available to all the scripts on that timeline—from the first frame to the last frame. But what happens if we have more than one timeline in a movie, as described in Scenario 3?
2.5.3.1 Scenario 3: Variables declared on separate timelines
Suppose we have two basic geometric
shapes
, a
square
and a
circle
, defined as movie clip symbols. Recall that each movie clip maintains its own independent timeline.
On frame 1 of the
square
clip symbol, we set the variable
x
to 3:
var x;
x = 3;
On frame 1 of the
circle
clip symbol, we set the variable
y
to 4:
var y;
y = 4;
We place instances of those clips on frame 1, layer 1 of the main timeline, and we
name
our instances
square
and
circle
.
First question: if we attach the following code:
trace(x);
trace(y);
to frame 1 of the
main
movie timeline (upon which
square
and
circle
have been placed), what appears in the Output window when the movie plays?
Answer:
undefined
undefined
The variable
x
is defined on the
square
timeline, and
y
is defined on the
circle
timeline;
neither
are defined on our main timeline. Because Flash cannot find any variable named
x
or
y
on the main timeline, it displays
undefined
in the Output window.
|
Variables attached to a movie clip timeline (like that of
square
or
circle
) have scope limited to that timeline. They are not directly accessible to scripts on other timelines, such as our main movie timeline.
|
|
Second question: if we place the
trace(x)
and
trace(y)
statements on frame 1 of our
square
movie clip instead of frame 1 of our main movie timeline, what will appear in the Output window?
Answer: the value of
x
, which is 3, and the value of
y
,
undefined
.
3
undefined
The value of
x
is displayed as 3, because
x
is defined on the timeline of
square
and is therefore accessible to the
trace( )
command, which also resides on that timeline. But the value of
y
doesn't appear in the Output window, because
y
is defined in
circle
, which is a separate timeline.
In Flash, a variable attached to an individual timeline (i.e., any movie clip) is directly accessible to the scripts on that timeline only. Furthermore, two or more movie clips can legitimately define a variable with the same name. For example, we could create a variable named
x
in
circle
with a value of 25, even though there already is a variable named
x
in
square
with a value of 3. The result of
trace(x)
from
square
would be 3, but from
circle
it would be 25.
We refer to variables attached to timelines as
timeline variables
or
movie clip variables
. Now let's create a
global variable
that is directly accessible to all the scripts in a movie (any timeline). We'll put the following code on frame 1 of the
square
timeline, even though the code would have the same effect from any movie clip:
_global.day = "Monday";
Now we place this code on frame 1 of the main movie:
trace(day);
Because the
day
variable is global, Flash can find it from the main timeline, even though it was created in
square
, and the Output window displays:
Monday
As we'll see in Section 2.5.7, later in this chapter, when a timeline variable and a global variable both have the same name, the timeline variable takes precedence. For example, if we create a timeline variable,
day
, inside our
circle
movie clip, as
follows
:
var day = "Friday";
and then we check the value of
day
inside
circle
:
trace(day);
the Output window displays "Friday" (the timeline variable's value), not "Monday" (the global variable's value). For more information on global variables, see
_global
in the
Language Reference
.
Though true global variables were not supported in Flash 5, it was possible to simulate them using the
Object
class or
MovieClip
class. To create a variable that is available on all timelines in Flash 5, use the following statement:
MovieClip.prototype.
myGlobalVariable
=
myValue
;
For example:
MovieClip.prototype.msg = "Hello world";
Alternatively, assign the variable as a property of
Object
, as follows:
Object.msg = "Hello world";
Then, from any other timeline, access
msg
like this:
trace(Object.msg);
We discuss this technique (and the reason it works) in Section 12.5.3.5, in Chapter 12. In Flash MX, the
_global
object is the preferred way to store global variables.
2.5.4 Accessing Variables on Different Timelines
Even though variables on one timeline are not accessible directly to the scripts on other timelines, they are accessible indirectly. To create, retrieve, or assign a variable on a separate timeline, we use
dot syntax
, a standard notation common to object-oriented programming languages such as Java, C++, and JavaScript. Here's the generic dot syntax
phrasing
we use to address a variable on a separate timeline:
movieClipInstanceName
.
variableName
That is, we refer to a variable on another timeline using the name of the movie clip that contains the variable, followed by a dot, and finally the variable name itself. In our earlier scenario, for example, from the main timeline we can refer to the variable
x
in the
square
clip as:
square.x
Also from the main timeline, we can refer to the variable
y
in the
circle
clip as:
circle.y
A reference to a variable that includes the variable's location is known as a
qualified reference
. Qualified references tell Flash where to find the variable we're referring to; unqualified references require Flash to make assumptions about where the variable resides, as discussed later in Section 2.5.7. Hence, it's always good form to use qualified references to variables.
We can use qualified references from our main movie timeline to assign and retrieve variables in
square
like this:
square.z = 5; // Assign 5 to
z
in
square
var mainZ; // Create
mainZ
on the main timeline
mainZ = square.z; // Assign the value of
z
in
square
to
mainZ
However, using the
movieClipInstanceName.variableName
syntax, we can't refer to variables in
square
from our
circle
clip. If we put a reference to
square.x
on a frame in
circle
, the interpreter
tries
to find a clip called
square
inside
of
circle
, but
square
lives on the main timeline. So, we need a mechanism that lets us refer to the timeline that contains the
square
clip (in this case, the main timeline) from the
circle
clip. That mechanism comes in the form of two special properties:
_root
and
_parent
.
2.5.4.1 The _root and _ parent properties
The
_root
property is a direct reference to the main timeline of a movie. From any depth of nesting in a movie clip structure, we can always address variables on the main movie timeline using
_root
, like this:
_root.mainZ // Access the variable
mainZ
on the main timeline
_root.firstName // Access the variable
firstName
on the main timeline
We can even combine a reference to
_root
with references to movie clip instances, drilling down the nested structure of a movie in the process. For example, we can reference the variable
x
, inside the clip
square
that resides on the main movie timeline, as:
_root.square.x
This reference works from
anywhere
in our movie, no matter what the depth of clip nesting is, because the reference starts at our main movie timeline,
_root
. Here's another nested example showing how to access the variable
area
in the instance
triangle
that resides on the timeline of the instance
shapes
:
_root.shapes.triangle.area
Any reference to a variable that starts with the
_root
keyword is called an
absolute reference
or a
fully qualified reference
, because it describes the location of the variable in relation to a fixed, immutable point in our movie: the main timeline.
There are times, however, when we want to refer to variables on other timelines without referring to the main timeline of a movie. To do so, we use the
_parent
property, which refers to the timeline upon which the current movie clip instance resides. For example, from code attached to a frame of the clip
square
, we can refer to variables on the timeline that contains
square
using this syntax:
_parent.
myVariable
References that start with the keyword
_parent
are called
relative references
, because they are resolved relative to the location of the clip in which they occur.
Suppose we have a variable,
size
, defined on the main timeline of a movie. We place a clip instance named
shapes
on our main movie timeline, and on the
shapes
timeline we define the variable
color
. We also place a clip named
triangle
on the
shapes
timeline, as shown in Figure 2-1.
To display the value of the variable
color
(which is in the
shapes
clip) from code attached to the timeline of
triangle
, we could use an absolute reference starting at the main timeline, like this:
trace(_root.shapes.color);
But that ties our code to the main movie timeline. To make our code more flexible, we should instead use the
_parent
property to create a relative reference, like this:
trace(_parent.color);
Our first approach (using
_root
) works from the top down; it starts at the main timeline and descends through the movie clip hierarchy until it reaches the
color
variable. The second approach (using
_parent
) works from the bottom up; it starts with the clip that contains the
trace( )
statement (the
triangle
clip), and ascends one level up the clip structure, which happens to be the
shapes
clip, where it finds the
color
variable.
We can use
_parent
twice in a row, separated by a dot, to
ascend
the hierarchy of clips and access our
size
variable on the main timeline. Here we attach some code to
triangle
that refers to
size
on the main movie timeline:
trace(_parent._parent.size);
Using the
_parent
property twice in succession ascends two levels up the movie clip hierarchy, which in this context
brings
us to the main timeline of the movie.
Your approach to variable addressing will depend on what you want to happen when you place instances of a movie clip symbol on various timelines. In our
triangle
example, if we wanted our reference to
color
to always point to
color
as defined in the
shapes
clip, we would use the
_root
syntax, which gives us a fixed reference to
color
in
shapes
. But if we wanted our reference to
color
to refer to a different
color
variable, depending on which timeline held a given
triangle
instance, we would use the
_parent
syntax.
|
The meaning of
_root
changes when a
.swf
file is loaded into a movie clip. For example, suppose we load
myPictures.swf
into a movie clip in
container.swf
. When
myPictures.swf
runs independently,
_root
means the main timeline of the
myPictures.swf
file. But when
myPictures.swf
runs inside a movie clip of
container.swf
,
_root
means the main timeline of
container.swf
.
|
|
Flash MX makes this problem easy to solve using a global variable. To retain a consistent reference to the main timeline of a movie, even when the movie is loaded into a movie clip, place the following code on the main timeline:
_global.
movieName
MainTimeline = this;
For example:
_global.myPicturesMainTimeline = this;
This command creates a global reference to the main timeline, which allows you to refer to variables on the main timeline from any other timeline using:
myPicturesMainTimeline.
variableName
instead of:
_root.
variableName
2.5.4.2 Accessing variables on different document levels
The
_root
property refers to the main movie timeline of the current level (i.e., the current document), but the Flash Player can accommodate multiple documents in its
document stack
. The main timeline of any movie loaded in the Player document stack can be referenced using
_level
n
, where
n
is the number of the level on which the movie resides. Level numbering begins with 0, such as
_level0
,
_level1
,
_level2
,
_level3
, and so on. For information on loading multiple movies, see Chapter 13. Here are some examples of multiple-level variable references:
_level1.firstName //
firstName
on _level1's main timeline
_level4.ball.area //
area
in
ball
clip on _level4's main timeline
_level0.guestBook.email //
email
in
guestBook
clip on _level0's main timeline
|
When referring to variables across movie clip instances, make sure that you have named your clip instances using the <Instance Name> field in the Property inspector and referred to them by the same name in your code. Don't confuse the symbol name in the Library with the instance name on the Stage. If your instances are not named, your code cannot refer to them by name. Unnamed instances and
misspelled
instance
names
are extremely common sources of problems.
|
|
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}
Prior to introducing support for dot syntax in Flash 5, Flash supported an older "
path
" style syntax that used backslashes instead of dots. Flash MX still supports the legacy syntax, but you should update it to the more modern dot syntax as described in Table C-2 in Appendix C.
2.5.5 Movie Clip Variable Life Span
Earlier, we said that the scope of a variable answers two questions: (a) how long does the variable exist? and (b) from where in our code can we set or retrieve the variable's value? For movie clip variables, we have shown the factors involved in answering the second question. But we
skipped
answering the first question. Let's return to it now with one final variable-coding scenario.
2.5.5.1 Scenario 4: Life span of movie clip variables
Suppose we create a new movie with keyframes at frames 1, 2, and 3. On frame 1, we place a clip instance,
ball
. On the
ball
timeline, we create a variable,
radius
. Assume frame 3 of our main timeline is a blank keyframe (the
ball
instance is not present there).
From frame 2 of the main movie timeline, we can find out the value of
radius
using this code:
trace(ball.radius);
Here's the question: if we move this line of code from frame 2 to frame 3 of the main timeline, what appears in the Output window when our movie plays?
Answer:
undefined
. When the
ball
clip is removed from the main timeline at frame 3, all its variables are
destroyed
in the process.
|
Movie clip variables last only while the clip in which they reside is present on stage. Variables defined on the main timeline of a Flash document persist within each document but are lost if the document is unloaded from the Player (either via the
unloadMovie
( )
function or because another movie is loaded into the movie's level).
|
|
A variable's life span is important when scripting movies that contain movie clips placed across multiple frames on various timelines. Always make sure that any clip you're addressing is present on a timeline before you try to use the variables in that clip.
2.5.6 Local Variables
Movie clip variables are scoped to movie clips and persist as long as the movie clip on which they are defined exists. Sometimes, that's longer than we need them to live. For situations in which we need a variable only temporarily, ActionScript offers variables with
local
scope (i.e.,
local variables
), which live for a much shorter time than normal movie clip variables.
Local variables are used in functions. If you haven't worked with functions before, you should skip the rest of this section and come back to it once you've read Chapter 9.
Functions often
employ
variables that are needed only within the function. For example, suppose we have a function that displays the elements of an array:
function displayElements(theArray) {
var counter = 0;
while(counter < theArray.length) {
trace("Element " + counter + ": " + theArray[counter]);
counter++;
}
}
The
counter
variable is required to display the elements of the array, but it has no use thereafter. We could leave it defined on the timeline, but that's bad form for two reasons: (a) if
counter
persists, it takes up memory during the rest of our movie, and (b) if
counter
is accessible outside our function, it may conflict with other variables named
counter
. We would, therefore, like
counter
to die after the
displayElements( )
function has finished.
To cause
counter
to be deleted automatically at the end of our function, we define it as a
local variable
. Unlike movie clip variables, local variables are automatically
marked
for
deallocation
(removal from memory) by the interpreter when the function that defines them finishes.
To specify that a variable should be local, declare it with the
var
keyword from inside your function, as in the
preceding
displayElements( )
example.
Take heed, though; when placed
outside
of a function, the
var
statement creates a normal timeline variable, not a local variable. As shown in Example 2-4, the location of the
var
statement makes all the difference.
Variables within functions need not be local. We can create or change a movie clip variable from inside a function by omitting the
var
keyword. If we do not use the
var
keyword, but instead simply assign a value to a variable from within a function, Flash treats that variable as a
nonlocal
variable under some conditions. Consider this variable assignment inside a function:
function setHeight () {
height = 10;
}
The effect of the statement
height
=
10;
depends on whether
height
is a local variable or movie clip variable. If
height
is a previously declared local variable (which it is not in the example at hand), the statement
height
=
10;
simply modifies the local variable's value. If there is no local variable named
height
, as in this case, the interpreter creates a movie clip (nonlocal) variable named
height
and sets its value to 10. As a nonlocal variable,
height
persists even after the function finishes.
Example 2-4
demonstrates
local and nonlocal variable usage.
Example 2-4. Local and nonlocal variables
var x = 5; // New nonlocal variable,
x
, is now 5
function variableDemo () {
x = 10; // Nonlocal variable,
x
, is now 10
y = 20; // New nonlocal variable,
y
, is now 20
var z = 30; // New local variable,
z
, is now 30
trace(x + "," + y + "," + z); // Display values in Output window
}
variableDemo(); // Call our function. Displays: 10,20,30
// Now check the values of
x
,
y
, and
z
after the function has finished.
trace(x); // Displays: 10 (reassignment in our function was permanent)
trace(y); // Displays: 20 (nonlocal variable,
y
, still exists)
trace(z); // Displays "undefined" in Flash MX or nothing in Flash 5
Note that it is possible (though confusing and ill-advised) to have both a local and a nonlocal variable that share the same name within a script but have different scopes. Example 2-5 shows such a case.
Example 2-5. Local and nonlocal variables with the same name
var myColor = "blue";
function hexRed () {
var myColor = "#FF0000";
return myColor;
}
trace(hexRed()); // Displays: #FF0000 (the local variable
myColor
)
trace(myColor); // Displays: "blue" (setting the local variable,
//
myColor
, to #FF0000 did not affect the nonlocal version)
2.5.7 The Scope Chain
Internally, Flash resolves each unqualified variable reference in our code using a
scope chain
, which is simply a hierarchy of places (scopes) to look when a reference to a variable is
resolved
(i.e., when the correct variable is located unambiguously and its value is retrieved). Whenever Flash encounters a variable in a script or function, it seeks the variable's value in the scope chain associated with that code. The hunt for the variable ends when the variable is found somewhere in the scope chain or when it is not found at all (the variable is
undefined
.) The scope chain for a frame script (code attached directly to a movie clip keyframe) includes two scopes, which are traditionally read from the bottom up:
When a variable is referenced in a frame script, Flash checks the current movie clip for the variable. If the variable is not found in the movie clip (or its prototype), Flash checks for a global variable of that name. If no global variable is found, Flash returns
undefined
. When both a movie clip variable and a global variable of the same name are defined, Flash retrieves the movie clip variable, because it is first in the search order for the scope chain. Hence, a movie clip variable always
overrides
a global variable of the same name.
By contrast, the scope chain for code in a nonnested function includes three scopes (again, read from the bottom up):
When a variable is referenced from within a function, Flash checks first for a local variable—defined with the
var
statement—within the function, as shown in Example 2-5. If no local variable is found, Flash checks for a movie clip variable in the movie clip in which the function was defined. (We'll consider this topic, including the behavior for nested functions, more thoroughly in Chapter 9.) If the variable is not found in that movie clip (or its prototype), Flash checks for a global variable of the same name. If no global variable is found, Flash returns
undefined
.
Using the
with
statement, we can add any object to the scope chain as the first place Flash should look for variables. For details, see Chapter 5.
Until you're familiar with the scope chain, you may want to qualify your variable name references (e.g.,
myGameMainTimeline.numPlayers
versus
numPlayers
) to tell Flash explicitly where to look for a variable. Qualified references are unambiguous and are therefore
easiest
to understand for all levels of programmers. See "Accessing Variables on Different Timelines," earlier in this chapter.
Don't confuse the scope chain with the
prototype chain
. Flash uses the scope chain to look up variables; it uses the prototype chain to look up properties and
methods
. We'll discuss the prototype chain in Chapter 12.
Note that, technically, each scope chain is implemented internally as a series of objects used by the interpreter to look up variables. See Chapter 9 of this book and David Flanagan's eloquent description of JavaScript's variable scope in
JavaScript: The Definitive Guide
(O'Reilly & Associates, Inc.). Bear in mind, however, that ActionScript adds movie clips to JavaScript's global and local execution contexts.
2.5.8 Event Handler Scope
As you'll see in Chapter 10, different kinds of event handlers have different scopes in Flash. Table 2-2 provides a summary of event handler scope. For each type of handler, an unqualified, nonlocal variable declaration (one without a
var
statement) will create a new variable in the movie clip listed in the
Scope chain
column of the table. We'll examine this topic more closely in Chapter 10.
Table 2-2. Event handler scope
|
Callback function
|
Global object
Clip in which the function was defined
Local variables
|
|
Listener method
|
Global object
Clip in which the function was defined
Local variables
|
|
Movie clip with
on( )
or
onClipEvent( )
handler
|
Global object
Clip that physically bears the handler at authoring time
|
|
Button with
on( )
handler
|
Global object
Clip on whose timeline the button resides
|
|