Le
funzioni
sono blocchi di codice che eseguono operazioni specifiche e possono essere riutilizzati all'interno del programma. In ActionScript 3.0 sono disponibili due tipi di funzioni:
metodi
e
chiusure di funzione
. Una funzione viene chiamata metodo oppure chiusura di funzione a seconda del contesto nel quale è definita. Se la funzione viene definita all'interno di una definizione di classe o associata a un'istanza di un oggetto, prende il nome di metodo. Se viene definita in qualunque altro modo, viene chiamata chiusura di funzione.
Le funzioni hanno sempre avuto un ruolo estremamente importante in ActionScript. In ActionScript 1.0, ad esempio, la parola chiave
class
non esisteva, quindi le “classi” erano definite dalle funzioni di costruzione. Anche se nel frattempo la parola chiave
class
è stata aggiunta al linguaggio, una conoscenza approfondita delle funzioni è ancora importante per sfruttare nel modo migliore le possibilità offerte da ActionScript. Questo compito può risultare più impegnativo per i programmatori che si aspettano un comportamento delle funzioni ActionScript analogo a quello di linguaggi come C++ o Java. Anche se le procedure di base per definire e chiamare le funzioni non dovrebbero rappresentare un problema per i programmatori esperti, alcune delle caratteristiche più avanzate di ActionScript richiedono un approfondimento.
Concetti di base delle funzioni
Chiamate di funzione
È possibile chiamare una funzione specificando il relativo identificatore seguito dall'operatore parentesi (
()
). L'operatore parentesi ha il compito di racchiudere gli eventuali parametri che si desidera inviare alla funzione. Ad esempio, la funzione
trace()
è una funzione di primo livello in ActionScript 3.0:
trace("Use trace to help debug your script");
Se chiamate una funzione senza parametri, dovete includere una coppia di parentesi vuote. Ad esempio, potete utilizzare il metodo
Math.random()
, che non accetta parametri, per generare un numero casuale:
var randomNum:Number = Math.random();
Definizione di funzioni personalizzate
In ActionScript 3.0 sono disponibili due metodi per definire una funzione: potete utilizzare un'istruzione di funzione o un'espressione di funzione. A seconda che preferiate uno stile di programmazione più statico o dinamico, potete scegliere una o l'altra tecnica. In genere, chi usa le istruzioni per definire le funzioni preferisce la programmazione statica (modalità rigorosa). Le espressioni vengono invece utilizzate per definire le funzioni quando esiste un'esigenza specifica in questo senso, solitamente nella programmazione dinamica (modalità standard).
Istruzioni di funzione
Le istruzioni di funzione sono la tecnica preferita per definire le funzioni in modalità rigorosa. Un'istruzione di funzione inizia con la parola chiave
function
, seguita da:
-
Il nome della funzione
-
I parametri, separati da virgole e racchiusi tra parentesi
-
Il corpo della funzione, ovvero il codice ActionScript da eseguire quando la funzione viene chiamata, racchiuso tra parentesi graffe
Ad esempio, il codice seguente crea una funzione che definisce un parametro, quindi chiama la funzione utilizzando la stringa “
hello"
come valore del parametro:
function traceParameter(aParam:String)
{
trace(aParam);
}
traceParameter("hello"); // hello
Espressioni di funzione
Il secondo modo per dichiarare una funzione prevede l'uso di un'istruzione di assegnazione con un'espressione di funzione, che talvolta viene anche definita letterale di funzione o funzione anonima. Si tratta di un metodo più verboso, largamente utilizzato nelle versioni precedenti di ActionScript.
Un'istruzione di assegnazione con un'espressione di funzione inizia con la parola chiave
var
, seguita da:
-
Il nome della funzione
-
L'operatore due punti (
:
)
-
La classe
Function
per indicare il tipo di dati
-
L'operatore di assegnazione (
=
)
-
La parola chiave
function
-
I parametri, separati da virgole e racchiusi tra parentesi
-
Il corpo della funzione, ovvero il codice ActionScript da eseguire quando la funzione viene chiamata, racchiuso tra parentesi graffe
Ad esempio, il codice seguente dichiara la funzione
traceParameter
utilizzando un'espressione di funzione:
var traceParameter:Function = function (aParam:String)
{
trace(aParam);
};
traceParameter("hello"); // hello
Notate che non occorre specificare un nome di funzione come avviene nelle istruzioni di funzione. Un'altra importante differenza tra le espressioni di funzione e le istruzioni di funzione consiste nel fatto che un'espressione di funzione è appunto un'espressione e non un'istruzione. Ciò significa che un'espressione di funzione, a differenza di un'istruzione di funzione, non può esistere come elemento autonomo, bensì può essere utilizzata solo all'interno di un'istruzione, solitamente un'istruzione di assegnazione. L'esempio seguente mostra un'espressione di funzione assegnata a un elemento array:
var traceArray:Array = new Array();
traceArray[0] = function (aParam:String)
{
trace(aParam);
};
traceArray[0]("hello");
Scelta tra istruzioni ed espressioni
Come regola generale, utilizzate un'istruzione di funzione a meno che le circostanze specifiche non suggeriscano l'uso di un'espressione. Le istruzioni di funzione sono meno verbose e, rispetto alle espressioni di funzione, producono risultati più omogenei tra modalità rigorosa e modalità standard.
Le istruzioni di funzione sono più facili da leggere delle istruzioni di assegnazione che contengono espressioni di funzione, consentono di scrivere codice più conciso e creano meno confusione delle espressioni di funzione, che richiedono l'uso delle due parole chiave
var
e
function
.
Inoltre, le istruzioni di funzione producono risultati più omogenei tra le due modalità del compilatore perché consentono di utilizzare la sintassi del punto sia in modalità rigorosa che standard per chiamare un metodo dichiarato mediante un'istruzione di funzione, il che non è sempre possibile per i metodi dichiarati con un'espressione di funzione. Il codice seguente, ad esempio, definisce una classe denominata Example con due metodi:
methodExpression()
, dichiarato con un'espressione di funzione, e
methodStatement()
, dichiarato con un'istruzione di funzione. In modalità rigorosa non potete utilizzare la sintassi del punto per chiamare il metodo
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
Le espressioni di funzione sono generalmente più indicate per la programmazione che privilegia il comportamento runtime, ovvero dinamico. Se preferite utilizzare la modalità rigorosa ma avete anche la necessità di chiamare un metodo dichiarato con un'espressione di funzione, potete utilizzare entrambe le tecniche. Innanzi tutto, potete chiamare il metodo utilizzando le parentesi quadre (
[]
) invece dell'operatore punto (
.
). Il metodo seguente ha esito positivo sia in modalità rigorosa che standard:
myExample["methodLiteral"]();
In secondo luogo, potete dichiarare l'intera classe come classe dinamica. Sebbene in questo modo sia possibile chiamare il metodo utilizzando l'operatore punto, la funzionalità della modalità rigorosa viene parzialmente sacrificata per tutte le istanze di tale classe. Ad esempio, il compilatore non genera un errore se si tenta di accedere a una proprietà non definita su un'istanza di una classe dinamica.
In determinate circostanze le espressioni di funzione risultano utili. Un caso frequente è quello delle funzioni che vengono utilizzate una sola volta e quindi eliminate. Un altro utilizzo, meno comune, riguarda l'associazione di una funzione a una proprietà prototype. Per ulteriori informazioni, vedete Oggetto prototype.
Esistono due sottili differenze tra le istruzioni di funzione e le espressioni di funzione di cui va tenuto conto quando si sceglie la tecnica da utilizzare. La prima è che un'espressione di funzione non viene considerata come oggetto indipendente ai fini della gestione della memoria e del processo di garbage collection. In altre parole, quando si assegna un'espressione di funzione a un altro oggetto, ad esempio a un elemento di array o una proprietà di un oggetto, nel codice viene creato semplicemente un riferimento all'espressione di funzione. Se l'array o l'oggetto al quale è associata l'espressione di funzione esce dall'area di validità o comunque cessa di essere disponibile, non è più possibile accedere all'espressione. Se l'array o l'oggetto viene eliminato, la memoria utilizzata dall'espressione di funzione diventa disponibile per il processo di garbage collection, ovvero può essere riutilizzata per altri scopi.
L'esempio seguente mostra che, se viene eliminata la proprietà alla quale è assegnata un'espressione di funzione, la funzione non è più disponibile. La classe Test è dinamica e consente quindi di aggiungere una proprietà denominata
functionExp
che contiene un'espressione di funzione. La funzione
functionExp()
può essere chiamata con l'operatore punto, ma non è più accessibile dopo l'eliminazione della proprietà
functionExp
.
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
Se, al contrario, la funzione viene inizialmente definita mediante un'istruzione di funzione, esiste come oggetto autonomo e continua a esistere anche dopo l'eliminazione della proprietà alla quale è associata. L'operatore
delete
funziona solo sulle proprietà degli oggetti, quindi non ha effetto se viene utilizzato per eliminare la funzione
stateFunc()
.
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
La seconda differenza tra un'istruzione di funzione e un'espressione di funzione consiste nel fatto che la prima esiste in tutta l'area di validità nella quale è definita, comprese le istruzioni che la precedono. Un'espressione di funzione, al contrario, viene definita solo per le istruzioni successive. Ad esempio, il codice seguente contiene una chiamata (che ha esito positivo) alla funzione
scopeTest()
prima che venga definita:
statementTest(); // statementTest
function statementTest():void
{
trace("statementTest");
}
Le espressioni di funzione non sono disponibili prima della posizione in cui vengono definite, quindi il codice seguente genera un errore runtime:
expressionTest(); // run-time error
var expressionTest:Function = function ()
{
trace("expressionTest");
}
Restituzione di valori da funzioni
Per restituire un valore da una funzione, utilizzate l'istruzione
return
seguita dall'espressione o dal valore letterale da restituire. Il seguente codice, ad esempio, restituisce un'espressione corrispondente al parametro:
function doubleNum(baseNum:int):int
{
return (baseNum * 2);
}
Notate che l'istruzione
return
termina la funzione, quindi eventuali istruzioni successive a un'istruzione
return
non vengono eseguite, come nell'esempio seguente:
function doubleNum(baseNum:int):int {
return (baseNum * 2);
trace("after return"); // This trace statement will not be executed.
}
In modalità rigorosa dovete restituire un valore del tipo appropriato se scegliete di specificare un tipo restituito. Ad esempio, il codice seguente genera un errore in modalità rigorosa perché non restituisce un valore valido:
function doubleNum(baseNum:int):int
{
trace("after return");
}
Funzioni nidificate
È possibile nidificare le funzioni, cioè dichiararle all'interno di altre funzioni. Una funzione nidificata è disponibile solo all'interno della funzione principale in cui è contenuta, a meno che non venga passato al codice esterno un riferimento alla funzione. Ad esempio, il codice seguente dichiara due funzioni nidificate all'interno della funzione
getNameAndVersion()
:
function getNameAndVersion():String
{
function getVersion():String
{
return "10";
}
function getProductName():String
{
return "Flash Player";
}
return (getProductName() + " " + getVersion());
}
trace(getNameAndVersion()); // Flash Player 10
Se passate al codice esterno, le funzioni nidificate vengono passate come chiusure di funzione, vale a dire che la funzione conserva le definizioni che si trovano nell'area di validità nel momento in cui viene definita. Per ulteriori informazioni, vedete Area di validità delle funzioni.
Parametri di funzione
ActionScript 3.0 introduce alcune funzionalità relative ai parametri di funzione che potrebbero sembrare inedite ai programmatori che iniziano a utilizzare questo linguaggio. Benché l'idea di passare i parametri mediante un valore o un riferimento risulti probabilmente familiare alla maggior parte dei programmatori, l'oggetto
arguments
e il parametro ... (rest) potrebbero invece rappresentare una novità.
Passaggio di argomenti mediante un valore o un riferimento
In molti linguaggi di programmazione, è importante comprendere la distinzione che esiste tra passare gli argomenti mediante un valore oppure mediante un riferimento, poiché tale distinzione può influire su come viene progettato il codice.
Passare un argomento mediante un valore significa copiarne il valore in una variabile locale da utilizzare con la funzione. Al contrario, se si specifica un argomento mediante un riferimento, viene passato solo un riferimento all'argomento e non il valore effettivo. Nel secondo caso non viene quindi creata una copia dell'argomento vero e proprio, bensì viene passato come argomento un riferimento alla variabile nel momento in cui l'argomento viene creato e assegnato a una variabile locale da utilizzare nella funzione. Poiché è un riferimento a una variabile esterna alla funzione, la variabile locale consente di modificare il valore della variabile originale.
In ActionScript 3.0, tutti gli argomenti vengono passati mediante riferimento perché tutti i valori sono memorizzati come oggetti. Tuttavia, gli oggetti che appartengono ai tipi di dati di base (Boolean, Number, int, uint e String) prevedono l'uso di operatori speciali grazie ai quali possono comportarsi come se venissero passati mediante un valore. Ad esempio, il codice seguente crea una funzione denominata
passPrimitives()
che definisce due parametri chiamati
xParam
e
yParam
, entrambi del tipo int. Questi parametri sono simili a variabili locali dichiarate nel corpo della funzione
passPrimitives()
. Quando la funzione viene chiamata con gli argomenti
xValue
e
yValue
, i parametri
xParam
e
yParam
vengono inizializzati mediante riferimenti agli oggetti int rappresentati da
xValue
e
yValue
. Poiché gli argomenti appartengono a un tipo di base, si comportano come se fossero passati mediante valore. Benché
xParam
e
yParam
contengano inizialmente solo riferimenti agli oggetti
xValue
e
yValue
, qualunque modifica delle variabili all'interno del corpo della funzione genera nuove copie dei valori in memoria.
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
All'interno della funzione
passPrimitives()
, i valori di
xParam
e
yParam
vengono incrementati, tuttavia ciò non influisce sui valori di
xValue
e
yValue
, come mostra l'ultima istruzione
trace
. Lo stesso varrebbe anche nel caso in cui i parametri avessero gli stessi nomi delle variabili,
xValue
e
yValue
, perché i valori
xValue
e
yValue
all'interno della funzione farebbero riferimento a nuove posizioni di memoria, distinte dalle variabili omonime esterne alla funzione.
Tutti gli altri oggetti, ovvero gli oggetti che non appartengono ai tipi di dati primitivi, vengono sempre passati mediante riferimento, quindi con la possibilità di modificare il valore della variabile originale. Ad esempio, il codice seguente crea un oggetto denominato
objVar
con due proprietà,
x
e
y
. L'oggetto viene passato come argomento alla funzione
passByRef()
. Poiché non appartiene a un tipo di base, l'oggetto non viene semplicemente passato mediante un riferimento, ma rimane un riferimento. Ciò significa che le modifiche apportate ai parametri all'interno della funzione non hanno effetto sulle proprietà dell'oggetto all'esterno della funzione.
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
Il parametro
objParam
fa riferimento allo stesso oggetto della variabile globale
objVar
. Come potete notare nelle istruzioni
trace
dell'esempio, le modifiche apportate alle proprietà
x
e
y
dell'oggetto
objParam
vengono applicate anche all'oggetto
objVar
.
Valori predefiniti dei parametri
In ActionScript 3.0, potete dichiarare
valori di parametro predefiniti
per una funzione. Se in una chiamata a una funzione con parametri predefiniti viene omesso un parametro con valori predefiniti, viene utilizzato il valore specificato per quel parametro nella definizione della funzione. Tutti i parametri con valori predefiniti devono essere posizionati alla fine dell'elenco dei parametri. I valori assegnati come predefiniti devono essere costanti della fase di compilazione. L'esistenza di un valore predefinito per un parametro fa sì che quel parametro diventi un
parametro opzionale
, mentre un parametro privo di valore predefinito viene considerato un
parametro obbligatorio
.
Ad esempio, il codice seguente crea una funzione con tre parametri, due dei quali hanno valori predefiniti. Quando la funzione viene chiamata con un solo parametro, vengono utilizzati i valori predefiniti dei parametri.
function defaultValues(x:int, y:int = 3, z:int = 5):void
{
trace(x, y, z);
}
defaultValues(1); // 1 3 5
L'oggetto arguments
Quando passate dei parametri a una funzione, potete utilizzare l'oggetto
arguments
per accedere alle informazioni relative a tali parametri. Seguono alcune osservazioni importanti relative all'oggetto
arguments
:
-
l'oggetto
arguments
è un array che include tutti i parametri passati alla funzione;
-
la proprietà
arguments.length
segnala il numero di parametri passati alla funzione;
-
la proprietà
arguments.callee
fornisce un riferimento alla funzione stessa, che è utile per le chiamate ricorsive alle espressioni di funzione.
Nota:
l'oggetto
arguments
non è disponibile se è presente un parametro denominato
arguments
oppure se utilizzate il parametro ... (rest).
Se il corpo della funzione contiene un riferimento all'oggetto
arguments
, ActionScript 3.0 consente di includere nelle chiamate di funzione più parametri di quelli definiti nella definizione della funzione; tuttavia, in modalità rigorosa genera un errore del compilatore se il numero di parametri non corrisponde al numero di parametri obbligatori (e facoltativamente, qualsiasi parametro opzionale). Potete ricorrere alla funzionalità di array dell'oggetto
arguments
per accedere a qualunque parametro passato alla funzione, a prescindere che sia o meno definito nella definizione della funzione. L'esempio seguente, che viene compilato solo in modalità rigorosa, utilizza l'array
arguments
con la proprietà
arguments.length
per tracciare tutti i parametri passati alla funzione
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
La proprietà
arguments.callee
viene spesso utilizzata nelle funzioni anonime per creare la ricorsività e rendere il codice più flessibile. Se il nome di una funzione ricorsiva cambia durante il ciclo di sviluppo, non occorre modificare la chiamata ricorsiva nel corpo della funzione se si utilizza
arguments.callee
al posto del nome della funzione. Nell'espressione di funzione seguente, la proprietà
arguments.callee
viene utilizzata per abilitare la ricorsività:
var factorial:Function = function (x:uint)
{
if(x == 0)
{
return 1;
}
else
{
return (x * arguments.callee(x - 1));
}
}
trace(factorial(5)); // 120
Se utilizzate il parametro ... (rest) nella dichiarazione della funzione, l'oggetto
arguments
non è disponibile e per accedere ai parametri è necessario utilizzare i rispettivi nomi che sono stati dichiarati.
È inoltre importante evitare di utilizzare la stringa
"arguments"
come nome di parametro perché impedisce l'uso dell'oggetto
arguments
. Ad esempio, se la funzione
traceArgArray()
viene riscritta con l'aggiunta di un parametro
arguments
, i riferimenti a
arguments
nel corpo della funzione sono relativi al parametro anziché all'oggetto
arguments.
Il codice seguente non produce alcun 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
Nelle versioni precedenti di ActionScript, l'oggetto
arguments
conteneva anche una proprietà denominata
caller
, che era un riferimento alla funzione che chiamava la funzione corrente. La proprietà
caller
non è presente in ActionScript 3.0, ma se occorre fare riferimento alla funzione chiamante, potete modificare quest'ultima in modo che passi un parametro supplementare contenente un riferimento a se stessa.
Il parametro ... (rest)
In ActionScript 3.0 è stata introdotta una nuova dichiarazione di parametro, il parametro ... (rest), che consente di specificare un parametro array che accetta qualunque numero di argomenti separati da virgole. Il parametro può avere qualsiasi nome che non corrisponda a una parola riservata e deve essere l'ultimo parametro specificato. L'uso di questo parametro rende indisponibile l'oggetto
arguments
. Anche se il parametro ... (rest) offre la stessa funzionalità dell'array
arguments
e della proprietà
arguments.length
, non fornisce invece una funzionalità simile a quella di
arguments.callee
. Prima di usare il parametro ... (rest), assicuratevi che non sia necessario utilizzare
arguments.callee
.
Il seguente esempio riscrive la funzione
traceArgArray()
utilizzando il parametro ... (rest) invece dell'oggetto
arguments
:
function traceArgArray(... args):void
{
for (var i:uint = 0; i < args.length; i++)
{
trace(args[i]);
}
}
traceArgArray(1, 2, 3);
// output:
// 1
// 2
// 3
Il parametro ... (rest) può anche essere utilizzato con altri parametri, a condizione che venga specificato per ultimo. L'esempio seguente modifica la funzione
traceArgArray()
in modo tale che il primo parametro,
x
, sia del tipo int, e il secondo utilizzi il parametro ... (rest). L'output ignora il primo valore perché il primo parametro non fa più parte dell'array creato dal parametro ... (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
Funzioni come oggetti
In ActionScript 3.0, le funzioni sono oggetti. Quando create una funzione, ciò che viene creato è in realtà un oggetto che non solo può essere passato come parametro a un'altra funzione, ma che può disporre anche di proprietà e metodi.
Le funzioni specificate come argomenti per altre funzioni vengono passate mediante riferimento e non mediante un valore. Quando passate una funzione come argomento, utilizzate solo l'identificatore e omettete l'operatore parentesi usato per chiamare il metodo. Ad esempio, il codice seguente passa una funzione denominata
clickListener()
come argomento al metodo
addEventListener()
:
addEventListener(MouseEvent.CLICK, clickListener);
Per quanto possa sembrare strano ai programmatori che iniziano a utilizzare ActionScript, le funzioni possono avere proprietà e metodi come qualunque altro oggetto. In effetti, ogni funzione dispone di una proprietà di sola lettura denominata
length
che memorizza il numero di parametri definiti per la funzione. Questa proprietà è diversa dalla proprietà
arguments.length
, che indica il numero di argomenti passati alla funzione. È bene ricordare che in ActionScript il numero di argomenti inviati a una funzione può superare quello dei parametri definiti per la stessa funzione. L'esempio seguente, che viene compilato solo in modalità standard perché la modalità rigorosa richiede una corrispondenza esatta tra il numero di argomenti passati e il numero di parametri definiti, mostra la differenza tra le due proprietà:
// 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 modalità rigorosa, potete definire proprietà personalizzate per la funzione all'esterno del corpo della funzione. Le proprietà di funzione possono servire come proprietà “quasi statiche” che consentono di salvare lo stato di una variabile relativo alla funzione. Ad esempio, potrebbe essere utile registrare quante volte viene utilizzata una particolare funzione. Questa funzionalità può servire se si sta creando un videogame e si desidera registrare quante volte un utente utilizza un comando specifico (benché sia anche possibile utilizzare una proprietà di classe statica per lo stesso scopo). Nell'esempio seguente, che viene compilato solo in modalità standard poiché la modalità rigorosa non consente di aggiungere proprietà dinamiche alle funzioni, viene creata una proprietà di funzione all'esterno della dichiarazione di funzione e incrementata la proprietà ogni volta che la funzione viene chiamata:
// Compiles only in standard mode
var someFunction:Function = function ():void
{
someFunction.counter++;
}
someFunction.counter = 0;
someFunction();
someFunction();
trace(someFunction.counter); // 2
Area di validità delle funzioni
L'area di validità di una funzione determina non solo l'area in cui, all'interno di un programma, quella funzione può essere chiamata, ma anche le definizioni alle quali la funzione può accedere. Le stesse regole dell'area di validità che valgono per gli identificatori delle variabili si applicano anche agli identificatori di funzione. Una funzione dichiarata nell'area di validità globale è disponibile in tutto il codice. Ad esempio, ActionScript 3.0 contiene funzioni globali, quali
isNaN()
e
parseInt()
, che sono disponibili in qualunque punto del codice. Una funzione nidificata (cioè dichiarata all'interno di un'altra funzione) può essere utilizzata in qualunque posizione all'interno della funzione in cui è stata dichiarata.
La catena dell'area di validità
Ogni volta che inizia l'esecuzione di una funzione, viene creata una serie di oggetti e di proprietà. Innanzi tutto, viene creato un oggetto speciale chiamato
oggetto di attivazione
, nel quale vengono memorizzati i parametri e le eventuali variabili locali o funzioni dichiarate nel corpo della funzione. Non è possibile accedere direttamente all'oggetto di attivazione perché è un meccanismo interno. In secondo luogo viene creata una
catena dell'area di validità
che contiene un elenco ordinato degli oggetti nei quali il runtime cerca le dichiarazioni di identificazione (gli identificatori). Ogni funzione che viene eseguita ha una catena dell'area di validità che viene memorizzata in una proprietà interna. Nel caso di una funzione nidificata, la catena dell'area di validità inizia con il proprio oggetto di attivazione, seguito dall'oggetto di attivazione della relativa funzione principale. La catena prosegue nello stesso modo fino al raggiungimento dell'oggetto globale, ovvero l'oggetto che viene creato all'inizio di un programma ActionScript e che contiene tutte le variabili globali e le funzioni.
Chiusure di funzione
Una
chiusura di funzione
è un oggetto che contiene un'istantanea della funzione e il relativo
ambiente lessicale
, il quale comprende tutte le variabili, le proprietà, i metodi e gli oggetti inclusi nella catena dell'area di validità della funzione, con i rispettivi valori. Le chiusure di funzione vengono create ogni volta che una funzione viene eseguita indipendentemente da un oggetto o da una classe. Il fatto che una chiusura di funzione mantenga l'area di validità nella quale è stata definita produce risultati interessanti quando una funzione viene passata in un'area di validità diversa come argomento o come valore restituito.
Il seguente codice, ad esempio, crea due funzioni:
foo()
, che restituisce una funzione nidificata di nome
rectArea()
che calcola l'area di un rettangolo, e
bar()
, che chiama
foo()
e memorizza la chiusura di funzione restituita in una variabile denominata
myProduct
. Anche se la funzione
bar()
definisce la propria variabile locale
x
(con valore 2), quando la chiusura di funzione
myProduct()
viene chiamata, essa mantiene la variabile
x
(con valore 40) definita nella funzione
foo().
La funzione
bar()
, pertanto, restituisce il valore
160
anziché
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
I metodi si comportano in modo analogo, perché conservano a loro volta le informazioni relative all'ambiente lessicale nel quale sono stati creati. Questa caratteristica è evidente soprattutto quando un metodo viene estratto dalla propria istanza per creare un metodo vincolato. La differenza principale tra una chiusura di funzione e un metodo vincolato consiste nel fatto che il valore della parola chiave
this
in un metodo vincolato fa sempre riferimento all'istanza alla quale è stata originariamente associata, mentre in una chiusura di funzione il valore di
this
può cambiare.
|
|
|