ActionScript Basics: Chapter 1 - ActionScript 3.0 Cookbook
Pages: 1, 2, 3, 4

Section 1.14: Generalizing a Method to Enhance Reusability

Problem

You want to perform slight variations of an action without having to duplicate multiple lines of code to accommodate minor differences.

Solution

Add parameters to your method to make it flexible enough to perform slightly different actions when invoked rather than performing exactly the same action or producing the same result each time.

Define the parameters that account for the variability in what you want the method to do:

private function average (a:Number, b:Number, c:Number):void {
  trace("The average is " + (c + b + c)/3);
}

If you don't know the exact number of parameters the method will receive, use the built-in arguments array to handle a variable number of parameters.

Discussion

A method that doesn't accept parameters generally does exactly the same result each time it is invoked. However, you will often need to perform almost exactly the same actions as an existing method, but with minor variations. Duplicating the entire method and then making minor changes to the second version is a bad idea in most cases. Usually, it makes your code harder to maintain and understand. More importantly, you'll usually find that you need not only two variations but many variations of the method. It can be unnecessarily difficult to maintain five or six variations of what should ideally be wrapped into a single method. The trick is to create a single method that can accept different values to operate on.

For example, let's say you have an average( ) method for averaging a set of numbers. Instead of having it always average the same two numbers, you want to specify arbitrary values to be averaged each time it is invoked. This can be accomplished with parameters.

The most common way to work with parameters is to list them within the parentheses in the method declaration. The parameter names should be separated by commas, and when you invoke the method you should pass it a comma-delimited list of arguments that corresponds to the parameters it expects.

The terms "parameters" and "arguments" are often used interchangeably to refer to the variables defined in the method declaration or the values that are passed to a method when it is invoked.

The following is a simple example of a method declaration using parameters:

// Define the function such that it expects two parameters: a and b.
private function average(a:Number, b:Number):Number {
  return (a + b)/2;
}

Now here's a method invocation in which arguments are passed during the method call:

// When you invoke the function, pass it two arguments, such as 
// 5 and 11, which correspond to the a and b parameters. 
var averageValue:Number = average(5, 11);

In most situations it is best to declare the parameters that the method should expect. However, there are some scenarios in which the number of parameters is unknown. For example, if you want the average( ) method to average any number of values, you can use the built-in arguments array that is available within any function's body. All the parameters that are passed to a function are automatically placed into that function's arguments array.

// There is no need to specify the parameters to accept when using the 
// arguments array.
private function average( ):Number {
  var sum:Number = 0;

  // Loop through each of the elements of the arguments array, and 
  // add that value to sum.
  for (var i:int = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }
  // Then divide by the total number of arguments
  return sum/arguments.length;
}

// You can invoke average( ) with any number of parameters. 
var average:Number = average (1, 2, 5, 10, 8, 20);

Technically, arguments is an object with additional properties beyond that of a basic array. However, while arguments is a special kind of array, you can still work with it in the same ways that you would a regular array.

Section 1.15: Exiting a Method

Problem

You want to exit a method.

Solution

Methods terminate automatically after the last statement within the method executes. Use a return statement to exit a method before reaching its end.

Discussion

The return statement exits the current method and the ActionScript interpreter continues execution of the code that initially invoked the method. Any statements within the method body that follow a return statement are ignored.

private function sampleFunction ( ):void {
  return;
  trace("Never called");
}

// Called from within another method:
sampleFunction( );
// Execution continues here after returning from the sampleFuction( ) invocation

In the preceding example, the return statement causes the method to terminate before performing any actions, so it isn't a very useful method. More commonly, you will use a return statement to exit a method under certain conditions. This example exits the method if the password is wrong:

private function checkPassword (password:String):void {

  // If password is not "SimonSays", exit the function.
  if (password != "SimonSays") {
    return;
  }

  // Otherwise, perform the rest of the actions.
  showForm ("TreasureMap");
}

// This method call uses the wrong password, so the 
// function exits.
checkPassword("MotherMayI");

// This method call uses the correct password, so the function 
// shows the TreasureMap form.
checkPassword("SimonSays");

In the preceding example, you may notice that the method is declared as void, yet it is possible to use a return statement within the method without getting a compiler error. When a return statement is used simply to exit from a method, it is valid within a method declared as void.

