Funkcje

Funkcje są to bloki kodu realizujące konkretne zadania i przystosowane do wielokrotnego wykorzystania w programie. W języku ActionScript 3.0 wyróżnia się dwa typy funkcji: metody i zamknięcia funkcji . To, czy funkcja jest nazywana metodą, czy zamknięciem funkcji, zależy od kontekstu, w jakim została zdefiniowana. Funkcja jest nazywana metodą, jeśli została zdefiniowana w ramach definicji klasy lub jest powiązana z instancją obiektu. Funkcja jest nazywana zamknięciem funkcji, jeśli jest zdefiniowana w inny sposób.

Funkcje były zawsze bardzo ważnym elementem języka ActionScript. Na przykład w języku ActionScript 1.0 nie istniało słowo class , dlatego „klasy” definiowało się za pomocą funkcji-konstruktorów. Mimo że od tamtej pory do języka dodano słowo class , pełne zrozumienie istoty funkcji nadal jest ważne dla programistów, którzy chcieliby w pełni wykorzystać potencjał języka. Początkowo może to stanowić pewien problem dla programistów, którzy oczekują, że funkcje w języku ActionScript będą zachowywały się podobnie, jak w języku C++ lub Java. Mimo że podstawowe zasady definiowania i wywoływania funkcji nie powinny sprawiać trudności doświadczonym programistom, pewne bardziej zaawansowane cechy funkcji języka ActionScript wymagają bliższego wyjaśnienia.

Podstawowe pojęcia związane z funkcjami

Wywoływanie funkcji

Wywołanie funkcji ma postać jej identyfikatora z dodanymi nawiasami ( () ). Operator nawiasów obejmuje wszelkie parametry, które chcemy przekazać do funkcji. Na przykład: funkcja trace() jest funkcją najwyższego poziomu języka ActionScript 3.0:

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

W przypadku wywołania funkcji bez parametrów należy za identyfikatorem funkcji umieścić pustą parę nawiasów. Na przykład do wygenerowania liczby losowej możemy użyć funkcji Math.random() , która nie ma parametrów:

var randomNum:Number = Math.random();

Definiowanie własnych funkcji

W języku ActionScript 3.0 istnieją dwa sposoby definiowania funkcji: użycie instrukcji funkcyjnej lub wyrażenia funkcyjnego. To, którą technikę wybierzemy, zależy od preferowanego stylu programowania: bardziej statycznego lub bardziej dynamicznego. Programiści, którzy preferują styl statyczny, ścisły, definiują funkcje za pomocą instrukcji funkcyjnych. Użycie wyrażeń funkcyjnych do definiowania funkcji jest uzasadnione specyficznymi potrzebami. Wyrażenia funkcyjne są częściej używane w stylu dynamicznym, w standardowym trybie kompilacji.

Instrukcje funkcyjne

Instrukcje funkcyjne to preferowana technika definiowania funkcji w trybie ścisłym. Instrukcja funkcyjna rozpoczyna się od słowa kluczowego function , po którym następują:

  • nazwa funkcji;

  • lista parametrów oddzielonych przecinkami, ujęta w nawiasy;

  • treść funkcji — tzn. kod w języku ActionScript, który ma być wykonywany po wywołaniu funkcji — ujęta w nawiasy sześcienne.

Na przykład poniższy kod tworzy funkcję, w której zdefiniowany jest parametr. Następnie wywołuje funkcję, przekazując jako wartość parametru ciąg znaków “ hello" :

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

Wyrażenia funkcyjne

Drugi sposób deklarowania funkcji polega na użyciu instrukcji przypisania z wyrażeniem funkcyjnym, które czasami bywa nazywane literałem funkcyjnym lub funkcją anonimową. Jest to bardziej rozwlekła metoda zapisu, szeroko stosowana we wcześniejszych wersjach języka ActionScript.

