Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning ActionScript 2.0 2006

.pdf
Скачиваний:
105
Добавлен:
17.08.2013
Размер:
12.47 Mб
Скачать

Chapter 4

Compare it to the first function syntax:

function functionName(inputValue:DataType):ReturnType

{

// Statements return <value>;

}

You can see that the alternate syntax actually creates a variable with a data type of Function. It is designed for situations in which the function is only going to be used in one place. Clearly this goes against what a function is normally used for, but anonymous functions are useful in situations where code needs to be called in response to a specific user action. For instance, say that you have a movie clip on the stage that you want to respond with some action when it is clicked. The following example enables you to invoke some code when a movie clip with the instance name continueButton is clicked:

continueButton.onRelease = function()

{

trace(“Pressed the continue button”);

}

This code assigns an anonymous function to the movie clip’s onRelease handler, which is a special property of all movie clips. Any function that has been assigned to this property is invoked when the user clicks the contents of the movie clip. In this case, the trace() statement executes every time the user clicks and releases the mouse button over the continueButton movie clip.

Don’t worry if you try this out and have difficulties getting it to work; it’s just a preview of what is to follow. You see much more of this type of function and about working with movie clips in general in Chapter 7.

Passing Functions as Arguments

Anonymous functions are interesting because they reveal something that would otherwise not be apparent, which is that functions are actually just another type of variable. Take a look at the following example:

var openBrowserWindow:Function; openBrowserWindow = function(url:String):Void

{

getURL(url, “externalWindow”);

}

openBrowserWindow(“http://www.apple.com/ipod”);

In this code, a variable of type Function is created. That variable is then assigned an anonymous function. Once the anonymous function is assigned to the variable, the variable behaves exactly like a function, because it is! This example shows that functions can be assigned to a variable just like a string or a number can be assigned to a variable. Similarly, functions can be passed to other functions, as you see next.

In some situations a function takes another function as an argument. Generally, this occurs when something is to happen after a delay, or in response to a user action such as a mouse click. When the action occurs, the function that you supply responds to the action. A good example of working with a delayed

88

Exploring ActionScript Functions and Scope

action is the global setInterval() function, which is built into the Flash player. It takes two arguments: a function that you provide, and the number of milliseconds to wait until that function is called:

function moveShip():Void

{

spaceshipClip._x += 10; updateAfterEvent();

}

setInterval(moveShip, 30);

Here, the setInterval() function accepts a function called moveShip, and an interval value of 30 ms (milliseconds). Every 30 ms, the moveShip() function is called, causing a movie clip with the instance name spaceshipClip to move 10 pixels to the right.

Notice that when the moveShip() function is passed to setInterval(), the round brackets are omitted, which means that the function does not actually execute and can be passed to another function. When the brackets are included after a function name, the function is called and any return values are passed back. Consider what would happen if you included the round brackets when passing moveShip() to setInterval():

setInterval(moveShip(), 30);

Instead of passing the moveShip() function, this line executes that function. moveShip()defines a return type of Void, so it does not return any value, and setInterval() ends up with its first parameter being undefined.

One frequent error is to put quotes around the name of the function that’s being passed in, like this:

setInterval(“moveShip”, 30);

Quotes indicate string data, and a string doesn’t work for setInterval() because it does not contain the reference to the actual function code.

Try It Out

Executing a Function Passed as an Argument

In this project, you work with passing functions as arguments. Here are the steps to follow:

1.Create a new Macromedia Flash document by selecting File New and choosing Flash Document from the New Document panel.

2.Click the first frame in the timeline, open the Actions panel (Window Development Panels Actions), and type in the following ActionScript code:

#include “tryItOut_passingFunctions.as”

3.Make sure that the library panel is open. (If it isn’t, select Window Library.)

4.Make sure that the components panel is open. (If it isn’t, select Window Components.)

5.Open the components panel’s UI Components section and locate the Button component. Drag that component to the library panel.

89

Chapter 4

6.Select File Save As, name the file tryItOut_passingFunctions.fla, choose an appropriate directory, and save it.

7.Create a new script file by selecting File New and choosing ActionScript File from the New Document panel.

8.Name the file tryItOut_passingFunctions.as and save it in the directory containing the

.fla file.

9.Type the following code into the new ActionScript file:

//Create a button using the standard Flash button component.

function createButton(callbackFunction:Function, buttonText:String, ; x:Number, y:Number):Void

{

var currentDepth:Number = this.getNextHighestDepth(); var newButton:mx.controls.Button;

createClassObject(mx.controls.Button, “button”+currentDepth, currentDepth);

newButton = _level0[“button” + currentDepth]; newButton.label = buttonText;

newButton._x = x; newButton._y = y;

newButton.addEventListener(“click”, callbackFunction);

}

//Function to be called when the start button is clicked function handleStart():Void

{

trace(“Start button clicked”);

}

//Function to be called when the stop button is clicked function handleStop():Void

{

trace(“Stop button clicked”);

}

//Create the start and stop buttons createButton(handleStart, “Start”, 10, 10); createButton(handleStop, “Stop”, 130, 10);

10.Save the file, return to the Flash project file, and select Control Test Movie. Two buttons should be on the screen. Clicking them generates messages in the output panel.

How It Works

This program demonstrates the use of a function to simplify a repeating task, and passing functions as arguments to other functions.

The program first defines a function to place a button on the screen:

// Create a button using the standard Flash button component. function createButton(callbackFunction:Function, buttonText:String, ;

90

Exploring ActionScript Functions and Scope

x:Number, y:Number):Void