In ActionScript 2.0, the function was Void. In ActionScript 3.0, it is lowercase void.

However, if you attempt to actually return a value in such a method, the compiler generates an error.

private function sampleMethod ( ):void {
  return "some value";  // This causes the compiler to generate an error.
}

Section 1.16: Obtaining the Result of a Method

Problem

You want to perform some method and return the results to the statement that invoked the function.

Solution

Use a return statement that specifies the value to return.

Discussion

When used without any parameters, the return statement simply terminates a method. However, any value specified after the return keyword is returned to statement that invoked the method. Usually, the returned value is stored in a variable for later use. The datatype of the return value must match the return type of the method:

private function average (a:Number, b:Number):Number {
  return (a + b)/2;
}

Now we can call the average( ) method and store the result in a variable and use the result in some way.

var playerScore:Number = average(6, 10);
trace("The player's average score is " + playerScore);

You can use the return value of a method, without storing it in a variable, by passing it as a parameter to another function, such as:

trace("The player's average score is " + average(6, 10));

Note, however, that if you do nothing with the return value of the function, the result is effectively lost. For example, this statement has no detectable benefit because the result is never displayed or used in any way:

average(6, 10);

Section 1.17: Handling Errors

Problem

You want to programmatically detect when certain errors occur and handle them using code.

Solution

Use a throw statement to throw an error when it is detected. Place any potentially error-generating code within a try block, and then have one or more corresponding catch blocks to handle possible errors.

Description