Instrukcja przypisania z wyrażeniem funkcyjnym rozpoczyna się od słowa kluczowego var , po którym następują:

  • nazwa funkcji;

  • operator dwukropka ( : );

  • klasa Function wskazująca typ danych;

  • operator przypisania ( = );

  • słowo kluczowe function ;

  • lista parametrów oddzielonych przecinkami, ujęta w nawiasy;

  • treść funkcji — tzn. kod w języku ActionScript, który ma być wykonywany po wywołaniu funkcji — ujęta w nawiasy sześcienne.

    W poniższym przykładzie zadeklarowano funkcję traceParameter , korzystając z wyrażenia funkcyjnego:

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

    Należy zwrócić uwagę, że w tym przypadku nie określamy nazwy funkcji, tak jak miało to miejsce w instrukcji funkcyjnej. Inna ważna różnica między instrukcjami funkcyjnymi a wyrażeniami funkcyjnymi polega na tym, że wyrażenie funkcyjne jest wyrażeniem, a nie instrukcją. Oznacza to, że wyrażenie funkcyjne nie może występować w kodzie autonomicznie, natomiast instrukcja funkcyjna — może. Wyrażenie funkcyjne może być użyte tylko w ramach instrukcji, zwykle instrukcji przypisania. Poniższy przykład ilustruje wyrażenie funkcyjne przypisane do elementu tablicy:

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

Wybór między instrukcjami a wyrażeniami

Co do zasady należy używać instrukcji funkcyjnych, chyba że szczególne okoliczności nakazują użycie wyrażenia. Instrukcje funkcyjne są bardziej zwięzłe i zachowują się podobnie w trybie ścisłym i w trybie standardowym, co odróżnia je od wyrażeń funkcyjnych.

Instrukcje funkcyjne są bardziej czytelne niż instrukcje przypisania zawierające wyrażenia funkcyjne. Instrukcje funkcyjne sprawiają, że kod jest bardziej zwięzły; są bardziej jednoznaczne niż wyrażenia funkcyjne, które wymagają zastosowania zarówno słowa kluczowego var , jak i function .

Instrukcje funkcyjne są traktowane podobnie w obu trybach kompilatora, tzn. zarówno w trybie ścisłym, jak i standardowym, dozwolone jest używanie kropki do wywoływania metod zadeklarowanych za pomocą instrukcji funkcyjnych. Nie zawsze dotyczy to metod zadeklarowanych za pomocą metod funkcyjnych. W poniższym przykładzie zadeklarowano klasę Example z dwiema metodami: methodExpression() , która jest zadeklarowana za pomocą wyrażenia funkcyjnego, oraz methodStatement() , która jest zadeklarowana za pomocą instrukcji funkcyjnej. W trybie ścisłym nie jest dozwolone użycie składni z kropką do wywołania metody methodExpression() .

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

Wyrażenia funkcyjne uważa się za lepiej przystosowane do programowania zorientowanego na zachowanie dynamiczne w czasie wykonywania. Programista, który preferuje pracę w trybie ścisłym, ale musi wywołać metodę zadeklarowaną za pomocą wyrażenia funkcyjnego, ma do wyboru dwie techniki. Po pierwsze, można wywołać metodę, używając nawiasów kwadratowych ( [] ) zamiast kropki ( . ). Poniższe wywołanie metody zostanie pomyślnie skompilowane zarówno w trybie ścisłym, jak i standardowym:

myExample["methodLiteral"]();

Po drugie — można zadeklarować całą klasę jako dynamiczną. Mimo że umożliwi to wywoływanie metody za pomocą operatora kropki, wadą takiego rozwiązania jest rezygnacja z części funkcjonalności trybu ścisłego w odniesieniu do wszystkich instancji tej klasy. Kompilator nie zgłosi na przykład błędu, gdy spróbujemy uzyskać dostęp do niezdefiniowanej właściwości w instancji klasy dynamicznej.

Istnieją pewne okoliczności, w których wyrażenia funkcyjne okazują się użyteczne. Jednym z typowych zastosowań wyrażeń funkcyjnych są funkcje używane tylko raz, a następnie likwidowane. Rzadziej spotykane zastosowanie polega na powiązaniu funkcji z właściwością prototypową. Więcej informacji zawiera sekcja Obiekt prototypowy .