{

// ...

}

For a button to react to a click, it needs to be passed a function. This example’s first line, which is the function declaration, does this by requiring a function to be passed as the first argument. It also requires a text label to place inside the button, as well as x and y coordinates for button placement.

createButton()places a Button component on the stage (you get to components a little later in this book) and lets the button know to call your function when it is clicked.

Two variables are created in the createButton() function, one to get the next available depth number and one to temporarily hold the new button:

var currentDepth:Number = this.getNextHighestDepth(); var newButton:mx.controls.Button;

Next, the button is created:

createClassObject(mx.controls.Button, “button”+currentDepth, currentDepth);

createClassObject() is a special function that’s used to place copies of components to the stage on- the-fly. It creates a button using the class called mx.controls.Button as a template and assigns a name to the new button. The name is combined with the depth number assigned to the component to ensure a unique name so that there are no naming conflicts.

Now you have a button (with a name that’s something like button1), but you need a way to work with it. The following line saves a handle to the new button, grabbing it by name from this, the current timeline:

newButton = this[“button” + currentDepth];

The button is given a label, and moved to a particular x and y coordinate:

newButton.label = buttonText; newButton._x = x; newButton._y = y;

Finally, the function that you provided is assigned to the button so that it is called when the button is clicked. You learn more about how this works in the Chapter 9 about Components. The important thing to note here is that the callbackFunction variable represents the function that you passed to createButton():

newButton.addEventListener(“click”, callbackFunction);

The next two functions, which you pass to createButton(), are created to react to the user clicking either of the buttons:

// Function to be called when the start button is clicked function handleStart():Void

{

91

Chapter 4

trace(“Start button clicked”);

}

// Function to be called when the stop button is clicked function handleStop():Void

{

trace(“Stop button clicked”);

}

Finally, two calls are made to createButton(), one passing in a handle to the handleStart() function, and one passing in a handle to the handleStop() button:

// Create the start and stop buttons createButton(handleStart, “Start”, 10, 10); createButton(handleStop, “Stop”, 130, 10);

At this point, the buttons are placed on the stage, and are ready to respond to being clicked.

Variable Scope

When a variable is created using the var keyword, it may be visible to some lines of code, but not visible to others. The visibility of a variable to other parts of a program is called variable scope and the path that the Macromedia Flash player searches to find a variable is called the scope chain. Until functions were discussed, variable scope was not an issue because the variables created were visible throughout the timeline. Now that you are working with functions. . .

When a variable is declared inside a function, it cannot be seen outside of that function. This is called being local in scope, and the variables are referred to as local variables. When the function terminates, variables declared within it are automatically destroyed.

When a variable is created on the main timeline, it is not immediately visible from another movie clip’s timeline. Likewise, a variable created within a nested movie clip is not immediately visible from the main timeline. These are called timeline variables. When a movie clip containing timeline variables is destroyed, so are the timeline variables.

