Las
funciones
son bloques de código que realizan tareas específicas y pueden reutilizarse en el programa. Hay dos tipos de funciones en ActionScript 3.0:
métodos
y
cierres de función
. Llamar a una función método o cierre de función depende del contexto en el que se define la función. Una función se denomina método si se define como parte de una definición de clase o se asocia a una instancia de un objeto. Y se denomina cierre de función si se define de cualquier otra manera.
Las funciones siempre han sido muy importantes en ActionScript. Por ejemplo, en ActionScript 1.0 la palabra clave
class
no existía, por lo que las “clases” se definían mediante funciones constructoras. Aunque la palabra clave
class
se añadió posteriormente al lenguaje, sigue siendo importante comprender a fondo las funciones para aprovechar al máximo las capacidades del lenguaje. Esto puede ser difícil de entender para los programadores que esperan que las funciones de ActionScript se comporten de forma similar a las funciones de lenguajes como C++ o Java. Aunque una definición básica de función e invocación no debe resultar difícil para los programadores con experiencia, algunas de las características más avanzadas de las funciones de ActionScript requieren una explicación.
Fundamentos de la utilización de funciones
Invocación de funciones
Para llamar a una función se utiliza su identificador seguido del operador paréntesis (
()
). Se puede utilizar el operador paréntesis para escribir los parámetros de función que se desea enviar a la función. Por ejemplo,
trace()
es una función de nivel superior en ActionScript 3.0:
trace("Use trace to help debug your script");
Si se llama a una función sin parámetros, hay que utilizar un par de paréntesis vacíos. Por ejemplo, se puede utilizar el método
Math.random()
, que no admite parámetros, para generar un número aleatorio:
var randomNum:Number = Math.random();
Funciones definidas por el usuario
Hay dos formas de definir una función en ActionScript 3.0: se puede utilizar una sentencia de función o una expresión de función. La técnica que se elija dependerá de si se prefiere un estilo de programación más estático o más dinámico. Si se prefiere la programación estática, o en modo estricto, se deben definir las funciones con sentencias de función. Las funciones deben definirse con expresiones de función si existe la necesidad específica de hacerlo. Las expresiones de función se suelen usar en programación dinámica (en modo estándar).
Sentencias de función
Las sentencias de función son la técnica preferida para definir funciones en modo estricto. Una sentencia de función empieza con la palabra clave
function
, seguida de:
-
El nombre de la función
-
Los parámetros, en una lista delimitada por comas y escrita entre paréntesis
-
El cuerpo de la función (es decir, el código ActionScript que debe ejecutarse cuando se invoca la función), escrito entre llaves.
Por ejemplo, el código siguiente crea una función que define un parámetro y después llama a la función con la cadena
"hello"
como valor del parámetro:
function traceParameter(aParam:String)
{
trace(aParam);
}
traceParameter("hello"); // hello
Expresiones de función
La segunda manera de declarar una función es utilizar una sentencia de asignación con una expresión de función (también se suele llamar literal de función o función anónima). Éste es un método que requiere escribir más y que se usaba mucho en versiones anteriores de ActionScript.
Una sentencia de asignación con una expresión de función empieza por la palabra clave
var
, seguida de:
-
El nombre de la función
-
El operador dos puntos (
:
)
-
La clase
Function
para indicar el tipo de datos
-
El operador de asignación (
=
)
-
La palabra clave
function
-
Los parámetros, en una lista delimitada por comas y escrita entre paréntesis
-
El cuerpo de la función (es decir, el código ActionScript que debe ejecutarse cuando se invoca la función), escrito entre llaves.
Por ejemplo, el código siguiente declara la función
traceParameter
mediante una expresión de función:
var traceParameter:Function = function (aParam:String)
{
trace(aParam);
};
traceParameter("hello"); // hello
Tenga en cuenta que, a diferencia de lo que ocurre en una sentencia de función, no se especifica un nombre de función. Otra diferencia importante entre las expresiones de función y las sentencias de función es que una expresión de función es una expresión, no una sentencia. Esto significa que una expresión de función no es independiente, como una sentencia de función. Una expresión de función sólo se puede utilizar como una parte de una sentencia (normalmente una sentencia de asignación). En el siguiente ejemplo se muestra la asignación de una expresión de función a un elemento de conjunto:
var traceArray:Array = new Array();
traceArray[0] = function (aParam:String)
{
trace(aParam);
};
traceArray[0]("hello");
Criterios para elegir entre sentencias y expresiones
Como regla general, se debe utilizar una sentencia de función a menos que circunstancias específicas requieran una expresión. Las sentencias de función son menos detalladas y proporcionan una experiencia más uniforme entre el modo estricto y el modo estándar que las expresiones de función.
También son más fáciles de leer que las sentencias de asignación que contienen expresiones de función. Por otra parte, las sentencias de función hacen que el código sea más conciso; son menos confusas que las expresiones de función, que requieren utilizar las palabras clave
var
y
function
.
Además, proporcionan una experiencia más uniforme entre los dos modos de compilador, ya que permiten utilizar la sintaxis con punto en modo estándar y en modo estricto para llamar a un método declarado con una sentencia de función. Esto no es así necesariamente para los métodos declarados con una expresión de función. Por ejemplo, el código siguiente define una clase denominada Example con dos métodos:
methodExpression()
, que se declara con una expresión de función, y
methodStatement()
, que se declara con una sentencia de función. En modo estricto no se puede utilizar la sintaxis con punto para llamar al método
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
Las expresiones de función se consideran más apropiadas para la programación centrada en el comportamiento dinámico (en tiempo de ejecución). Si se prefiere utilizar el modo estricto, pero también hay que llamar a un método declarado con una expresión de función, se puede utilizar cualquiera de las dos técnicas. En primer lugar, se puede llamar al método utilizando corchetes (
[]
) el lugar del operador punto (
.
). La siguiente llamada a método funciona correctamente tanto en modo estricto como en modo estándar:
myExample["methodLiteral"]();
En segundo lugar, se puede declarar toda la clase como una clase dinámica. Aunque esto permite llamar al método con el operador punto, la desventaja es que se sacrifica parte de la funcionalidad en modo estricto para todas las instancias de la clase. Por ejemplo, el compilador no genera un error si se intenta acceder a una propiedad no definida en una instancia de una clase dinámica.
Hay algunas circunstancias en las que las expresiones de función son útiles. Las expresiones de función se suelen utilizar para crear funciones que se utilizan una sola vez y después se descartan. Otro uso menos común es asociar una función a una propiedad de prototipo. Para obtener más información, consulte El objeto prototype.
Hay dos diferencias sutiles entre las sentencias de función y las expresiones de función que se deben tener en cuenta al elegir la técnica que se va a utilizar. La primera diferencia es que las expresiones de función no existen de forma independiente como objetos con respecto a la administración de la memoria y la recolección de elementos no utilizados. Es decir, cuando se asigna una expresión de función a otro objeto, como un elemento de conjunto o una propiedad de objeto, se crea la única referencia a esa expresión de función en el código. Si el conjunto o el objeto al que la expresión de función está asociada se salen del ámbito o deja de estar disponible, se dejará de tener acceso a la expresión de función. Si se elimina el conjunto o el objeto, la memoria utilizada por la expresión de función quedará disponible para la recolección de elementos no utilizados, lo que significa que se podrá recuperar esa memoria y reutilizarla para otros propósitos.
En el siguiente ejemplo se muestra que, para una expresión de función, cuando se elimina la propiedad a la que está asignada la expresión, la función deja de estar disponible. La clase Test es dinámica, lo que significa que se puede añadir una propiedad denominada
functionExp
que contendrá una expresión de función. Se puede llamar a la función
functionExp()
con el operador punto, pero cuando se elimina la propiedad
functionExp
, la función deja de ser accesible.
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
Si, por otra parte, la función se define primero con una sentencia de función, existe como su propio objeto y seguirá existiendo incluso después de que se elimine la propiedad a la que está asociada. El operador
delete
sólo funciona en propiedades de objetos, por lo que incluso una llamada para eliminar la función
stateFunc()
no funciona.
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 segunda diferencia entre las sentencias de función y las expresiones de función es que las sentencias de función existen en todo el ámbito en que están definidas, incluso en sentencias que aparecen antes que la sentencia de función. En cambio, las expresiones de función sólo están definidas para las sentencias posteriores. Por ejemplo, el código siguiente llama correctamente a la función
scopeTest()
antes de que se defina:
statementTest(); // statementTest
function statementTest():void
{
trace("statementTest");
}
Las expresiones de función no están disponibles antes de ser definidas, por lo que el código siguiente produce un error en tiempo de ejecución:
expressionTest(); // run-time error
var expressionTest:Function = function ()
{
trace("expressionTest");
}
Devolución de valores de funciones
Para devolver un valor de la función se debe utilizar la sentencia
return
seguida de la expresión o el valor literal que se desea devolver. Por ejemplo, el código siguiente devuelve una expresión que representa al parámetro:
function doubleNum(baseNum:int):int
{
return (baseNum * 2);
}
Tenga en cuenta que la sentencia
return
finaliza la función, por lo que las sentencias que estén por debajo de una sentencia
return
no se ejecutarán, como se indica a continuación:
function doubleNum(baseNum:int):int {
return (baseNum * 2);
trace("after return"); // This trace statement will not be executed.
}
En modo estricto se debe devolver un valor del tipo apropiado si se elige especificar un tipo devuelto. Por ejemplo, el código siguiente genera un error en modo estricto porque no devuelve un valor válido:
function doubleNum(baseNum:int):int
{
trace("after return");
}
Funciones anidadas
Es posible anidar funciones, lo que significa que pueden declararse funciones dentro de otras funciones. Una función anidada sólo está disponible dentro de su función principal, a menos que se pase una referencia a la función a código externo. Por ejemplo, el código siguiente declara dos funciones anidadas dentro de la función
getNameAndVersion()
:
function getNameAndVersion():String
{
function getVersion():String
{
return "10";
}
function getProductName():String
{
return "Flash Player";
}
return (getProductName() + " " + getVersion());
}
trace(getNameAndVersion()); // Flash Player 10
Cuando se pasan funciones anidadas a código externo, se pasan como cierres de función, lo que significa que la función retiene todas las definiciones que hubiera en el ámbito cuando se definió la función. Para obtener más información, consulte Ámbito de una función.
Parámetros de función
ActionScript 3.0 proporciona funcionalidad para los parámetros de función que puede resultar novedosa para los programadores que empiecen a estudiar el lenguaje. Aunque la mayoría de los programadores deberían estar familiarizados con la idea de pasar parámetros por valor o referencia, es posible que el objeto
arguments
y el parámetro ... (rest) sean desconocidos para muchos.
Pasar argumentos por valor o por referencia
En muchos lenguajes de programación, es importante comprender la diferencia entre pasar argumentos por valor o por referencia; esta diferencia puede afectar a la manera de diseñar el código.
Al pasar por valor, el valor del argumento se copia en una variable local para usarlo en la función. Al pasar por referencia, sólo se pasa una referencia al argumento, en lugar del valor real. No se realiza ninguna copia del argumento real. En su lugar, se crea una referencia a la variable pasada como argumento y se asigna dicha referencia a una variable local para usarla en la función. Como una referencia a una variable externa a la función, la variable local proporciona la capacidad de cambiar el valor de la variable original.
En ActionScript 3.0, todos los argumentos se pasan por referencia, ya que todos los valores se almacenan como objetos. No obstante, los objetos que pertenecen a los tipos de datos simples, como Boolean, Number, int, uint y String, tienen operadores especiales que hacen que se comporten como si se pasaran por valor. Por ejemplo, el código siguiente crea una función denominada
passPrimitives()
que define dos parámetros denominados
xParam
y
yParam
, ambos de tipo int. Estos parámetros son similares a variables locales declaradas en el cuerpo de la función
passPrimitives()
. Cuando se llama a la función con los argumentos
xValue
e
yValue
, los parámetros
xParam
e
yParam
se inicializan con referencias a los objetos int representados por
xValue
e
yValue
. Como los argumentos son valores simples, se comportan como si se pasaran por valor. Aunque
xParam
e
yParam
sólo contienen inicialmente referencias a los objetos
xValue
e
yValue
, los cambios realizados a las variables en el cuerpo de la función generan nuevas copias de los valores en la 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
En la función
passPrimitives()
, los valores de
xParam
e
yParam
se incrementan, pero esto no afecta a los valores de
xValue
e
yValue
, como se indica en la última sentencia
trace.
Esto es así aunque se asigne a los parámetros los mismos nombres que a las variables,
xValue
e
yValue
, ya que dentro de la función
xValue
e
yValue
señalarían nuevas ubicaciones de la memoria que existen por separado de las variables externas a la función que tienen el mismo nombre.
Todos los demás objetos (es decir, los objetos que no pertenecen a los tipos de datos simples) se pasan siempre por referencia, ya que esto ofrece la capacidad de cambiar el valor de la variable original. Por ejemplo, el código siguiente crea un objeto denominado
objVar
con dos propiedades,
x
e
y
. El objeto se pasa como un argumento a la función
passByRef()
. Como el objeto no es un tipo simple, no sólo se pasa por referencia, sino que también se mantiene como una referencia. Esto significa que los cambios realizados en los parámetros dentro de la función afectarán a las propiedades del objeto fuera de la función.
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
El parámetro
objParam
hace referencia al mismo objeto que la variable
objVar
global. Como se puede ver en las sentencias
trace
del ejemplo, los cambios realizados en las propiedades
x
e
y
del objeto
objParam
se reflejan en el objeto
objVar
.
Valores predeterminados de los parámetros
En ActionScript 3.0, se puede declarar
valores predeterminados de parámetros
para una función. Si una llamada a una función con valores predeterminados de parámetros omite un parámetro con valores predeterminados, se utiliza el valor especificado en la definición de la función para ese parámetro. Todos los parámetros con valores predeterminados deben colocarse al final de la lista de parámetros. Los valores asignados como valores predeterminados deben ser constantes de tiempo de compilación. La existencia de un valor predeterminado para un parámetro convierte de forma efectiva a ese parámetro en un
parámetro opcional
. Un parámetro sin un valor predeterminado se considera un
parámetro requerido
.
Por ejemplo, el código siguiente crea una función con tres parámetros, dos de los cuales tienen valores predeterminados. Cuando se llama a la función con un solo parámetro, se utilizan los valores predeterminados de los parámetros.
function defaultValues(x:int, y:int = 3, z:int = 5):void
{
trace(x, y, z);
}
defaultValues(1); // 1 3 5
El objeto arguments
Cuando se pasan parámetros a una función, se puede utilizar el objeto
arguments
para acceder a información sobre los parámetros pasados a la función. Algunos aspectos importantes del objeto
arguments
son:
-
El objeto
arguments
es un conjunto que incluye todos los parámetros pasados a la función.
-
La propiedad
arguments.length
notifica el número de parámetros pasados a la función.
-
La propiedad
arguments.callee
proporciona una referencia a la misma función, que resulta útil para llamadas recursivas a expresiones de función.
Nota:
el objeto
arguments
no estará disponible si algún parámetro tiene el nombre
arguments
o si se utiliza el parámetro ... (rest).
Si se hace referencia al objeto
arguments
en el cuerpo de una función, ActionScript 3.0 permite que las llamadas a funciones incluyan más parámetros que los definidos en la definición de la función, pero generará un error del compilador en modo estricto si el número de parámetros no coincide con el número de parámetros requeridos (y de forma opcional, los parámetros opcionales). Se puede utilizar el conjunto aspect del objeto
arguments
para acceder a cualquier parámetro pasado a la función, independientemente de si ese parámetro está definido en la definición de la función. En el ejemplo siguiente, que sólo compila en modo estándar, se utiliza el conjunto
arguments
junto con la propiedad
arguments.length
para hacer un seguimiento de todos los parámetros pasados a la función
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 propiedad
arguments.callee
se suele utilizar en funciones anónimas para crear recursión. Se puede utilizar para añadir flexibilidad al código. Si el nombre de una función recursiva cambia a lo largo del ciclo de desarrollo, no es necesario preocuparse de cambiar la llamada recursiva en el cuerpo de la función si se utiliza
arguments.callee
en lugar del nombre de la función. La propiedad
arguments.callee
se utiliza en la siguiente expresión de función para habilitar la recursión:
var factorial:Function = function (x:uint)
{
if(x == 0)
{
return 1;
}
else
{
return (x * arguments.callee(x - 1));
}
}
trace(factorial(5)); // 120
Si se utiliza el parámetro ... (rest) en la declaración de la función, el objeto
arguments
no estará disponible. Hay que acceder a los parámetros a través de los nombres de parámetro declarados.
También hay que procurar no utilizar la cadena
"arguments"
como nombre de parámetro, ya que ocultará el objeto
arguments
. Por ejemplo, si se vuelve a escribir la función
traceArgArray()
de forma que se añade un parámetro
arguments
, las referencias a
arguments
en el cuerpo de la función hacen referencia al parámetro, en lugar de al objeto
arguments.
El siguiente código no produce ningún resultado:
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
El objeto
arguments
de versiones anteriores de ActionScript también contenía una propiedad denominada
caller
, que es una referencia a la función que llamó a la función actual. La propiedad
caller
no existe en ActionScript 3.0, pero si se necesita una referencia a la función que llama, se puede modificar dicha función de forma que pase un parámetro adicional que sea una referencia sí mismo.
El parámetro ...(rest)
ActionScript 3.0 introduce una declaración de un parámetro nuevo que se llama ... (rest). Este parámetro permite especificar un parámetro de tipo conjunto que acepta un número arbitrario de argumentos delimitados por comas. El parámetro puede tener cualquier nombre que no sea una palabra reservada. Este parámetro debe especificarse el último. El uso de este parámetro hace que el objeto
arguments
no esté disponible. Aunque el parámetro ... (rest) ofrece la misma funcionalidad que el conjunto
arguments
y la propiedad
arguments.length
, no proporciona funcionalidad similar a la que ofrece
arguments.callee
. Hay que asegurarse de que no es necesario utilizar
arguments.callee
antes de utilizar el parámetro ... (rest).
En el ejemplo siguiente se reescribe la función
traceArgArray()
con el parámetro ... (rest) en lugar del objeto
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
El parámetro ... (rest) también puede utilizarse con otros parámetros, con tal de que sea el último parámetro de la lista. En el ejemplo siguiente se modifica la función
traceArgArray()
de forma que su primer parámetro,
x
, sea de tipo int y el segundo parámetro utilice el parámetro ... (rest). La salida omite el primer valor porque el primer parámetro ya no forma parte del conjunto creada por el parámetro ... (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
Funciones como objetos
En ActionScript 3.0 las funciones son objetos. Al crear una función, se crea un objeto que no sólo se puede pasar como un parámetro a otra función, sino que además tiene propiedades y métodos asociados.
Las funciones pasadas como argumentos a otra función se pasan por referencia, no por valor. Al pasar una función como un argumento sólo se utiliza el identificador y no el operador paréntesis que se utiliza para llamar al método. Por ejemplo, el código siguiente pasa una función denominada
clickListener()
como un argumento al método
addEventListener()
:
addEventListener(MouseEvent.CLICK, clickListener);
Aunque pueda parecer extraño a los programadores sin experiencia en ActionScript, las funciones pueden tener propiedades y métodos, igual que cualquier otro objeto. De hecho, cada función tiene una propiedad de sólo lectura denominada
length
que almacena el número de parámetros definidos para la función. Es distinta de la propiedad
arguments.length
, que notifica el número de argumentos enviados a la función. Debe recordarse que en ActionScript el número de argumentos enviados a una función pueden superar el número de parámetros definidos para dicha función. En el ejemplo siguiente, que sólo se compila en modo estándar porque el modo estricto requiere una coincidencia exacta entre el número de argumentos pasados y el número de parámetros definidos, se muestra la diferencia entre las dos propiedades:
// 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 */
En modo estándar se pueden definir propiedades de función propias fuera del cuerpo de la función. Las propiedades de función pueden servir como propiedades casi estáticas que permiten guardar el estado de una variable relacionada con la función. Por ejemplo, si se desea hacer un seguimiento del número de veces que se llama a una función determinada. Esta funcionalidad puede ser útil cuando se programa un juego y se desea hacer un seguimiento del número de veces que un usuario utiliza un comando específico, aunque también se podría utilizar una propiedad de clase estática para esto. El ejemplo siguiente, que sólo compila en modo estándar porque el modo estricto no permite añadir propiedades dinámicas a funciones, crea una propiedad de función fuera de la declaración de función e incrementa la propiedad cada vez que se llama a la función:
// Compiles only in standard mode
var someFunction:Function = function ():void
{
someFunction.counter++;
}
someFunction.counter = 0;
someFunction();
someFunction();
trace(someFunction.counter); // 2
Ámbito de una función
El ámbito de una función determina no sólo en qué partes de un programa se puede llamar a esa función, sino también a qué definiciones tiene acceso la función. Las mismas reglas de ámbito que se aplican a los identificadores de variable se aplican a los identificadores de función. Una función declarada en el ámbito global estará disponible en todo el código. Por ejemplo, ActionScript 3.0 contiene funciones globales, como
isNaN()
y
parseInt()
, que están disponibles desde cualquier punto del código. Una función anidada (una función declarada dentro de otra función) puede utilizarse en cualquier punto de la función en que se declaró.
La cadena de ámbitos
Cuando se inicia la ejecución de una función, se crean diversos objetos y propiedades. En primer lugar, se crea un objeto especial denominado
objeto de activación
que almacena los parámetros y las variables o funciones locales declaradas en el cuerpo de la función. No se puede acceder al objeto de activación directamente, ya que es un mecanismo interno. En segundo lugar, se crea una
cadena de ámbitos
que contiene una lista ordenada de objetos en las que el motor de ejecución comprueba las declaraciones de identificadores. Cada función que ejecuta tiene una cadena de ámbitos que se almacena en una propiedad interna. Para una función anidada, la cadena de ámbitos empieza en su propio objeto de activación, seguido del objeto de activación de la función principal. La cadena continúa de esta manera hasta que llega al objeto global. El objeto global se crea cuando se inicia un programa de ActionScript y contiene todas las variables y funciones globales.
Cierres de función
Un
cierre de función
es un objeto que contiene una instantánea de una función y su
entorno léxico
. El entorno léxico de una función incluye todas las variables, propiedades, métodos y objetos de la cadena de ámbitos de la función, junto con sus valores. Los cierres de función se crean cada vez que una función se ejecuta aparte de un objeto o una clase. El hecho de que los cierres de función conserven el ámbito en que se definieron crea resultados interesantes cuando se pasa una función como un argumento o un valor devuelto en un ámbito diferente.
Por ejemplo, el código siguiente crea dos funciones:
foo()
, que devuelve una función anidada denominada
rectArea()
que calcula el área de un rectángulo y
bar()
, que llama a
foo()
y almacena el cierre de función devuelto en una variable denominada
myProduct
. Aunque la función
bar()
define su propia variable local
x
(con el valor 2), cuando se llama al cierre de función
myProduct()
, conserva la variable
x
(con el valor 40) definida en la función
foo().
Por tanto, la función
bar()
devuelve el valor
160
en lugar de
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
Los métodos se comportan de manera similar, ya que también conservan información sobre el entorno léxico en que se crearon. Esta característica se aprecia especialmente cuando se extrae un método de su instancia, lo que crea un método vinculado. La diferencia principal entre un cierre de función y un método vinculado es que el valor de la palabra clave
this
en un método vinculado siempre hace referencia a la instancia a la que estaba asociado originalmente, mientras que en un cierre de función el valor de la palabra clave
this
puede cambiar.
|
|
|