Functions

Functions are blocks of code that carry out specific tasks and can be reused in your program. There are two types of functions in ActionScript 3.0: methods and function closures. Whether a function is a called a method or a function closure depends on the context in which the function is defined. A function is called a method if you define it as part of a class definition or attach it to an instance of an object. A function is called a function closure if it is defined in any other way.

Functions have always been extremely important in ActionScript. In ActionScript 1.0, for example, the class keyword did not exist, so “classes” were defined by constructor functions. Although the class keyword has since been added to the language, a solid understanding of functions is still important if you want to take full advantage of what the language has to offer. This can be a challenge for programmers who expect ActionScript functions to behave similarly to functions in languages such as C++ or Java. Although basic function definition and invocation should not present a challenge to experienced programmers, some of the more advanced features of ActionScript functions require some explanation.

Basic function concepts

Calling functions

You call a function by using its identifier followed by the parentheses operator (()). You use the parentheses operator to enclose any function parameters you want to send to the function. For example, the trace() function is a top-level function in ActionScript 3.0:

trace("Use trace to help debug your script");

If you are calling a function with no parameters, you must use an empty pair of parentheses. For example, you can use the Math.random() method, which takes no parameters, to generate a random number:

var randomNum:Number = Math.random();

Defining your own functions

There are two ways to define a function in ActionScript 3.0: you can use a function statement or a function expression. The technique you choose depends on whether you prefer a more static or dynamic programming style. Define your functions with function statements if you prefer static, or strict mode, programming. Define your functions with function expressions if you have a specific need to do so. Function expressions are more often used in dynamic, or standard mode, programming.

Function statements

Function statements are the preferred technique for defining functions in strict mode. A function statement begins with the function keyword, followed by:

  • The function name

  • The parameters, in a comma-delimited list enclosed in parentheses

  • The function body—that is, the ActionScript code to be executed when the function is called, enclosed in curly brackets

For example, the following code creates a function that defines a parameter and then calls the function using the string “ hello" as the parameter value:

function traceParameter(aParam:String) 
{ 
    trace(aParam); 
} 
 
traceParameter("hello"); // hello

Function expressions

The second way to declare a function is to use an assignment statement with a function expression, which is also sometimes called a function literal or an anonymous function. This is a more verbose method that is widely used in earlier versions of ActionScript.

An assignment statement with a function expression begins with the var keyword, followed by:

  • The function name

  • The colon operator (:)

  • The Function class to indicate the data type

  • The assignment operator (=)

  • The function keyword

  • The parameters, in a comma-delimited list enclosed in parentheses

  • The function body—that is, the ActionScript code to be executed when the function is called, enclosed in curly brackets

    For example, the following code declares the traceParameter function using a function expression:

    var traceParameter:Function = function (aParam:String) 
    { 
        trace(aParam); 
    }; 
    traceParameter("hello"); // hello

    Notice that you do not specify a function name, as you do in a function statement. Another important difference between function expressions and function statements is that a function expression is an expression rather than a statement. This means that a function expression cannot stand on its own as a function statement can. A function expression can be used only as a part of a statement, usually an assignment statement. The following example shows a function expression assigned to an array element:

    var traceArray:Array = new Array(); 
    traceArray[0] = function (aParam:String) 
    { 
        trace(aParam); 
    }; 
    traceArray[0]("hello");

Choosing between statements and expressions

As a general rule, use a function statement unless specific circumstances call for the use of an expression. Function statements are less verbose, and they provide a more consistent experience between strict mode and standard mode than function expressions.

Function statements are easier to read than assignment statements that contain function expressions. Function statements make your code more concise; they are less confusing than function expressions, which require you to use both the var and function keywords.

Function statements provide a more consistent experience between the two compiler modes in that you can use dot syntax in both strict and standard mode to call a method declared using a function statement. This is not necessarily true for methods declared with a function expression. For example, the following code defines a class named Example with two methods: methodExpression(), which is declared with a function expression, and methodStatement(), which is declared with a function statement. In strict mode, you cannot use dot syntax to call the methodExpression() method.

class Example 
{ 
var methodExpression = function() {} 
function methodStatement() {} 
} 
 