Istnieją także dwie subtelne różnice między instrukcjami funkcyjnymi a wyrażeniami funkcyjnymi, które należy wziąć pod uwagę, dokonując wyboru techniki. Pierwsza różnica polega na tym, że wyrażenia funkcyjne nie istnieją jako odrębne obiekty z perspektywy zarządzania pamięcią i czyszczenia pamięci. Innymi słowy, gdy przypisujemy wyrażenie funkcyjne do innego obiektu, np. elementu tablicy lub właściwości obiektu, tworzymy jedyne odwołanie do tego wyrażenia funkcyjnego, jakie istnieje w kodzie. Jeśli tablica lub obiekt, z którym powiązane jest wyrażenie, znajdzie się poza zasięgiem lub z innego powodu przestanie być dostępne, utracimy dostęp do wyrażenia funkcyjnego. Usunięcie tablicy lub obiektu spowoduje, że pamięć zajęta przez wyrażenie funkcyjne zostanie oznaczona jako przeznaczona do wyczyszczenia, a w konsekwencji będzie ją można odzyskać i wykorzystać w innym celu.

Poniższy przykład demonstruje fakt, że po usunięciu właściwości, do której przypisane jest wyrażenie funkcyjne, funkcja przestaje być dostępna. Klasa Test jest klasą dynamiczną, a zatem możemy do niej dodać właściwość o nazwie functionExp zawierającą wyrażenie funkcyjne. Funkcję functionExp() można wywoływać, posługując się kropką, jednak po usunięciu właściwości functionExp funkcja ta przestaje być dostępna.

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

Jeśli natomiast funkcja zostanie najpierw zdefiniowana za pomocą instrukcji funkcyjnej, to będzie istnieć jako autonomiczny obiekt, nawet po usunięciu właściwości, do której została przypisana. Operator delete działa tylko na właściwościach obiektów, a zatem nawet jawne usunięcie funkcji stateFunc() nie odniesie skutku.

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

Druga różnica między instrukcjami funkcyjnymi a wyrażeniami funkcyjnymi polega na tym, że instrukcje funkcyjne istnieją w całym zasięgu, w jakim są zdefiniowane, również we wszystkich instrukcjach poprzedzających taką instrukcję funkcyjną. Z kolei wyrażenia funkcyjne są zdefiniowane tylko w instrukcjach następujących po nich. Na przykład poniższy kod pomyślnie wywołuje funkcję scopeTest() zanim została ona zdefiniowana:

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

Wyrażenia funkcyjne są niedostępne, zanim nie zostaną zdefiniowane, a zatem następujący kod spowoduje zgłoszenie błędu w czasie wykonywania:

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

Zwracanie wartości z funkcji

W celu zwrócenia wartości z funkcji należy użyć instrukcji return , po której powinno następować wyrażenie lub wartość literalna przeznaczona do zwrócenia. Poniższy kod przykładowy zwraca wyrażenie reprezentujące parametr:

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

Należy zwrócić uwagę, że instrukcja return kończy wykonywanie funkcji, a zatem instrukcje następujące po instrukcji return nie zostaną wykonane, co ilustruje poniższy przykład:

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

W trybie ścisłym, jeśli zadeklarowano typ wartości zwracanej przez funkcję, to funkcja musi zwracać wartość odpowiedniego typu. Na przykład poniższy kod spowoduje zgłoszenie błędu w trybie ścisłym, ponieważ nie zwraca właściwej wartości:

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

Funkcje zagnieżdżone

Istnieje możliwość zagnieżdżania funkcji, tzn. deklarowania funkcji wewnątrz innych funkcji. Funkcja zagnieżdżona jest dostępna tylko wewnątrz swojej funkcji nadrzędnej, chyba że odwołanie do funkcji zostanie przekazane do kodu zewnętrznego. W poniższym przykładowym kodzie zadeklarowano dwie funkcje zagnieżdżone wewnątrz funkcji getNameAndVersion() :

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

Funkcje zagnieżdżone są przekazywane do kodu zewnętrznego jako zamknięcia funkcji, tzn. zachowują wszelkie definicje pozostające w zasięgu, w którym funkcja była zdefiniowana. Więcej informacji zawiera sekcja Zasięg funkcji .

