Book HomeActionScript: The Definitive GuideSearch this book

9.7. Function Scope

ActionScript statements have a certain scope, or area of effect within which they are valid. When a statement is attached to a movie clip, that statement's scope is limited to the clip that bears it. For example, here we refer to a variable, score, in an assignment statement:

score = 10;

If that statement were attached to clipA, then the interpreter would set the value of score in clipA, because the statement is "scoped" to clipA. If that statement were attached to clipB, the interpreter would set the value of score in clipB, because the statement is scoped to clipB. The location of the statement determines its scope, and, hence, its effect.

Statements in the body of a function operate in their own, separate scope, called a local scope. A function's local scope is like a private phone booth for the function, distinct from the scope of the clip or object to which the function is attached. The local scope of a function is created when the function is invoked and destroyed when the function finishes executing. When resolving variables referenced in the statements of the function body, the interpreter looks first in the function's scope.

Function parameters, for example, are defined in the local scope of a function -- not the scope of the timeline that bears the function. Parameters, hence, are accessible to the statements of a function's body only while the function is running. Statements outside the function have no access to the function's parameters.

A function's local scope provides a place for temporary variables for use solely within a function. This eliminates potential name conflicts between function variables and timeline variables, and it reduces our overall memory usage.

9.7.1. The Scope Chain

Even though functions operate in their own local scopes, normal timeline variables are still accessible to the statements of a function body. The local scope of a function is the first place the interpreter looks to resolve variable references, but if the variable reference can't be found in the local scope, the search extends to the object or movie clip that bears the function.

For example, suppose we define a variable, firstName, on the timeline of a movie clip, clipA. We also declare a function, getName( ), on clipA. Inside getName( ), we refer to the variable firstName in a trace( ) statement:

firstName = "Christine";

function getName ( ) {
  trace(firstName);
}

getName( );

When we invoke getName( ), the interpreter must find the value of firstName. There is no variable called firstName in the local scope of getName( ), so the interpreter checks the timeline of clipA for the variable firstName. There, the interpreter finds firstName and displays its value, "Christine".

Our trace( ) statement is able to refer to a timeline variable from the body of our getName( ) function because getName( ) does not, itself, define a parameter or variable called firstName. Now consider what happens when we add a parameter called firstName to the getName( ) function:

firstName = "Christine";

function getName (firstName) {
  trace(firstName);
}

getName("Kathy");

This time, when we invoke getName( ), we assign the value "Kathy" to the parameter firstName. When the interpreter executes the trace( ) statement, it searches the local scope where it finds the firstName parameter and its value "Kathy". So the output of the function this time is "Kathy" not "Christine". Even though the timeline variable firstName exists, the function's local variable called firstName takes precedence.

Our example demonstrates the operation of the scope chain -- the hierarchy of objects used by the interpreter to resolve references to variables and object properties. For functions attached to timelines, the scope chain has only two levels: the local scope and the scope of the movie clip that bears the function. But when we attach functions to custom objects and classes, the scope chain can involve many more levels, as we'll see in "Object Property Inheritance" in Chapter 12, "Objects and Classes".

If the scope of the variables and properties we want to access from our function body differs from the statement's scope, we must use dot syntax to form an explicit reference. For example:

function dynamicGoto( ) {
  // Deliberately go outside the function's local scope
  _root.myClip.gotoAndPlay(_root.myClip.targetFrame);
}

Note that a function's scope chain is determined relative to the function declaration statement, not any function invocation statement. That is, the code in a function's body is scoped to the movie clip that bears the function declaration, not the movie clip that bears the statement that invokes the function.

Here's an example showing a misguided use of the scope chain:

// CODE ON MAIN MOVIE TIMELINE
// This function's scope chain includes the main movie
function rotate(degrees) {
  _rotation += degrees;
}

If we attempt to rotate clipA using _root.rotate, it rotates the entire main movie, not just clipA:

// CODE ON clipA's TIMELINE
_root.rotate(30);  // Oops! This rotates the entire movie!

In this situation we can fix our problem by passing the clip to be rotated as an argument to the rotate( ) function:

function rotate (theClip, degrees) {
	theClip._rotation += degrees;
}

// Invoke rotate( ) with a reference to clipA
_root.rotate(clipA, 30);

9.7.2. Local Variables

Variables assigned to a function's local scope are called local variables. Local variables, including parameters, are accessible only to statements in the body of the function in which they are defined and exist only while that function runs. To create a local variable (other than the parameters that automatically become local variables), we use the var statement inside any function, like this:

function funcName( ) {
  var temp = "just testing!";  // Declares the local variable temp
}

Local variables are useful for holding information temporarily. Here, for example, we use the local variable lastSpacePlusOne to hold an interim result. Like all local variables, it dies when the function ends:

function getLastWord(text) {
  var lastSpacePlusOne = text.lastIndexOf(" ") + 1;                 // Local
  var lastWord = text.subString(lastSpacePlusOne, text.length);     // Local
  return lastWord;
}

// Displays: "word"
trace(getLastWord("Tell me the last word"));

// Displays: undefined. lastSpacePlusOne is local, and not
// available outside the getLastWord( ) function.
trace(lastSpacePlusOne);

When local variables expire at the end of a function, the memory associated with them is freed. By using local variables to store all temporary values, we can conserve memory in a program. Furthermore, when we declare a local variable, we need not worry that it might conflict with a timeline variable of the same name.

Of course, not all variables used in functions need to be local. Earlier we learned that we can read timeline variables from inside a function; we can also create and write to them. Variable assignment statements within a function that do not apply to a local variable are scoped to the timeline instead of the function. In this example, x is a local variable, but y and z are timeline variables:

var z = 1;

function createVars( ) {
  var x = 10;    // Create local variable x
  y = 13;        // Create timeline variable y
  z = 2;         // Alter timeline variable z
}
createVars( );   // Call the function
trace(x);        // x is undefined (x dies when the function ends)
trace(y);        // y is 13 (y exists after the function ends)
trace(z);        // z is 2 (z was permanently altered by the function)

The rules for creating timeline variables apply even if our function is a method of an object. We won't cover objects until Chapter 12, "Objects and Classes", but for those familiar with object-oriented programming, Example 9-2 shows code that proves the point. Note that x is defined on the timeline because it doesn't exist in the local scope of newFunc( ) or as a property of newObj.

Example 9-2. Variable Scope Within Object Methods

newObj = new Object( );                    // Create an object
newObj.newFunc = function( ) { x = 12; };  // Attach a new method
newObj.newFunc ( );                        // Call the method

// Now let's check for x
trace("x is " + x);                       // x is 12
trace("newObj.x is " + newObj.x);         // newObj.x is undefined


Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.