var myEx:Example = new Example(); 
myEx.methodExpression(); // error in strict mode; okay in standard mode 
myEx.methodStatement(); // okay in strict and standard modes

Function expressions are considered better suited to programming that focuses on run-time, or dynamic, behavior. If you prefer to use strict mode, but also need to call a method declared with a function expression, you can use either of two techniques. First, you can call the method using square brackets ([]) instead of the dot (.) operator. The following method call succeeds in both strict mode and standard mode:

myExample["methodLiteral"]();

Second, you can declare the entire class as a dynamic class. Although this allows you to call the method using the dot operator, the downside is that you sacrifice some strict mode functionality for all instances of that class. For example, the compiler does not generate an error if you attempt to access an undefined property on an instance of a dynamic class.

There are some circumstances in which function expressions are useful. One common use of function expressions is for functions that are used only once and then discarded. Another less common use is for attaching a function to a prototype property. For more information, see The prototype object.

There are two subtle differences between function statements and function expressions that you should take into account when choosing which technique to use. The first difference is that function expressions do not exist independently as objects with regard to memory management and garbage collection. In other words, when you assign a function expression to another object, such as an array element or an object property, you create the only reference to that function expression in your code. If the array or object to which your function expression is attached goes out of scope or is otherwise no longer available, you no longer have access to the function expression. If the array or object is deleted, the memory that the function expression uses becomes eligible for garbage collection, which means that the memory is eligible to be reclaimed and reused for other purposes.

The following example shows that for a function expression, once the property to which the expression is assigned is deleted, the function is no longer available. The class Test is dynamic, which means that you can add a property named functionExp that holds a function expression. The functionExp() function can be called with the dot operator, but once the functionExp property is deleted, the function is no longer accessible.

dynamic class Test {} 
var myTest:Test = new Test(); 
 
// function expression  
myTest.functionExp = function () { trace("Function expression") }; 
myTest.functionExp(); // Function expression 
delete myTest.functionExp; 
myTest.functionExp(); // error

If, on the other hand, the function is first defined with a function statement, it exists as its own object and continues to exist even after you delete the property to which it is attached. The delete operator only works on properties of objects, so even a call to delete the function stateFunc() itself does not work.

dynamic class Test {} 
var myTest:Test = new Test(); 
 
// function statement 
function stateFunc() { trace("Function statement") } 
myTest.statement = stateFunc; 
myTest.statement(); // Function statement 
delete myTest.statement; 
delete stateFunc; // no effect 
stateFunc();// Function statement 
myTest.statement(); // error

The second difference between function statements and function expressions is that function statements exist throughout the scope in which they are defined, including in statements that appear before the function statement. Function expressions, by contrast, are defined only for subsequent statements. For example, the following code successfully calls the scopeTest() function before it is defined:

statementTest(); // statementTest 
 
function statementTest():void 
{ 
    trace("statementTest"); 
}

Function expressions are not available before they are defined, so the following code results in a run-time error:

expressionTest(); // run-time error 
 
var expressionTest:Function = function () 
{ 
    trace("expressionTest"); 
}

Returning values from functions

To return a value from your function, use the return statement followed by the expression or literal value that you want to return. For example, the following code returns an expression representing the parameter:

function doubleNum(baseNum:int):int 
{ 
    return (baseNum * 2); 
}

Notice that the return statement terminates the function, so that any statements below a return statement are not executed, as follows:

function doubleNum(baseNum:int):int { 
    return (baseNum * 2); 
    trace("after return"); // This trace statement will not be executed. 
}

In strict mode, you must return a value of the appropriate type if you choose to specify a return type. For example, the following code generates an error in strict mode, because it does not return a valid value:

function doubleNum(baseNum:int):int 
{ 
    trace("after return"); 
}

Nested functions

You can nest functions, which means that functions can be declared within other functions. A nested function is available only within its parent function unless a reference to the function is passed to external code. For example, the following code declares two nested functions inside the getNameAndVersion() function:

function getNameAndVersion():String 
{ 
    function getVersion():String 
    { 
        return "10"; 
    } 
    function getProductName():String 
    { 
        return "Flash Player"; 
    } 
    return (getProductName() + " " + getVersion()); 
} 
trace(getNameAndVersion()); // Flash Player 10

When nested functions are passed to external code, they are passed as function closures, which means that the function retains any definitions that are in scope when the function is defined. For more information, see Function scope.

Function parameters

ActionScript 3.0 provides some functionality for function parameters that may seem novel for programmers new to the language. Although the idea of passing parameters by value or reference should be familiar to most programmers, the arguments object and the ... (rest) parameter may be new to many of you.

Passing arguments by value or by reference

In many programming languages, it’s important to understand the distinction between passing arguments by value or by reference; the distinction can affect the way code is designed.

To be passed by value means that the value of the argument is copied into a local variable for use within the function. To be passed by reference means that only a reference to the argument is passed instead of the actual value. No copy of the actual argument is made. Instead, a reference to the variable passed as an argument is created and assigned to a local variable for use within the function. As a reference to a variable outside the function, the local variable gives you the ability to change the value of the original variable.

In ActionScript 3.0, all arguments are passed by reference, because all values are stored as objects. However, objects that belong to the primitive data types, which includes Boolean, Number, int, uint, and String, have special operators that make them behave as if they were passed by value. For example, the following code creates a function named passPrimitives() that defines two parameters named xParam and yParam, both of type int. These parameters are similar to local variables declared inside the body of the passPrimitives() function. When the function is called with the arguments xValue and yValue, the parameters xParam and yParam are initialized with references to the int objects represented by xValue and yValue. Because the arguments are primitives, they behave as if passed by value. Although xParam and yParam initially contain only references to the xValue and yValue objects, any changes to the variables within the function body generate new copies of the values in memory.

function passPrimitives(xParam:int, yParam:int):void 
{ 
    xParam++; 
    yParam++; 
    trace(xParam, yParam); 
} 
 
var xValue:int = 10; 
var yValue:int = 15; 
trace(xValue, yValue);// 10 15 
passPrimitives(xValue, yValue); // 11 16 
trace(xValue, yValue);// 10 15

Within the passPrimitives() function, the values of xParam and yParam are incremented, but this does not affect the values of xValue and yValue, as shown in the last trace statement. This would be true even if the parameters were named identically to the variables, xValue and yValue, because the xValue and yValue inside the function would point to new locations in memory that exist separately from the variables of the same name outside the function.

All other objects—that is, objects that do not belong to the primitive data types—are always passed by reference, which gives you ability to change the value of the original variable. For example, the following code creates an object named objVar with two properties, x and y. The object is passed as an argument to the passByRef() function. Because the object is not a primitive type, the object is not only passed by reference, but also stays a reference. This means that changes made to the parameters within the function affect the object properties outside the function.

function passByRef(objParam:Object):void 
{ 
    objParam.x++; 
    objParam.y++; 
    trace(objParam.x, objParam.y); 
} 
var objVar:Object = {x:10, y:15}; 
trace(objVar.x, objVar.y); // 10 15 
passByRef(objVar); // 11 16 
trace(objVar.x, objVar.y); // 11 16

The objParam parameter references the same object as the global objVar variable. As you can see from the trace statements in the example, changes to the x and y properties of the objParam object are reflected in the objVar object.

Default parameter values

In ActionScript 3.0, you can declare default parameter values for a function. If a call to a function with default parameter values omits a parameter with default values, the value specified in the function definition for that parameter is used. All parameters with default values must be placed at the end of the parameter list. The values assigned as default values must be compile-time constants. The existence of a default value for a parameter effectively makes that parameter an optional parameter. A parameter without a default value is considered a required parameter.

For example, the following code creates a function with three parameters, two of which have default values. When the function is called with only one parameter, the default values for the parameters are used.

function defaultValues(x:int, y:int = 3, z:int = 5):void 
{ 
    trace(x, y, z); 
} 
defaultValues(1); // 1 3 5

The arguments object