Parametry funkcji

Język ActionScript 3.0 oferuje w odniesieniu do parametrów funkcji pewne rozwiązania, które mogą stanowić nowość dla programistów, którzy dotychczas nie zetknęli się z tym językiem. Mimo że koncepcja przekazywania parametrów przez wartość lub przez odwołanie (referencję) powinna być znana większości programistów, to wielu czytelników prawdopodobnie nie zetknęło się wcześniej z obiektem arguments oraz parametrem ... (rest).

Przekazywanie argumentów przez wartość lub przez odwołanie

W wielu językach programowania bardzo istotne jest rozróżnienie między przekazywaniem argumentów przez wartość a przekazywaniem ich przez odwołanie. Różnica ta wpływa na konstrukcję kodu.

Przekazanie przez wartość oznacza, że wartość argumentu jest kopiowana do zmiennej lokalnej używanej wewnątrz funkcji. Przekazanie przez odwołanie oznacza przekazanie wyłącznie odwołania (wskaźnika) do argumentu, a nie jego faktycznej wartości. Argument nie jest kopiowany. Tworzone jest natomiast odwołanie do zmiennej przekazanej jako argument i odwołanie to jest przypisywane zmiennej lokalnej używanej wewnątrz funkcji. Jako odwołanie do zmiennej znajdującej się poza funkcją, zmienna lokalna umożliwia zmianę wartości pierwotnej zmiennej.

W języku ActionScript 3.0 wszystkie argumenty są przekazywane przez odwołanie, ponieważ wszystkie wartości są przechowywane jako obiekty. Jednak obiekty należące do pierwotnych typów danych, czyli Boolean, Number, int, uint oraz String, mają specjalne operatory sprawiające, że zachowują się, jak gdyby były przekazywane przez wartość. Poniższy przykładowy kod tworzy funkcję o nazwie passPrimitives() , która definiuje dwa parametry o nazwach xParam i yParam , oba typu int. Parametry te są podobne do zmiennych lokalnych zadeklarowanych w treści funkcji passPrimitives() . Gdy funkcja zostaje wywołana z argumentami xValue i yValue , parametry xParam i yParam są inicjowane odwołaniami do obiektów int reprezentowanych przez argumenty xValue i yValue . Ponieważ argumenty należą do typu pierwotnego, zachowują się tak, jak gdyby były przekazywane przez wartość. Mimo że parametry xParam i yParam początkowo zawierały tylko odwołania do obiektów xValue i yValue , wszelkie modyfikacje zmiennych w treści funkcji powodują utworzenie nowych kopii wartości w pamięci.

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

Wewnątrz funkcji passPrimitives() wartości xParam i yParam są inkrementowane, ale nie wpływa to na wartość xValue i yValue , czego dowodzi wynik ostatniej instrukcji trace . Kod działałby tak samo nawet wtedy, gdyby parametry miały nazwy identyczne z nazwami zmiennych xValue i yValue , ponieważ nazwy xValue i yValue wewnątrz funkcji wskazywałyby na nowe miejsca w pamięci, autonomiczne względem zmiennych o tych samych nazwach poza funkcją.

Wszystkie pozostałe obiekty — tj. obiekty nienależące do pierwotnych typów danych — są zawsze przekazywane przez odwołanie, co umożliwia zmianę wartości oryginalnej zmiennej. Poniższy przykładowy kod tworzy obiekt o nazwie objVar z dwiema właściwościami: x i y . Obiekt ten jest przekazywany jako argument do funkcji passByRef() . Ponieważ obiekt nie należy do typu pierwotnego, jest nie tylko przekazywany przez odwołanie, lecz również pozostaje odwołaniem. Oznacza to, że modyfikacja parametrów wewnątrz funkcji wpłynie na właściwości obiektów poza funkcją.

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

Parametr objParam odwołuje się do tego samego obiektu, co zmienna globalna objVar . Jak wynika z wyników instrukcji trace w przykładowym kodzie, zmiany właściwości x i y obiektu objParam są odzwierciedlone w obiekcie objVar .