Flash Player 8.5 supports a try/catch methodology for handling errors in ActionScript. That means you can write code that can intelligently deal with certain error types should they occur. While you cannot handle syntax errors (the .swf won't even compile in that case), you can handle most other error types, such as missing or invalid data. The benefit is that you can attempt to resolve the situation programmatically.

An example may help to illustrate when and how you might use try/catch methodology: Consider an application that draws a rectangle based on user-input dimensions. To draw a rectangle within the application, you want to have certain range limitations on the dimensions the user can input. For example, you may want to make sure the values are defined, valid numeric values greater than 1 and less than 200. While there are certainly ways you can work to ensure the quality and validity of the data before even attempting to draw the rectangle, you can also use try/catch methodology as a fail-safe. You can have Flash attempt to draw the rectangle, but if the dimension values are detected to be invalid or out of range, you can throw an error that can be handled programmatically. At that point you can do many things, from simply skipping the action, to substituting default data, to alerting the user to enter valid data.

There are two basic parts involved in working with errors in ActionScript: throwing the error and catching the error. There are several errors, which are thrown automatically by the player, such as IllegalOperationError, MemoryError, and ScriptTimeoutError. These are in the flash.errors package. But you can also detect when an error has occurred and throw your own custom error. You can throw an error using the throw statement. The throw statement uses the throw keyword followed by a value or reference that should be thrown. Most frequently you should throw an Error object or an instance of an Error subclass. For example:

throw new Error("A general error occurred.");

As you can see, the Error constructor accepts one parameter, a message to associate with the error. The parameter is optional, and depending on how you are handling the errors, you may or may not choose to use it. However, in most cases it makes sense to specify an error message. It is possible, then, to log the error messages for debugging purposes.

Once an error has been thrown, Flash halts the current process and looks for a catch block to handle the error. This is where the try and catch blocks come into play. Any code that could potentially throw an error should be enclosed in a try block. Then, if an error is thrown, only the code in the try block is halted, and the associated catch block is called. The following is the simplest scenario:

try {
  trace("This code is about to throw an error.");
  throw new Error("A general error occurred.");
  trace("This line won't run");
}
catch (errObject:Error) {
  trace("The catch block has been called.");
  trace("The message is: " + errObject.message);
}

The preceding code produces the following in the Output panel:

This code is about to throw an error.
The catch block has been called.
The message is: A general error occurred.

Of course, the preceding example is overly simplistic, and you wouldn't realistically use code in an actual application, but it does illustrate the basic process. You can see that as soon as the error is thrown, the try block is exited, and the catch block is run and passed a reference to the Error object that was thrown.

Much more frequently, the error is thrown from within a function or method. Then Flash looks to see if the throw statement within the function is contained within a try block. If so, it calls the associated catch block as you've seen already. However, if the throw statement in the function is not within a try block, Flash exits the function and next looks to see if the function call was made within a try block. If so, it halts the code in the try block and runs the associated catch block. Again, a very simple example:

private function displayMessage(message:String):void {
  if(message == undefined) {
    throw new Error("No message was defined.");
  }
  trace(message);
}

try {
  trace("This code is about to throw an error.");
  displayMessage( );
  trace("This line won't run");
}
catch (errObject:Error) {
  trace("The catch block has been called.");
  trace("The message is: " + errObject.message);
}

In the preceding example the Output panel would display the following:

This code is about to throw an error.
The catch block has been called.
The message is: No message was defined.

As you can see from the output, the code works very similarly to the way in which the previous example worked, except the throw statement is hidden within a function instead of being called directly within the try block. The advantage is that you can start to then create functions and methods that are intelligent enough to know if and when to throw errors. You can then simply use those functions and methods within try blocks, and you can handle any errors should they occur.

The following code illustrates a more realistic example:

// Define a function that draws a rectangle within a specified sprite
private function drawRectangle(sprite:Sprite, newWidth:Number, newHeight:Number):void {

  // Check to see if either of the specified dimensions are not 
  // a number. If so, then thrown an error.
  if(isNaN(newWidth) || isNaN(newHeight)) {
    throw new Error("Invalid dimensions specified.");
  }

  // If no error was thrown, then draw the rectangle.
  sprite.graphics.lineStyle(1, 0, 1);
  sprite.graphics.lineTo(nWidth, 0);
  sprite.graphics.lineTo(nWidth, nHeight);
  sprite.graphics.lineTo(0, nHeight);
  sprite.graphics.lineTo(0, 0);
}

Now we can call the drawRectangle( ) method using a try/catch statement.

try {

  // Attempt to draw two rectangles within the current sprite. 
  // In this example it is assumed that the variables for the dimensions 
  // are retreiving values from user input, a database, an XML file, 
  // or some other datasource.
  drawRectangle(this, widthA, heightA);
  drawRectangle(this, widthB, heightB);
}
catch(errObject:Error) {

  // If an error occurs, clear any rectangles that were drawn from 
  // the sprite. Then display a message to the user.
  this.graphics.clear( );
  tOutput.text = "An error occurred: " + errObject.message;
}

In addition to the try and catch blocks, you can also specify a finally block. The finally block contains code that is called regardless of whether an error was thrown. In many cases the finally block may not be necessary. For example, the following two examples do the same thing:

//Without using finally:
private function displayMessage(message:String):void {
  try {
    if(message == undefined) {
      throw new Error("The message is undefined.");
    }
    trace(message);
  }
  catch (errObject:Error) {
    trace(errObject.message);
  }
  trace("This is the last line displayed.");
}
//With finally:
private function displayMessage(message:String):void {
  try {
    if(message == undefined) {
      throw new Error("The message is undefined.");
    }
    trace(message);
  }
  catch (errObject:Error) {
    trace(errObject.message);
  }
  finally {
    trace("This is the last line displayed.");
  }
}

However, the finally block runs no matter what occurs within the try and catch blocks, including a return statement. So the following two functions are not the equivalent:

//Without using finally:
private function displayMessage(message:String):void {
  try {
    if(message == undefined) {
      throw new Error("The message is undefined.");
    }
    trace(message);
  }
  catch (errObject:Error) {
    trace(errObject.message);
    return;
  }
  // This line won't run if an error is caught.
  trace("This is the last line displayed.");
}
//With finally:
private function displayMessage(message:String):void {
  try {
    if(message == undefined) {
      throw new Error("The message is undefined.");
    }
    trace(message);
  }
  catch (errObject:Error) {
    trace(errObject.message);
    return;
  }
  finally {
    // This runs, even if an error is caught.
    trace("This is the last line displayed.");
  }
}

You can create much more complex error handling systems than what is shown in this recipe. Throughout this book you will find examples of more complex error handling in appropriate contexts.

This excerpt is from ActionScript 3.0 Cookbook. Well before Ajax and Windows Presentation Foundation, Macromedia Flash provided the first method for building "rich" web pages. Now, Adobe is making Flash a full-fledged development environment, and learning ActionScript 3.0 is key. That's a challenge for even the most experienced Flash developer. This Cookbook offers more than 300 solutions to solve a wide range of coding dilemmas, so you can learn to work with the new version right away.

buy button