When parameters are passed to a function, you can use the arguments object to access information about the parameters passed to your function. Some important aspects of the arguments object include the following:

  • The arguments object is an array that includes all the parameters passed to the function.

  • The arguments.length property reports the number of parameters passed to the function.

  • The arguments.callee property provides a reference to the function itself, which is useful for recursive calls to function expressions.

    Note: The arguments object is not available if any parameter is named arguments or if you use the ... (rest) parameter.

    If the arguments object is referenced in the body of a function, ActionScript 3.0 allows function calls to include more parameters than those defined in the function definition, but generates a compiler error in strict mode if the number of parameters doesn’t match the number of required parameters (and optionally, any optional parameters). You can use the array aspect of the arguments object to access any parameter passed to the function, whether or not that parameter is defined in the function definition. The following example, which only compiles in standard mode, uses the arguments array along with the arguments.length property to trace all the parameters passed to the traceArgArray() function:

    function traceArgArray(x:int):void 
    { 
        for (var i:uint = 0; i < arguments.length; i++) 
        { 
            trace(arguments[i]); 
        } 
    } 
     
    traceArgArray(1, 2, 3); 
     
    // output: 
    // 1 
    // 2 
    // 3

    The arguments.callee property is often used in anonymous functions to create recursion. You can use it to add flexibility to your code. If the name of a recursive function changes over the course of your development cycle, you need not worry about changing the recursive call in your function body if you use arguments.callee instead of the function name. The arguments.callee property is used in the following function expression to enable recursion:

    var factorial:Function = function (x:uint) 
    { 
        if(x == 0) 
        { 
            return 1; 
        } 
        else 
        { 
            return (x * arguments.callee(x - 1)); 
        } 
    } 
     
    trace(factorial(5)); // 120

    If you use the ... (rest) parameter in your function declaration, the arguments object is not available to you. Instead, you must access the parameters using the parameter names that you declared for them.

    You should also be careful to avoid using the string "arguments" as a parameter name, because it shadows the arguments object. For example, if the function traceArgArray() is rewritten so that an arguments parameter is added, the references to arguments in the function body refer to the parameter rather than the arguments object. The following code produces no output:

    function traceArgArray(x:int, arguments:int):void 
    { 
        for (var i:uint = 0; i < arguments.length; i++) 
        { 
            trace(arguments[i]); 
        } 
    } 
     
    traceArgArray(1, 2, 3); 
     
    // no output

    The arguments object in previous versions of ActionScript also contained a property named caller, which is a reference to the function that called the current function. The caller property is not present in ActionScript 3.0, but if you need a reference to the calling function, you can alter the calling function so that it passes an extra parameter that is a reference to itself.

The ... (rest) parameter

ActionScript 3.0 introduces a new parameter declaration called the ... (rest) parameter. This parameter allows you to specify an array parameter that accepts any number of comma- delimited arguments. The parameter can have any name that is not a reserved word. This parameter declaration must be the last parameter specified. Use of this parameter makes the arguments object unavailable. Although the ... (rest) parameter gives you the same functionality as the arguments array and arguments.length property, it does not provide functionality similar to that provided by arguments.callee. You should ensure that you do not need to use arguments.callee before using the ... (rest) parameter.

The following example rewrites the traceArgArray() function using the ... (rest) parameter instead of the arguments object:

function traceArgArray(... args):void 
{ 
    for (var i:uint = 0; i < args.length; i++) 
    { 
        trace(args[i]); 
    } 
} 
 
traceArgArray(1, 2, 3); 
 
// output: 
// 1 
// 2 
// 3

The ... (rest) parameter can also be used with other parameters, as long as it is the last parameter listed. The following example modifies the traceArgArray() function so that its first parameter, x, is of type int, and the second parameter uses the ... (rest) parameter. The output skips the first value, because the first parameter is no longer part of the array created by the ... (rest) parameter.

function traceArgArray(x: int, ... args) 
{ 
    for (var i:uint = 0; i < args.length; i++) 
    { 
        trace(args[i]); 
    } 
} 
 
traceArgArray(1, 2, 3); 
 
// output: 
// 2 
// 3

Functions as objects

Functions in ActionScript 3.0 are objects. When you create a function, you are creating an object that not only can be passed as a parameter to another function, but also can have properties and methods attached to it.