Domyślne wartości parametrów

W języku ActionScript 3.0 można zadeklarować domyślne właściwości parametrów dla funkcji. Jeśli w wywołaniu funkcji mającej wartości domyślne parametrów zostaną pominięte takie parametry, przyjmowane są wartości podane w definicji funkcji. Wszystkie parametry z wartościami domyślnymi muszą być umieszczone na końcu listy parametrów. Przypisywane wartości domyślne muszą być stałymi znanymi w czasie kompilacji. Istnienie wartości domyślnej parametru powoduje, że parametr ten automatycznie staje się parametrem opcjonalnym . Parametr bez wartości jest uznawany za parametr wymagany .

Poniższy przykładowy kod tworzy funkcję z trzema parametrami, z których dwa mają wartości domyślne. Gdy funkcja zostanie wywołana tylko z jednym parametrem, dla pozostałych dwóch parametrów przyjęte zostaną wartości domyślne.

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

Obiekt arguments

Wewnątrz funkcji, do której przekazywane są parametry, można skorzystać z obiektu arguments , aby uzyskać dostęp do informacji o przekazanych parametrach. Oto niektóre ważne aspekty użycia obiektu arguments :

  • Obiekt arguments jest tablicą zawierającą wszystkie parametry przekazane do funkcji.

  • Właściwość arguments.length zawiera liczbę argumentów przekazanych do funkcji.

  • Właściwość arguments.callee zawiera odwołanie do samej funkcji, co bywa przydatne przy rekurencyjnych wywołaniach wyrażeń funkcyjnych.

    Uwaga: Obiekt arguments nie jest dostępny, jeśli którykolwiek z parametrów ma nazwę arguments lub jeśli używany jest parametr ... (rest).

    Jeśli do obiektu arguments istnieją odwołania w treści funkcji, wówczas w języku ActionScript 3.0 dozwolone jest wywoływanie funkcji z większą liczbą parametrów niż podana w definicji funkcji. Jednak w trybie dokładnym kompilator zgłosi błąd, jeśli liczba parametrów nie będzie zgodna z liczbą parametrów wymaganych (i ewentualnie parametrów opcjonalnych). Korzystając z obiektu arguments jak z tablicy, można odwołać się do dowolnego parametru przekazanego do funkcji, niezależnie od tego, czy został wymieniony w definicji funkcji. Poniższy przykład, który daje się skompilować bezbłędnie tylko w trybie standardowym, ilustruje użycie tablicy arguments oraz właściwości arguments.length do wyświetlenia wszystkich parametrów przekazanych do funkcji traceArgArray() :

    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

    Właściwość arguments.callee jest często używana w funkcjach anonimowych do uzyskania rekurencji. Takie rozwiązanie pozwala na bardziej elastyczne programowanie. Jeśli w toku prac nad programem nazwa funkcji rekurencyjnej ulegnie zmianie, nie będzie konieczne modyfikowanie wywołania rekurencyjnego w treści funkcji — wystarczy zamiast nazwy użyć odwołania arguments.callee . Właściwość arguments.callee została użyta w poniższym wyrażeniu funkcyjnym, umożliwiając rekurencję:

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

    Jeśli deklaracja funkcji zawiera parametr ... (rest), to obiekt arguments nie jest dostępny. Do parametrów należy się wówczas odwoływać za pomocą nazw, pod jakimi zostały zadeklarowane.

    Należy również unikać używania ciągu znaków "arguments" w charakterze nazwy parametru, ponieważ taki parametr przesłoni obiekt arguments . Przykładowo, jeśli funkcja traceArgArray() zostanie zmodyfikowana poprzez dodanie parametru arguments , odwołania do obiektu arguments w treści funkcji będą teraz dotyczyły tego parametru, a nie obiektu arguments . Poniższy kod nie spowoduje wyświetlenia żadnych wyników.

    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

    Obiekt arguments w poprzednich wersjach języka ActionScript zawierał też właściwość o nazwie caller będącą odwołaniem do funkcji, która wywołała bieżącą funkcję. Właściwość caller nie występuje w języku ActionScript 3.0, ale jeśli musimy odwołać się do funkcji wywołującej, możemy ją tak zmodyfikować, aby przekazywała odwołanie do samej siebie jako dodatkowy parametr.