When a variable is prepended with the keyword _global, the variable is made visible to every piece of code throughout the project. Such variables are called global variables and persist as long as the movie plays, or until explicitly deleted.

Figure 4-1 shows a simplified representation of how the scope chain is organized. Code within a function has access to its own local function variables, as well as to timeline variables and global variables. Code on the timeline has access to its own timeline variables and to global variables, but has no access to any function’s local variables.

Code always works in the context of a timeline, so code placed on a movie clip’s timeline always has direct access to both global variables and variables defined within the same timeline.

92

Exploring ActionScript Functions and Scope

_global

var globalVar:String = “foo”;

timeline

var timelineVar:String = “bar”;

function

var functionVar:String = “baz”;

Figure 4-1

Here’s an example that demonstrates variable scope in action:

_global.myGlobalVar = “foo”;

var myTimelineVar:String = “bar”;

function myFunction1():Void

{

var myFunctionVar1:String = “baz”; trace(“myFunction1->myGlobalVar: “ + _global.myGlobalVar); trace(“myFunction1->myTimelineVar: “ + myTimelineVar); trace(“myFunction1->myFunctionVar1: “ + myFunctionVar1); trace(“myFunction1->myFunctionVar2: “ + myFunctionVar2); myFunction2();

}

function myFunction2():Void

{

var myFunctionVar2:String = “qux”; trace(“myFunction1->myFunction2->myGlobalVar: “ + _global.myGlobalVar); trace(“myFunction1->myFunction2->myTimelineVar: “ + myTimelineVar); trace(“myFunction1->myFunction2->myFunctionVar1: “ + myFunctionVar1); trace(“myFunction1->myFunction2->myFunctionVar2: “ + myFunctionVar2);

}

myFunction1();

trace(“myGlobalVar: “ + _global.myGlobalVar);

93

Chapter 4

trace(“myTimelineVar: “ + myTimelineVar); trace(“myFunctionVar1: “ + myFunctionVar1); trace(“myFunctionVar2: “ + myFunctionVar2);

//Outputs:

//myFunction2->myGlobalVar: foo

//myFunction2->myTimelineVar: bar

//myFunction2->myFunctionVar1: undefined

//myFunction2->myFunctionVar2: qux

//myFunction1->myGlobalVar: foo

//myFunction1->myTimelineVar: bar

//myFunction1->myFunctionVar1: baz

//myFunction1->myFunctionVar2: undefined

//myGlobalVar: foo

//myTimelineVar: bar

//myFunctionVar1: undefined

//myFunctionVar2: undefined

The last four lines of output show that code on the timeline can see timeline variables and global variables, but not the local variable in either of the two functions. The middle four lines of output show that code within myFunction1() can see its own local variable as well as timeline variables and global variables, but not the local variable in myFunction2(). The top four lines of output show that

myFunction2() can see its own local variable as well as timeline variables and global variables, but not the local variable in myFunction1(). This last case is actually a bit different than in some other programming languages, where a function that is called by another function has access to the calling function’s local variables.

There is a slight change in scope when it comes to a function defined within another function. In the preceding example, you saw that the local variable in myFunction1() was not visible from within myFunction2() and vice versa. When an anonymous function is created within another function, the anonymous function does have access to the parent function’s local variables, as the following example shows:

function myFunction1():Void

{

var myFunctionVar1:String = “foo”;

var myFunction2:Function = function():Void

{

var myFunctionVar2:String = “bar”; trace(“myFunction2->myFunctionVar1: “ + myFunctionVar1); trace(“myFunction2->myFunctionVar2: “ + myFunctionVar2);

}

myFunction2();

trace(“myFunction1->myFunctionVar1: “ + myFunctionVar1); trace(“myFunction1->myFunctionVar2: “ + myFunctionVar2);

}

myFunction1();

// Outputs:

94

Exploring ActionScript Functions and Scope

//myFunction2->myFunctionVar1: foo

//myFunction2->myFunctionVar2: bar

//myFunction1->myFunctionVar1: foo

//myFunction1->myFunctionVar2: undefined

In this example, code within myFunction2() has access to the local variables for myFunction1()! That only happens for anonymous functions defined within another function, and is extremely handy when you deal with event handlers, which are discussed in Chapter 10.

Managing Variable Scope

Managing variable scope in an application is important in making code behavior predictable and helping track down problems. The axiom to follow is

