2.5 Variable Scope

ActionScript for Flash MX: The Definitive Guide, 2nd Edition
By Colin Moock
Chapter 2.  Variables

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.

Figure 2-1. A sample movie clip hierarchy
figs/act2.0201.gif

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.movieNameMainTimeline = 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 _leveln, 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.

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:

  • Global object (interpreter looks here last)

  • Enclosing movie clip object (interpreter looks here first)

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):

  • Global object (_global)

  • Enclosing movie clip object

  • Local variables

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

Event handler type

Scope chain

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

     



    ActionScript for Flash MX. The Definitive Guide
    ActionScript for Flash MX: The Definitive Guide, Second Edition
    ISBN: 059600396X
    EAN: 2147483647
    Year: 2002
    Pages: 780
    Authors: Colin Moock

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net