Parametr ... (rest)

W języku ActionScript 3.0 wprowadzono możliwość zadeklarowania nowego rodzaju parametru o nazwie ... (rest). Umożliwia on podanie jako argumentu jednej tablicy zawierającej dowolną liczbę argumentów oddzielonych przecinkami. Parametr może mieć dowolną nazwę niebędącą słowem zastrzeżonym. Deklaracja takiego parametru musi być ostatnia na liście deklaracji. Użycie tego parametru powoduje, że obiekt arguments staje się niedostępny. Mimo że parametr... (rest) oferuje tę samą funkcjonalność, co tablica arguments i właściwość arguments.length , nie oferuje funkcjonalności właściwości arguments.callee . Decydując się na użycie parametru ... (rest), należy mieć świadomość, że uniemożliwi to korzystanie z właściwości arguments.callee .

W poniższym przykładzie funkcja traceArgArray() została zmodyfikowana w taki sposób, że zamiast obiektu arguments używany jest w niej parametr ... (rest):

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

Parametru ... (rest) można również używać z innymi parametrami, o ile będzie on ostatni na liście parametrów. W poniższym przykładzie funkcja traceArgArray() została zmodyfikowana w taki sposób, że pierwszy parametr, x , jest typu int, a drugi parametr jest parametrem ... (rest). Wyniki nie zawierają pierwszej wartości, ponieważ pierwszy parametr nie należy już do tablicy utworzonej przez parametr ... (rest).

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

Funkcje jako obiekty

W języku ActionScript 3.0 funkcje są obiektami. Utworzenie funkcji jest równoznaczne z utworzeniem obiektu, który nie tylko można przekazywać jako parametr do innej funkcji, lecz który może mieć również własne właściwości i metody.

Funkcje jako argumenty innych funkcji są przekazywane przez odwołanie, a nie przez wartość. Przekazując funkcję jako argument, podajemy tylko identyfikator, bez nawiasów używanych w wywołaniu metody. W poniższym przykładowym kodzie funkcja o nazwie clickListener() jest przekazywana jako argument do metody addEventListener() :

addEventListener(MouseEvent.CLICK, clickListener);

Mimo że programistom, którzy po raz pierwszy stykają się z językiem ActionScript, często wydaje się to dziwne, funkcje mogą mieć właściwości i metody — tak samo, jak każdy inny obiekt. W istocie każda funkcja ma właściwość o nazwie length przeznaczoną tylko do odczytu, w której przechowywana jest liczba parametrów zdefiniowanych dla funkcji. Należy odróżnić tę właściwość od właściwości arguments.length , która zawiera liczbę argumentów faktycznie przekazanych do funkcji. Jak pamiętamy, w języku ActionScript liczba argumentów przekazanych do funkcji może być większa od liczby parametrów zdefiniowanych dla tej funkcji. Poniższy przykład, który można skompilować bezbłędnie tylko w trybie standardowym (gdyż w trybie ścisłym wymagana jest dokładna zgodność liczby przekazanych argumentów z liczbą zdefiniowanych parametrów), ilustruje różnicę między tymi dwiema właściwościami:

// 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 */

W trybie standardowym możliwe jest definiowanie własnych właściwości funkcji poza treścią funkcji. Właściwości funkcji mogą pełnić rolę właściwości quasi-statycznych, pozwalając na zapisanie stanu zmiennej związanej z funkcją. Na przykład można w ten sposób rejestrować liczbę wywołań funkcji. Taka funkcjonalność byłaby przydatna w grze, w której chcemy rejestrować każde użycie określonego polecenia przez użytkownika. Jednak ten sam efekt uzyskalibyśmy, stosując właściwość klasy statycznej. Poniższy kod przykładowy, który da się skompilować bez błędów tylko w trybie standardowym (ponieważ tryb ścisły nie zezwala na dodawanie właściwości dynamicznych do funkcji) tworzy właściwość funkcji poza jej deklaracją i inkrementuje tę właściwość przy każdym wywołaniu funkcji:

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