Keep variable scope as narrow as possible.

Countless hours can be wasted trying to track down and fix problems with variables that are too visible to other parts of a project. This means thinking twice before creating a timeline variable, and thinking three times before creating a global variable.

Several reasons exist for minimizing the use of global variables in particular:

They persist through the duration of the project’s execution, taking up memory.

They increase the impact of a code change. The effect of changing global data isn’t always immediately visible, and is harder to debug.

They reduce code reusability. Functions that reference global variables cannot be copied and pasted into another project without also implementing the global variables in the same way.

Instead of using global variables, use function arguments to pass data that is needed for any particular function.

The use of timeline variables should be minimized for the same reasons as for global variables, although you will likely need to use at least some timeline variables. As you become a more experienced ActionScript programmer, you may want to learn to create your own custom classes to manage your data and to organize the structure of your code. Chapter 5 introduces you to how object-oriented programming can help you better organize your code, and Chapter 27 gets you started working with custom classes.

Try It Out

Accessing Variables Declared in Different Places

In this project, you pass functions as arguments.

1.Create a new Macromedia Flash document.

2.Click the first frame in the timeline, open the Actions panel, and type in the following ActionScript code:

#include “tryItOut_variableScope.as”

95

Chapter 4

3.Save the file as tryItOut_variableScope.fla in an appropriate directory.

4.Create a new script file.

5.Save the file as tryItOut_variableScope.as in the directory in which you saved the .fla file.

6.Type the following code into the new ActionScript file:

_global.someVar = 0; var someVar:Number = 1;

function someFunction():Void

{

var someVar:Number = 2;

trace(someVar);

}

someFunction();

trace(someVar); delete someVar; trace(someVar);

7.Save the file, return to the .fla, and select Control Test Movie.

How It Works

Using the same variable name for multiple variables exposes how the scope chain works. The variable within the function takes precedence over the timeline variable and the global variable, so the trace prints out the value 2. Outside the function, the timeline variable is next in the scope chain, so the trace prints out the value 1. When the timeline variable is deleted, the trace prints out the value 0, which is the variable within the function.

This example also shows why it is not a good idea to have variable naming matches between local, timeline, and global variables.

Side Effects

When a function manipulates data through a means other than a return statement, it is said to have side effects. The goal of good programming is to reduce a function’s side effects because they tend to make code harder to debug and fix. A side effect of a function might be the changing of a global variable, the modification of data in one of its input arguments, or the sending of data to a server. Here’s an example of a function with a side effect:

var numCars:Number = 10;

function incrementNumber(incAmount:Number):Number

{

numCars = numCars + incAmount;

}

trace(numCars);

96

Exploring ActionScript Functions and Scope

// Outputs: 10

incrementNumber(5);

trace(numCars); // Outputs: 15

The side effect for the function incrementNumber() is to directly modify a timeline variable. This would be better done as follows:

var numCars:Number = 10;

function incrementNumber(startingNumber:Number, incAmount:Number):Number

{

return startingNumber + incAmount;

}

trace(numCars); // Outputs: 10

numCars = incrementNumber(numCars, 5);

trace(numCars); // Outputs: 15

incrementNumber(numCars, 5);

trace(numCars); // Outputs: 15

This version is cleaner because it is explicit about what the function does. The variable numCars does not get modified unless it is explicitly assigned the incremented value. Looking through the first two trace() statements, it does get incremented, but only because it is assigned the output of the incrementNumber() function. The third trace shows that calling incrementNumber() without assigning the return value to numCars has no effect on numCars.

It is possible for a function to modify one or more arguments that have been passed in. Recall from the discussion on variables at the beginning of this chapter that when a primitive data type is assigned to another variable, a copy is made. When a composite data type is assigned to another variable, the variable is not copied. Instead, the new variable points to the same data in the same location in memory as the source variable. This is the same behavior that takes place when a variable is passed into a function, and it is through this sharing of memory that it is possible for a function to manipulate the contents of an argument variable. Here’s an example:

var currentDate:Date = new Date();

function getDateNextWeek(inputDate:Date):Date

{

inputDate.setTime(inputDate.getTime() + 7 * 24 * 60 * 60 * 1000); return inputDate;

}

trace(currentDate);

97