Recipe 1.8 Creating Reusable Code

1.8.1 Problem

You want to perform a series of actions at various times without duplicating code unnecessarily throughout your movie.

1.8.2 Solution

Create a function and then call (i.e., invoke) it by name whenever you need to execute those actions.

There is more than one way to create (i.e., define or declare) a function. Here is how to create a named function:

function functionName (  ) {
  // Statements go here.

To call (i.e., execute) the named function, refer to it by name, such as:

functionName(  );

Here is how to create a function literal:

functionName = function (  ) {
  // Statements go here.

Although not strictly required, it is considered a best practice to include a semicolon following the closing curly brace when defining a function literal.

1.8.3 Discussion

Grouping statements into a function allows you to define the function once but execute it as many times as you'd like. This is useful when you need to perform similar actions at various times without duplicating the same code in multiple places. Keeping your code centralized in functions makes it easier to understand (because you can write the function once and then ignore the details when using it) and easier to maintain (because you can make changes in one place rather than in multiple places).

There are two common ways of defining ActionScript functions: as named functions or function literals (a.k.a. anonymous functions). Each of these ways of declaring a function has its own use.

The named function declaration is the most common choice (when not defining a function to be used as a method) and has at least one advantage over function literals: named functions are accessible within the entire keyframe (or on( ) or onClipEvent( ) handler) even if they come after the call to the function.

For example, even though the writeMessage( ) function is not declared until after it is invoked, the function is still available:

// Invoke the writeMessage(  ) function, which is declared later in the script.
writeMessage(  );

// Declare (define) the writeMessage(  ) function as a named function.
function writeMessage (  ) {
  trace("Hello, friend.");

// The function is available before or after it has been declared.
writeMessage(  );

In contrast, a function literal is accessible only from lines of code that come after the declaration:

// The ActionScript interpreter will not be able to find a function with this name, 
// and so nothing happens (it fails silently).
writeMessage2(  );

// Declare (define) the writeMessage2(  ) function as an anonymous function literal
writeMessage2 = function (  ) {
  trace("Hello, friend.");

// However, the function is available from lines of code after it has been declared.
writeMessage2(  );

However, there are several reasons to use function literals:

  • You can assign a function literal to a global variable so that the function can be accessed from any timeline.

  • Function literals offer a convenient, compact, and intuitive way to define methods for objects.

  • Function literals can be treated like other variables, in that they can be passed to other functions or have their values reassigned.

Here, we assign a function literal as a property of the _global object:

_global.launchBookExamples = function (  ) {
  getURL("", "_blank");

Therefore, from anywhere on any timeline, you can execute the function by simply using its name. For example, you might attach this script to a button:

myButton.onRelease = function (  ) {
  launchBookExamples(  );

Here, we define the function as a method of a movie clip (where onEnterFrame( ) is a special, built-in method for movie clips that you need to define before it can be used):

myClip_mc.onEnterFrame = function (  ) {
  trace("Hooray for methods!");

Of course, you can define custom methods as well by simply assigning the function literal to a new property of the object:

myClip_mc.myCustomMethod = function (  ) {
  trace("Hooray for methods!");

It is also worth noting that you can set one method equal to another method. This technique is often used to assign the same actions to a movie clip or button for two different events. You can define an anonymous function and assign it to one of the event handler methods, and then simply assign one event handler method to the other. A common example of this is when you want to define the same actions for when a user releases a button or movie clip and when they release outside:

// Define an onRelease(  ) method for a movie clip.
myMovieClip.onRelease = function (  ) {
  trace("Hooray for methods!");

// Assign the same method definition to the onReleaseOutside(  ) method as well.
myMovieClip.onReleaseOutside = myMovieClip.onRelease;

Functions can also be passed as data. You can conveniently pass a function literal to another function that requires a function as one of its arguments, such as setInterval( ) or the Array.sort( ) method:

// Set an interval that calls a function that increments a variable, i, and displays
// the value.
setInterval(function (  ) {trace(++i);}, 1000);

Functions are subject to the same scope limitations as variables. Timeline functions are accessible only while the timeline on which they are defined exists. A timeline function can be a named function or an anonymous function assigned to a timeline variable. Additionally, timeline functions can be called only by using the proper target path. When you access the function from the same timeline, you do not need to include the target path, but when you want to access the function from another timeline, you need to make sure you provide the correct path.

// Explicitly invoke a function that is defined on the main timeline.
_root.myFunction(  );

If your function is used within the same timeline only, you don't need to worry about scope issues. However, if you intend to use the function throughout many timelines, two solutions are generally employed:

  • Define the function as a global function. If you make a function a global function, you can call it by name from any timeline in the movie without having to worry about scope:

    _global.myFunction = function (  ) {
      trace("Global functions are fun!");
  • Define the function as a (static) method of a global class. All of the built-in classes are global by default, and if you define a class with a global constructor (see Recipe 12.5), then even your custom classes can be global. This technique is really a variation on the first, but with the advantage that classes offer you a way of organizing your functions in a potentially meaningful way (for example, the built-in Math class organizes many mathematical functions).

    Part I: Local Recipes
    Part II: Remote Recipes