Zasięg funkcji

Zasięg funkcji określa nie tylko to, w których miejscach kodu funkcję można wywoływać, lecz także do jakich definicji funkcja ma dostęp. Identyfikatory funkcji podlegają tym samym regułom zasięgu, co identyfikatory zmiennych. Funkcja zadeklarowana w zasięgu globalnym jest dostępna w całym kodzie. Na przykład w języku ActionScript 3.0 zdefiniowane są funkcje globalne, takie jak isNaN() i parseInt() , z których można korzystać w dowolnym miejscu kodu. Funkcja zagnieżdżona — czyli funkcja zadeklarowana wewnątrz innej funkcji — może być używana w dowolnym miejscu funkcji, w którym została zadeklarowana.

Łańcuch zasięgów

Za każdym razem, gdy rozpoczyna się wykonywanie funkcji, tworzona jest pewna liczba obiektów i właściwości. Po pierwsze, tworzony jest specjalny obiekt nazywany obiektem aktywacji , w którym przechowywane są parametry i wszelkie zmienne lokalne lub funkcje zadeklarowane w treści funkcji. Obiekt aktywacji nie jest dostępny bezpośrednio, ponieważ stanowi mechanizm wewnętrzny. Po drugie: tworzony jest łańcuch zasięgów zawierający uporządkowaną listę obiektów, w których poszukiwane są deklaracje identyfikatorów. Każda wykonywana funkcja ma swój łańcuch zasięgu przechowywany we właściwości wewnętrznej. W przypadku funkcji zagnieżdżonych łańcuch zasięgu rozpoczyna się od jej własnego obiektu aktywacji, po którym następuje obiekt aktywacji funkcji nadrzędnej. Łańcuch jest kontynuowany na tej zasadzie, aż do osiągnięcia obiektu globalnego. Obiekt globalny jest tworzony z chwilą rozpoczęcia wykonywania programu w języku ActionScript i zawiera wszystkie zmienne i funkcje globalne.

Zamknięcia funkcji

Zamknięcie funkcji to obiekt zawierający obraz stanu funkcji i jej środowiska leksykalnego . Środowisko leksykalne funkcji zawiera wszystkie zmienne, właściwości, metody i obiekty w łańcuchu zasięgów funkcji oraz ich wartości. Zamknięcia funkcji tworzone są za każdym razem, gdy funkcja jest wykonywana w oderwaniu od obiektu lub klasy. Fakt, że zamknięcia funkcji zachowują zasięg, w którym zostały zdefiniowane, prowadzi do interesujących skutków przekazania funkcji jako argumentu lub wartości zwracanej do innego zasięgu.

Przykładowo, w poniższym kodzie tworzone są dwie funkcje: foo() , która zwraca funkcję zagnieżdżoną o nazwie rectArea() , obliczającą pole powierzchni prostokąta, oraz bar() , która wywołuje funkcję foo() i zapisuje zwrócone zamknięcie funkcji w zmiennej o nazwie myProduct . Mimo że funkcja bar() definiuje własną zmienną lokalną x (o wartości 2), w wywołanym zamknięciu funkcji myProduct() zachowana jest zmienna x (o wartości 40) zdefiniowana w funkcji foo(). Funkcja bar() zwraca zatem wartość 160 zamiast wartości 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

Metody zachowują się podobnie, tj. zachowują informacje o środowisku leksykalnym, w którym zostały utworzone. Ta cecha jest najbardziej zauważalna, gdy metoda zostanie wyodrębniona ze swojej instancji, stając się metodą powiązaną. Zasadniczą różnicą między zamknięciem funkcji a metodą powiązaną jest fakt, że wartość słowa kluczowego this w metodzie powiązanej jest zawsze odwołaniem do instancji, do której ta metoda pierwotnie należała, natomiast w zamknięciu funkcji wartość słowa kluczowego this może ulegać zmianie.