Functions passed as arguments to another function are passed by reference and not by value. When you pass a function as an argument, you use only the identifier and not the parentheses operator that you use to call the method. For example, the following code passes a function named clickListener() as an argument to the addEventListener() method:

addEventListener(MouseEvent.CLICK, clickListener);

Although it may seem strange to programmers new to ActionScript, functions can have properties and methods, just as any other object can. In fact, every function has a read-only property named length that stores the number of parameters defined for the function. This is different from the arguments.length property, which reports the number of arguments sent to the function. Recall that in ActionScript, the number of arguments sent to a function can exceed the number of parameters defined for that function. The following example, which compiles only in standard mode because strict mode requires an exact match between the number of arguments passed and the number of parameters defined, shows the difference between the two properties:

// Compiles only in standard mode 
function traceLength(x:uint, y:uint):void 
{ 
    trace("arguments received: " + arguments.length); 
    trace("arguments expected: " + traceLength.length); 
} 
 
traceLength(3, 5, 7, 11); 
/* output: 
arguments received: 4 
arguments expected: 2 */

In standard mode, you can define your own function properties by defining them outside your function body. Function properties can serve as quasi-static properties that allow you to save the state of a variable related to the function. For example, you may want to track the number of times a particular function is called. Such functionality could be useful if you are writing a game and want to track the number of times a user uses a specific command, although you could also use a static class property for this. The following example, which compiles only in standard mode because strict mode does not allow you to add dynamic properties to functions, creates a function property outside the function declaration and increments the property each time the function is called:

// Compiles only in standard mode 
var someFunction:Function = function ():void 
{ 
    someFunction.counter++; 
} 
 
someFunction.counter = 0; 
 
someFunction(); 
someFunction(); 
trace(someFunction.counter); // 2

Function scope

A function’s scope determines not only where in a program that function can be called, but also what definitions the function can access. The same scope rules that apply to variable identifiers apply to function identifiers. A function declared in the global scope is available throughout your code. For example, ActionScript 3.0 contains global functions, such as isNaN() and parseInt(), that are available anywhere in your code. A nested function—a function declared within another function—can be used anywhere in the function in which it was declared.

The scope chain

Any time a function begins execution, a number of objects and properties are created. First, a special object called an activation object is created that stores the parameters and any local variables or functions declared in the function body. You cannot access the activation object directly, because it is an internal mechanism. Second, a scope chain is created that contains an ordered list of objects that the runtime checks for identifier declarations. Every function that executes has a scope chain that is stored in an internal property. For a nested function, the scope chain starts with its own activation object, followed by its parent function’s activation object. The chain continues in this manner until it reaches the global object. The global object is created when an ActionScript program begins, and contains all global variables and functions.

Function closures

A function closure is an object that contains a snapshot of a function and its lexical environment. A function’s lexical environment includes all the variables, properties, methods, and objects in the function’s scope chain, along with their values. Function closures are created any time a function is executed apart from an object or a class. The fact that function closures retain the scope in which they were defined creates interesting results when a function is passed as an argument or a return value into a different scope.

For example, the following code creates two functions: foo(), which returns a nested function named rectArea() that calculates the area of a rectangle, and bar(), which calls foo() and stores the returned function closure in a variable named myProduct. Even though the bar() function defines its own local variable x (with a value of 2), when the function closure myProduct() is called, it retains the variable x (with a value of 40) defined in function foo(). The bar() function therefore returns the value 160 instead of 8.

function foo():Function 
{ 
    var x:int = 40; 
    function rectArea(y:int):int // function closure defined 
    { 
        return x * y 
    }  
    return rectArea; 
} 
function bar():void 
{ 
    var x:int = 2; 
    var y:int = 4; 
    var myProduct:Function = foo(); 
    trace(myProduct(4)); // function closure called 
} 
bar(); // 160

Methods behave similarly in that they also retain information about the lexical environment in which they were created. This characteristic is most noticeable when a method is extracted from its instance, which creates a bound method. The main difference between a function closure and a bound method is that the value of the this keyword in a bound method always refers to the instance to which it was originally attached, whereas in a function closure the value of the this keyword can change.