Buscar:

viernes, 4 de marzo de 2011

Programación Orientada a Objetos con ActionScript [1ª Parte]

En este capítulo se describen los elementos de ActionScript que admiten la programación orientada a objetos. No se describen principios generales de la programación orientada a objetos, como el diseño de objetos, la abstracción, la encapsulación, la herencia y el polimorfismo. El capítulo se centra en la manera de aplicar estos principios con ActionScript 3.0.Como ActionScript fue originalmente un lenguaje de creación de scripts, en ActionScript 3.0 la compatibilidad con la programación orientada a objetos es opcional. Esto proporciona a los programadores la flexibilidad de elegir el mejor enfoque para proyectos de ámbito y complejidad variables. Para tareas pequeñas, es posible que sea suficiente con utilizar un paradigma de programación mediante procedimientos. Para proyectos más grandes, los principios de la
programación orientada a objetos pueden facilitar la comprensión, el mantenimiento y la ampliación del código.

Fundamentos de la programación orientada a objetos

Introducción a la programación orientada a objetos:
La programación orientada a objetos es una forma de organizar el código de un programa agrupándolo en objetos, que son elementos individuales que contienen información (valores de datos) y funcionalidad. La utilización de un enfoque orientado a objetos para organizar un programa permite agrupar partes específicas de la información (por ejemplo, información de una canción como el título de álbum, el título de la pista o el nombre del artista) junto con funcionalidad o acciones comunes asociadas con dicha información (como "añadir pista a la lista de reproducción" o "reproducir todas las canciones de este artista"). Estos elementos se combinan en un solo elemento, denominado objeto (por ejemplo, un objeto "Album" o "MusicTrack"). Poder agrupar estos valores y funciones proporciona varias ventajas, como la
capacidad de hacer un seguimiento de una sola variable en lugar de tener que controlar varias variables, agrupar funcionalidad relacionada y poder estructurar programas de maneras que reflejen mejor el mundo real.

Tareas comunes de la programación orientada a objetos:

En la práctica, la programación orientada a objetos consta de dos partes. Por un lado, las estrategias y técnicas para diseñar un programa (diseño orientado a objetos). Esto es un tema amplio que no se tratará en este capítulo. El otro aspecto de la programación orientada a objetos son las estructuras de programación que están disponibles en un lenguaje de programación determinado para crear un programa con un enfoque orientado a objetos. En este capítulo se describen las siguientes tareas comunes de la programación orientada a objetos:

  • Definición de clases
  • Crear propiedades, métodos y descriptores de acceso get y set (métodos descriptores de acceso)
  • Controlar el acceso a clases, propiedades, métodos y descriptores de acceso
  • Crear propiedades y métodos estáticos
  • Crear estructuras de tipo enumeración
  • Definir y utilizar interfaces
  • Trabajo con herencia, incluida la sustitución de elementos de clase
Conceptos y términos importantes:
La siguiente lista de referencia contiene términos importantes que se utilizan en este capítulo:
  • Atributo: característica asignada a un elemento de clase (como una propiedad o un método) en la definición de clase. Los atributos se suelen utilizar para definir si la propiedad o el método serán accesibles para el código de otras partes del programa. Por ejemplo, private y public son atributos. Sólo se puede llamar a un método privado desde dentro de la clase; en cambio, se puede llamar a un método público desde cualquier parte del programa.
  • Clase: definición de la estructura y el comportamiento de objetos de un tipo determinado (como una plantilla o un plano de los objetos de ese tipo de datos).
  • Jerarquía de clases: estructura de varias clases relacionadas, que especifica qué clases heredan funcionalidad de otras clases.
  • Constructor: método especial que se puede definir en una clase y que se llama para crear una instancia de la clase.
  • Se suele utilizar un constructor para especificar valores predeterminados o realizar operaciones de configuración del objeto.
  • Tipo de datos: tipo de información que una variable concreta puede almacenar. En general, tipo de datos significa lo mismo que clase.
  • Operador punto: signo del punto (.), que en ActionScript y en muchos otros lenguajes de programación se utiliza para indicar que un nombre hace referencia a un elemento secundario de un objeto (como una propiedad o un método). Por ejemplo, en la expresión:
    myObjeto.myPropiedad
    , el operador punto indica que el término myPropiedad hace referencia a algún valor que es un elemento del objeto denominado myObjeto.
  • Enumeración: conjunto de valores de constante relacionados, agrupados por comodidad como propiedades de una clase individual.
  • Herencia: mecanismo de la programación orientada a objetos que permite a una definición de clase incluir toda la funcionalidad de una definición de clase distinta (y añadir nueva funcionalidad).
  • Instancia: objeto real creado en un programa.
  • Espacio de nombres: fundamentalmente se trata de un atributo personalizado que ofrece un control más preciso sobre el código al que puede acceder otro código.

Ejecución de los ejemplos del capítulo:
A medida que progresa en el estudio del capítulo, es posible que desee probar algunos de los listados de código. Como los listados de código de este capítulo se centran principalmente en definir y manipular tipos de datos, para probar los ejemplos hay que crear una instancia de la clase que se está definiendo, manipular esa instancia con sus propiedades o métodos y, a continuación, ver los valores de las propiedades de la instancia. Para ver esos valores hay que escribir valores en una instancia de campo de texto del escenario o utilizar la función trace() para imprimir valores en el panel Salida.
Clases
Una clase es una representación abstracta de un objeto. Una clase almacena información sobre los tipos de datos que un objeto puede contener y los comportamientos que un objeto puede exhibir. La utilidad de esta abstracción puede no ser apreciable al escribir scripts sencillos que sólo contienen unos pocos objetos que interactúan entre sí. Sin embargo, a medida que el ámbito de un programa crece y aumenta el número de objetos que hay que administrar, las clases pueden ayudar a obtener mayor control sobre la creación y la interacción mutua de los objetos.

Con ActionScript 1.0 los programadores podían utilizar objetos Function para crear construcciones similares a las clases. ActionScript 2.0 añadió formalmente la compatibilidad con las clases con palabras clave como class y extends. En ActionScript 3.0 no sólo se mantienen las palabras clave introducidas en ActionScript 2.0, sino que también se han añadido algunas capacidades nuevas, como el control de acceso mejorado con los atributos protected e internal, y mejor control de la herencia con las palabras clave final y override.
A los programadores con experiencia en la creación de clases con lenguajes de programación como Java, C++ o C#, el sistema de objetos de ActionScript les resultará familiar. ActionScript comparte muchas de las mismas palabras clave y nombres de atributo, como class, extends y public, que se describen en las siguiente secciones.

Nota: en este capítulo, el término propiedad designa cualquier miembro de un objeto o una clase, incluidas variables, constantes y métodos. Por otra parte, aunque los términos class y static se suelen utilizar como sinónimos, en este capítulo tienen un significado diferente. Por ejemplo, en este capítulo la frase propiedades de clase hace referencia a todos los miembros de una clase, no únicamente a los miembros estáticos.
Definiciones de clase:
En las definiciones de clase de ActionScript 3.0 se utiliza una sintaxis similar a la utilizada en las definiciones de clase de ActionScript 2.0. La sintaxis correcta de una definición de clase requiere la palabra clave class seguida del nombre de la clase. El cuerpo de la clase, que se escribe entre llaves ({}), sigue al nombre de la clase. Por ejemplo, el código siguiente crea una clase denominada Forma que contiene una variable denominada visible:

public class Forma
{
    var visible:Boolean = true;
}
 
Un cambio de sintaxis importante afecta a las definiciones de clase que están dentro de un paquete. En ActionScript 2.0, si una clase estaba dentro de un paquete, había que incluir el nombre de paquete en la declaración de la clase. En ActionScript 3.0 se incluye la sentencia package y hay que incluir el nombre del paquete en la declaración del paquete, no en la declaración de clase. Por ejemplo, las siguientes declaraciones de clase muestran la manera de definir la clase BitmapData, que forma parte del paquete flash.display, en ActionScript 2.0 y en ActionScript 3.0:

// ActionScript 2.0
class flash.display.BitmapData {}


// ActionScript 3.0
package flash.display
{
    public class BitmapData {}
}


Atributos de clase:
ActionScript 3.0 permite modificar las definiciones de clase mediante uno de los cuatro atributos siguientes:

Todos estos atributos, salvo internal, deben ser incluidos explícitamente para obtener el comportamiento asociado.
Por ejemplo, si no se incluye el atributo dynamic al definir una clase, no se podrá añadir propiedades a una instancia de clase en tiempo de ejecución. Un atributo se asigna explícitamente colocándolo al principio de la definición de clase, como se muestra en el código siguiente:

dynamic class Forma {}

Hay que tener en cuenta que la lista no incluye un atributo denominado abstract. Esto se debe a que las clases abstractas no se admiten en ActionScript 3.0. También se debe tener en cuenta que la lista no incluye atributos denominados private y protected. Estos atributos sólo tienen significado dentro de una definición de clase y no se pueden aplicar a las mismas clases. Si no se desea que una clase sea visible públicamente fuera de un paquete, debe
colocarse la clase dentro de un paquete y marcarse con el atributo internal. Como alternativa, se pueden omitir los atributos internal y public, y el compilador añadirá automáticamente el atributo internal. Si no se desea que una clase sea visible fuera del archivo de código fuente en el que está definida, se debe colocar al final del archivo de
código fuente después de la llave final de la definición de paquete.

Cuerpo de la clase:
El cuerpo de la clase, que se escribe entre llaves, se usa para definir las variables, constantes y métodos de la clase. En el siguiente ejemplo se muestra la declaración para la clase Accessibility en la API de Adobe Flash Player:

public final class Accessibility
{
    public static function get Activo():Boolean;
    public static function ActualizaPropiedad():void;
}

También se puede definir un espacio de nombres dentro de un cuerpo de clase. En el siguiente ejemplo se muestra cómo se puede definir un espacio de nombres en el cuerpo de una clase y utilizarse como atributo de un método en dicha clase:

public class EjemploClase
{
    public namespace EjEspacioNombre;
    EjEspacioNombre function HacerAlgo():void;
}


ActionScript 3.0 permite incluir en el cuerpo de una clase no sólo definiciones, sino también sentencias. Las sentencias que están dentro de un cuerpo de clase pero fuera de una definición de método se ejecutan una sola vez: cuando se encuentra por primera vez la definición de la clase y se crea el objeto de clase asociado. En el ejemplo siguiente se incluye una llamada a una función externa, Hola(), y una sentencia trace que emite un mensaje de confirmación
cuando se define la clase:

function Hola():String
{
    trace("hola");
}
class EjemploClase
{
    Hola();
    trace("Clase Creada");
}
//Salida cuando la clase se crea
//hola
//Clase Creada

A diferencia de las versiones anteriores de ActionScript, en ActionScript 3.0 se permite definir en un mismo cuerpo de clase una propiedad estática y una propiedad de instancia con el mismo nombre. Por ejemplo, el código siguiente declara una variable estática denominada Mensaje y una variable de instancia con el mismo nombre:

class PruebaEstatica
{
    static var Mensaje:String = "Variable Estática";
    var Mensaje:String = "Variable de Instancia";
}
// En el Script
var myPE:PruebaEstatica = new PruebaEstatica();
trace(PruebaEstatica.Mensaje); // Salida: Variable Estatica
trace(myPE.Mensaje); // Salida: Varieble de Instancia

Atributos de propiedad de clase:
En las descripciones del modelo de objetos de ActionScript, el término propiedad significa cualquier cosa que pueda ser un miembro de una clase, incluidas variables, constantes y métodos. Esto difiere de la manera en que se utiliza el término en la Referencia del lenguaje y componentes ActionScript 3.0, donde se aplica a un concepto menos amplio y sólo incluye miembros de clase que son variables o se definen mediante un método captador o definidor. En ActionScript 3.0 hay un conjunto de atributos que se pueden utilizar con cualquier propiedad de una clase. En la tabla siguiente se muestra este conjunto de atributos.

Atributos del espacio de nombres de control de acceso:
ActionScript 3.0 proporciona cuatro atributos especiales que controlan el acceso a las propiedades definidas dentro de una clase: public, private, protected e internal.
El atributo public hace que una propiedad esté visible en cualquier parte del script. Por ejemplo, para hacer que un método esté disponible para el código fuera de su paquete, hay que declarar el método con el atributo public. Esto se cumple para cualquier propiedad, independientemente de que se declare con la palabra clave var, const o function.

El atributo private hace que una propiedad sólo esté visible para los orígenes de llamada de la clase en la que se define la propiedad. Este comportamiento difiere del comportamiento del atributo private en ActionScript 2.0, que permitía a una subclase tener acceso a una propiedad privada de una superclase. Otro cambio importante de comportamiento está relacionado con el acceso en tiempo de ejecución. En ActionScript 2.0, la palabra clave private sólo prohibía el acceso en tiempo de compilación y se podía evitar fácilmente en tiempo de ejecución. Esto ya no se cumple en ActionScript 3.0. Las propiedades marcadas como private no están disponibles en tiempo de compilación ni en tiempo de ejecución.

Por ejemplo, el código siguiente crea una clase simple denominada PrivateEjemplo con una variable privada y después intenta acceder a la variable privada desde fuera de la clase. En ActionScript 2.0, el acceso en tiempo de compilación estaba prohibido, pero la prohibición se podía evitar fácilmente utilizando el operador de acceso a una propiedad ([]), que realiza la búsqueda de propiedades en tiempo de ejecución, no en tiempo de compilación.

class EjemploPrivado
{
    private var privVar:String = "Variable Privada";
}


var miEjemplo:EjemploPrivado = new EjemploPrivado();
trace(
miEjemplo.privVar);// Error en modo estricto en tiempo de ejecucion
trace(
miEjemplo["privVar"]); /* ActionScript 2.0 permite el acceso, pero en ActionScript 3.0, esta es un error en tiempo de ejecucion */

En ActionScript 3.0, un intento de acceder a una propiedad privada mediante el operador punto (miEjemplo.privVar) provoca un error de tiempo de compilación si se utiliza el modo estricto. De lo contrario, el error se notifica en tiempo de ejecución, de la misma manera que al usar el operador de acceso a una propiedad (miEjemplo["privVar"]).

En la tabla siguiente se resumen los resultados de intentar acceder a una propiedad privada que pertenece a una clase cerrada (no dinámica):




En clases declaradas con el atributo dynamic, los intentos de acceder a una variable privada no provocarán un error en tiempo de ejecución. La variable simplemente no está visible, por lo que Flash Player o Adobe® AIR™ devuelven el valor undefined. No obstante, se producirá un error en tiempo de compilación si se utiliza el operador punto en modo estricto. El ejemplo siguiente es igual que el anterior, con la diferencia de que la clase Ejmeplo Privado se declara como una clase dinámica:

dynamic class EjemploPrivado
{
    private var privVar:String = "Variable Privada";
}
var miEjemplo:EjemploPrivado = new EjemploPrivado();
trace(miEjemplo.privVar);//Error en modo estricto, en tiempo de ejecucion
trace(miEjemplo["privVar"]); // Salida: undefined

Las clases dinámicas generalmente devuelven el valor undefined en lugar de generar un error cuando código externo a una clase intenta acceder a una propiedad privada. En la tabla siguiente se muestra que sólo se genera un error cuando se utiliza el operador punto para acceder a una propiedad privada en modo estricto:

El atributo protected, que es una de las novedades de ActionScript 3.0, hace que una propiedad esté visible para los orígenes de llamada en su propia clase o en una subclase. Es decir, una propiedad protegida está disponible en su propia clase o para clases de nivel inferior en la jerarquía de herencia. Esto se cumple tanto si la subclase está en el mismo paquete como si está en un paquete diferente.

Para los usuarios familiarizados con ActionScript 2.0, esta funcionalidad es similar al atributo private en ActionScript 2.0. El atributo protected de ActionScript 3.0 también es similar al atributo protected en Java, pero difiere en que la versión de Java también permite acceder a quien realiza la llamada en el mismo paquete. El atributo protected resulta útil cuando se tiene una variable o método requerido por las subclases que se desea ocultar del código que esté fuera de la cadena de herencia.

El atributo internal, que es una de las novedades de ActionScript 3.0, hace que una propiedad esté visible para los orígenes de llamada en su propio paquete. Es el atributo predeterminado para el código de un paquete y se aplica a cualquier propiedad que no tenga ninguno de los siguientes atributos:
  • public
  • private
  • protected
  • un espacio de nombres definido por el usuario
El atributo internal es similar al control de acceso predeterminado en Java, aunque en Java no hay ningún nombre explícito para este nivel de acceso y sólo se puede alcanzar mediante la omisión de cualquier otro modificador de acceso. El atributo internal está disponible en ActionScript 3.0 para ofrecer la opción de indicar explícitamente la intención de hacer que una propiedad sólo sea visible para orígenes de llamada de su propio paquete.

Atributo static:
El atributo static, que se puede utilizar con propiedades declaradas con las palabras clave var, const o function, permite asociar una propiedad a la clase en lugar de asociarla a instancias de la clase. El código externo a la clase debe llamar a propiedades estáticas utilizando el nombre de la clase en lugar de un nombre de instancia.
Las subclases no heredan las propiedades estáticas, pero las propiedades forman parte de una cadena de ámbitos de subclase. Esto significa que en el cuerpo de una subclase se puede utilizar una variable o un método estático sin hacer referencia a la clase en la que se definió.

Atributos de espacio de nombres definido por el usuario:
Como alternativa a los atributos de control de acceso predefinidos se puede crear un espacio de nombres personalizado para usarlo como un atributo. Sólo se puede utilizar un atributo de espacio de nombres por cada definición y no se puede utilizar en combinación con uno de los atributos de control de acceso (public, private, protected, internal).

Variables:
Las variables pueden declararse con las palabras clave var o const. Es posible cambiar los valores de las variables declaradas con la palabra clave var varias veces durante la ejecución de un script. Las variables declaradas con la palabra clave const se denominan constantes y se les puede asignar valores una sola vez. Un intento de asignar un valor nuevo a una constante inicializada provoca un error.

Variables estáticas:
Las variables estáticas se declaran mediante una combinación de la palabra clave static y la sentencia var o const.
Las variables estáticas, que se asocian a una clase en lugar de a una instancia de una clase, son útiles para almacenar y compartir información que se aplica a toda una clase de objetos. Por ejemplo, una variable estática es adecuada si se desea almacenar un recuento del número de veces que se crea una instancia de una clase o si se desea almacenar el número máximo de instancias de la clase permitidas.

En el ejemplo siguiente se crea una variable totalContador para hacer un seguimiento del número de instancias de clase creadas y una constante MAX_NUM para almacenar el número máximo de instancias creadas. Las variables totalContador y MAX_NUM son estáticas porque contienen valores que se aplican a la clase como un todo en lugar de a una instancia concreta.

class VarEstatico
{
    public static var totalContador:int = 0;
    public static const MAX_NUM:uint = 16;
}


El código externo a la clase VarEstatico y a cualquiera de sus subclases sólo puede hacer referencia a las propiedades totalContador y MAX_NUM a través de la misma clase. Por ejemplo, el siguiente código funciona:

trace(VarEstatico.totalContador); // Salida: 0
trace(VarEstatico.MAX_NUM); // Salida: 16


No se puede acceder a variables estáticas a través de una instancia de la clase, por lo que el código siguiente devuelve errores:

var miVarEstatico:VarEstatico = new VarEstatico();
trace(
miVarEstatico.totalContador); // error
trace(
miVarEstatico.MAX_NUM); // error

Las variables declaradas con las palabras clave static y const deben ser inicializadas a la vez que se declara la constante, como hace la clase VarEstatico para MAX_NUM. No se puede asignar un valor a MAX_NUM dentro del constructor o de un método de instancia. El código siguiente generará un error, ya que no es una forma válida de inicializar una constante estática:

// !! Error al inicializar constante estática de esta manera
class VarEstatico2
{
    public static const TIPO_UNICO:uint;
    function InicializaEstatico():void
    {
       
TIPO_UNICO = 16;
    }
}

Variables de instancia:
Las variables de instancia incluyen propiedades declaradas con las palabras clave var y const, pero sin la palabra clave static. Las variables de este tipo, que se asocian a instancias de clase en lugar de a una clase completa, son útiles para almacenar valores específicos de una instancia. Por ejemplo, la clase Array tiene una propiedad de instancia denominada length que almacena el número de elementos de conjunto contenidos en una instancia concreta de la
clase Array.

En una subclase no se pueden sustituir las variables de instancia, aunque se declaren como var o const. Sin embargo, se puede obtener una funcionalidad similar a la sustitución de variables sustituyendo métodos de captador y definidor.

Métodos:
Los métodos son funciones que forman parte de una definición de clase. Cuando se crea una instancia de la clase, se vincula un método a esa instancia. A diferencia de una función declarada fuera de una clase, un método sólo puede utilizarse desde la instancia a la que está asociado.

Los métodos se definen con la palabra clave function. Al igual que sucede con cualquier propiedad de clase, puede aplicar cualquiera de sus atributos a los métodos, incluyendo private, protected, public, internal, static o un espacio de nombres personalizado. Puede utilizar una sentencia de función como la siguiente:

public function EjemploFuncion():String {}

También se puede utilizar una variable a la que se asigna una expresión de función, de la manera siguiente:

public var EjemploFuncion:Function = function () {}

En la mayoría de los casos se deseará utilizar una sentencia de función en lugar de una expresión de función por las siguientes razones:

  • Las sentencias de función son más concisas y fáciles de leer.
  • Permiten utilizar las palabras clave override y final.
  • Crean un vínculo más fuerte entre el identificador (es decir, el nombre de la función) y el código del cuerpo del método. Como es posible cambiar el valor de una variable con una sentencia de asignación, la conexión entre una variable y su expresión de función se puede hacer más fuerte en cualquier momento. Aunque se puede solucionar este problema declarando la variable con const en lugar de var, esta técnica no se considera una práctica recomendable, ya que hace que el código sea difícil de leer e impide el uso de las palabras clave override y final.
Un caso en el que hay que utilizar una expresión de función es cuando se elige asociar una función al objeto prototipo.

Métodos constructores:
Los métodos constructores, que a veces se llaman simplemente constructores, son funciones que comparten el nombre con la clase en la que se definen. Todo el código que se incluya en un método constructor se ejecutará siempre que una instancia de la clase se cree con la palabra clave new. Por ejemplo, el código siguiente define una clase simple denominada Ejemplo que contiene una sola propiedad denominada Estado. El valor inicial de la variable Estado se
establece en la función constructora.

class Ejemplo
{

    public var Estado:String;
    public function Ejemplo()
    {
        Estado = "Inicializado";
    }
}
var miEjemplo:Ejemplo = new Ejemplo();
trace(miEjemplo.Estado); // Salida: Inicializado


Los métodos constructores sólo pueden ser públicos, pero el uso del atributo public es opcional. No se puede utilizar en un constructor ninguno de los otros especificadores de control de acceso, incluidos private, protected e internal . Tampoco se puede utilizar un espacio de nombres definido por el usuario con un método constructor.

Un constructor puede hacer una llamada explícita al constructor de su superclase directa utilizando la sentencia super(). Si no se llama explícitamente al constructor de la superclase, el compilador inserta automáticamente una llamada antes de la primera sentencia en el cuerpo del constructor. También se puede llamar a métodos de la superclase mediante el prefijo super como una referencia a la superclase. Si se decide utilizar super() y super en el mismo cuerpo de constructor, hay que asegurarse de llamar primero a super(). De lo contrario, la referencia super no se comportará de la manera esperada. También se debe llamar al constructor super() antes que a cualquier sentencia throw o return.

En el ejemplo siguiente se ilustra lo que sucede si se intenta utilizar la referencia super antes de llamar al constructor super(). Una nueva clase, EjemploEx, amplía la clase Ejemplo. El constructor de EjemploEx intenta acceder a la variable de estado definida en su superclase, pero lo hace antes de llamar a super(). La sentencia trace() del constructor de EjemploEx produce el valor null porque la variable Estado no está disponible hasta que se ejecuta el
constructor super().

class EjemploEx extends Ejemplo
{
    public function EjemploEx()
    {
        trace(super.Estado);
        super();
    }
}
var miEjemploEx:EjemploEx = new EjemploEx(); //Salida: Null


Aunque se puede utilizar la sentencia return dentro de un constructor, no se permite devolver un valor. Es decir, las sentencias return no deben tener expresiones o valores asociados. Por consiguiente, no se permite que los métodos constructores devuelvan valores, lo que significa que no se puede especificar ningún tipo de devolución.

Si no se define un método constructor en la clase, el compilador creará automáticamente un constructor vacío. Si la clase amplía otra clase, el compilador incluirá una llamada super() en el constructor que genera.

Métodos estáticos:
Los métodos estáticos, también denominados métodos de clase, son métodos que se declaran con la palabra clave static. Estos métodos, que se asocian a una clase en lugar de a una instancia de clase, son útiles para encapsular la funcionalidad que afecta a algo más que el estado de una instancia individual. Como los métodos estáticos se asocian a una clase como un todo, sólo se puede acceder a dichos métodos a través de una clase, no a través de una instancia de la clase.

Los métodos estáticos son útiles para encapsular la funcionalidad que no se limita a afectar al estado de las instancias de clase. Es decir, un método debe ser estático si proporciona funcionalidad que no afecta directamente al valor de una instancia de clase. Por ejemplo, la clase Date tiene un método estático denominado parse(), que convierte una cadena
en un número. El método es estático porque no afecta a una instancia individual de la clase. El método parse() recibe una cadena que representa un valor de fecha, analiza la cadena y devuelve un número con un formato compatible con la representación interna de un objeto Date. Este método no es un método de instancia porque no tiene sentido aplicar el método a una instancia de la clase Date.

El método parse() estático puede compararse con uno de los métodos de instancia de la clase Date, como getMonth(). El método getMonth() es un método de instancia porque opera directamente en el valor de una instancia recuperando un componente específico, el mes, de una instancia de Date.

Como los métodos estáticos no están vinculados a instancias individuales, no se pueden utilizar las palabras clave this o super en el cuerpo de un método estático. Las referencias this y super sólo tienen sentido en el contexto de un método de instancia.

En contraste con otros lenguajes de programación basados en clases, en ActionScript 3.0 los métodos estáticos no se heredan.

Métodos de instancia:
Los métodos de instancia son métodos que se declaran sin la palabra clave static. Estos métodos, que se asocian a instancias de una clase en lugar de a la clase como un todo, son útiles para implementar funcionalidad que afecta a instancias individuales de una clase. Por ejemplo, la clase Array contiene un método de instancia denominado sort(), que opera directamente en instancias de Array.

En el cuerpo de un método de instancia, las variables estáticas y de instancia están dentro del ámbito, lo que significa que se puede hacer referencia a las variables definidas en la misma clase mediante un identificador simple. Por ejemplo, la clase siguiente, CustomArray, amplía la clase Array. La clase CustomArray define una variable estática denominada arrayContaTotal para hacer un seguimiento del número total de instancias de clase, una variable de instancia denominada arrayNumero que hace un seguimiento del orden en que se crearon las instancias y un método de instancia denominado getPosicion() que devuelve los valores de estas variables.

public class CustomArray extends Array
{
    public static var arrayContTotal:int = 0;

   
public var arrayNumero:int;

   
public function CustomArray()
    {
        arrayNumber = ++arrayContTotal;
    }

    public function getPosicion():String
    {
        return ("Array " + arrayNumber + " de " + arrayCountTotal);
    }
}


Aunque el código externo a la clase debe hacer referencia a la variable estática arrayContTotal a través del objeto de clase mediante CustomArray.arrayContTotal, el código que reside dentro del cuerpo del método getPosicion() puede hacer referencia directamente a la variable estática arrayContTotal. Esto se cumple incluso para variables estáticas de superclases. Aunque en ActionScript 3.0 las propiedades estáticas no se heredan, las propiedades estáticas de las superclases están dentro del ámbito. Por ejemplo, la clase Array tiene unas pocas variables estáticas, una de las cuales es una constante denominada DESCENDING. El código que reside en una subclase de Array puede hacer referencia a la constante estática DESCENDING mediante un identificador simple:

public class CustomArray extends Array
{
    public function PruebaEstatica():void
    {
        trace(DESCENDING); // Salida: 2
    }
}


El valor de la referencia this en el cuerpo de un método de instancia es una referencia a la instancia a la que está asociado el método. El código siguiente muestra que la referencia this señala a la instancia que contiene el método:

class ThisPrueba
{
    function thisValor():ThisPrueba
    {
        return this;
    }
}


var miPrueba:ThisPrueba = new ThisPrueba();
trace(miPrueba.thisValor() == miPrueba); // Salida: true


La herencia de los métodos de instancia se puede controlar con las palabras clave override y final. Se puede utilizar el atributo override para redefinir un método heredado y el atributo final para evitar que las subclases sustituyan un método.

Métodos descriptores de acceso (captador y definidor):
Las funciones descriptoras de acceso get y set, también denominadas captadores y definidores, permiten implementar los principios de programación relacionados con la ocultación de información y encapsulación a la vez que ofrecen una interfaz de programación fácil de usar para las clases que se crean. Estas funciones permiten mantener las propiedades de clase como privadas de la clase ofreciendo a los usuarios de la clase acceso a esas propiedades como si accedieran a una variable de clase en lugar de llamar a un método de clase.

La ventaja de este enfoque es que permite evitar las funciones descriptoras de acceso tradicionales con nombres poco flexibles, como getPropertyName() y setPropertyName(). Otra ventaja de los captadores y definidores es que permiten evitar tener dos funciones públicas por cada propiedad que permita un acceso de lectura y escritura.
La siguiente clase de ejemplo, denominada GetSet, incluye funciones descriptoras de acceso get y set denominadas publicAcceso() que proporcionan acceso a la variable privada denominada PropiedadPrivada:

class GetSet
{
    private var PropiedadPrivada:String;

    public function get publicAcceso():String
    {
        return PropiedadPrivada;
    }

    public function set publicAcceso(setValor:String):void
    {
        PropiedadPrivada = setValor;
    }
}


Si se intenta directamente acceder a la propiedad PropiedadPrivada se producirá un error, como se muestra a continuación:

var miGetSet:GetSet = new GetSet();
trace(miGetSet.PropiedadPrivada); // Error


En su lugar, un usuario de la clase GetSet utilizará algo que parece ser una propiedad denominada publicAcceso, pero que en realidad es un par de funciones descriptoras de acceso get y set que operan en la propiedad privada denominada PropiedadPrivada. En el ejemplo siguiente se crea una instancia de la clase GetSet y después se establece el valor de
PropiedadPrivada mediante el descriptor de acceso público denominado publicAcceso:

var miGetSet:GetSet = new GetSet();
trace(miGetSet.publicAcceso); // resultado: null
miGetSet.publicAcceso = "Hola";
trace(myGetSet.publicAccess); // resultado: Hola


Las funciones captadoras y definidoras también permiten sustituir propiedades heredadas de una superclase, algo que no es posible al usar variables miembro de clase normales. Las variables miembro de clase que se declaran mediante la palabra clave var no se pueden sustituir en una subclase. Sin embargo, las propiedades que se crean mediante funciones captadoras y definidoras no tienen esta restricción. Se puede utilizar el atributo override en funciones captadoras y definidoras heredadas de una superclase.

Métodos vinculados:
Un método vinculado, a veces denominado cierre de método, es simplemente un método que se extrae de su instancia.
Los métodos que se pasan como argumentos a una función o se devuelven como valores desde una función son ejemplos de métodos vinculados. Una de las novedades de ActionScript 3.0 es que un método vinculado es similar a un cierre de función, ya que conserva su entorno léxico incluso cuando se extrae de su instancia. Sin embargo, la diferencia clave entre un método vinculado y un cierre de función es que la referencia this para un método vinculado permanece vinculada a la instancia que implementa el método. Es decir, la referencia this de un método vinculado siempre señala al objeto original que implementó el método. Para los cierres de función, la referencia this es genérica, lo que significa que señala al objeto con el que esté relacionada la función cuando se invoque.

Es importante comprender los métodos vinculados para utilizar la palabra clave this. Debe recordarse que la palabra clave this proporciona una referencia al objeto principal de un método. La mayoría de los programadores que utilizan ActionScript esperan que la palabra clave this siempre haga referencia al objeto o la clase que contiene la definición de un método. Sin embargo, sin la vinculación de métodos esto no se cumplirá siempre. Por ejemplo, en las versiones anteriores de ActionScript, la referencia this no siempre hacía referencia a la instancia que implementaba el método.

En ActionScript 2.0, cuando se extraen métodos de una instancia no sólo no se vincula la referencia this a la instancia original, sino que además las variables y los métodos miembro de la clase de la instancia no están disponibles. Esto es no un problema en ActionScript 3.0 porque se crean métodos vinculados automáticamente cuando se pasa un método como parámetro. Los métodos vinculados garantizan que la palabra clave this siempre haga referencia al objeto o la clase en que se define un método.

El código siguiente define una clase denominada ThisPrueba, que contiene un método denominado foo() que define el método vinculado y un método denominado bar() que devuelve el método vinculado. El código externo a la clase crea una instancia de la clase ThisPrueba, llama al método bar() y almacena el valor devuelto en una variable denominada miFunc.

class ThisPrueba
{
    private var num:Number = 3;
    function foo():void // método vinculado definido
    {
        trace("foo es esto:
" + this);
        trace("num: " + num);
    }
    function bar():Function    {        return foo; // método vinculado devuelto
    }
}
var miPrueba:ThisPrueba = new ThisPrueba();
var miFunc:Function = miPrueba.bar();
trace(this); // salida: [objeto global]
miFunc();
/* salida:
foo es esto: [objeto ThisPrueba]
salida: num: 3*/


Las dos últimas líneas de código muestran que la referencia this del método vinculado foo() sigue señalando a una instancia de la clase ThisPrueba, aunque la referencia this de la línea inmediatamente anterior señala al objeto global.

Además, el método vinculado almacenado en la variable miFunc sigue teniendo acceso a las variables miembro de la clase ThisPrueba. Si se ejecutara este mismo código en ActionScript 2.0, las referencias this coincidirían y el valor de la variable num sería undefined.
La adición de métodos vinculados se aprecia mejor en aspectos como los controladores de eventos, ya que el método addEventListener() requiere que se pase una función o un método como un argumento.


Enumeraciones con clases:
Las enumeraciones son tipos de datos personalizados que se crean para encapsular un pequeño conjunto de valores.
ActionScript 3.0 no ofrece una capacidad de enumeración específica, a diferencia de C++, que incluye la palabra clave enum, o Java, con su interfaz Enumeration. No obstante, se pueden crear enumeraciones utilizando clases y constantes estáticas. Por ejemplo, la clase PrintJob en ActionScript 3.0 utiliza una enumeración denominada PrintJobOrientation
para almacenar el conjunto de valores formado por "landscape" y "portrait", como se muestra en el código siguiente:


public final class PrintJobOrientation
{
    public static const PAISAJE:String = "Paisaje";
    public static const RETRATO:String = "Retrato";
}

Por convención, una clase de enumeración se declara con el atributo final porque no es necesario ampliarla. La clase sólo contiene miembros estáticos, lo que significa que no se crean instancias de la clase, sino que se accede a los valores de la enumeración directamente a través del objeto de clase, como se indica en el siguiente fragmento de código:


var pj:PrintJob = new PrintJob();
if(pj.start())
{
    if (pj.orientation == PrintJobOrientation.RETRATO)
    {
        ...
    }
    ...
}


Todas las clases de enumeración en ActionScript 3.0 sólo contienen variables de tipo String, int o uint. La ventaja de utilizar enumeraciones en lugar de valores literales numéricos o de cadena es que es más fácil detectar errores tipográficos con las enumeraciones. Si se escribe incorrectamente el nombre de una enumeración, el compilador de ActionScript genera un error. Si se utilizan valores literales, el compilador no mostrará ninguna advertencia en caso
de que encuentre una palabra escrita incorrectamente o se utilice un número incorrecto. En el ejemplo anterior, el compilador genera un error si el nombre de la constante de enumeración es incorrecto, como se indica en el siguiente fragmento:

if (pj.orientation == PrintJobOrientation.RETRATO) // Error de compilacion


Sin embargo, el compilador no generará un error si se escribe incorrectamente un valor literal de cadena, como se muestra a continuación:

if (pj.orientation == "Retratoi") // Sin error de Compilacion

Otra técnica para crear enumeraciones también implica crear una clase independiente con propiedades estáticas para la enumeración. No obstante, esta técnica difiere en que cada una de las propiedades estáticas contiene una instancia de la clase en lugar de una cadena o un valor entero. Por ejemplo, el código siguiente crea una clase de enumeración para los días de la semana:
 


public final class Día
{
public static const LUNES:
Día= new Día();
public static const MARTES:
Día= new Día();
public static const MIERCOLES:
Día= new Día();
public static const JUEVES:
Día= new Día();
public static const VIERNES:
Día= new Día();
public static const SABADO:
Día= new Día();
public static const DOMINGO:
Día= new Día();



En ActionScript 3.0 no se emplea esta técnica, pero la utilizan muchos desarrolladores que prefieren la verificación de tipos mejorada que proporciona. Por ejemplo, un método que devuelve un valor de enumeración puede restringir el valor devuelto al tipo de datos de la enumeración. El código siguiente muestra, además de una función que devuelve un día de la semana, una llamada a función que utiliza el tipo de la enumeración como una anotación de tipo:

function getDía():Día
{
    var date:Date = new Date();
    var retDía:Día;
    switch (date.day)
    {
        case 0:
            retDía = Día.LUNES;
        break;
        case 1:
            retDía = Día.MARTES;
        break;
        case 2:
            retDía = Día.MIERCOLES;
        break;
        case 3:
            retDía = Día.JUEVES;
        break;
        case 4:
            retDía = Día.VIERNES;
        break;
        case 5:
            retDía = Día.SABADO;
        break;
        case 6:
            retDía = Día.DOMINGO;
        break;
    }
    return retDía;
}
var DíaSemana:Día = getDía();



También se puede mejorar la clase Día de forma que asocie un entero con cada día de la semana y proporcione un método toString() que devuelva una representación de cadena del día. Se puede mejorar la clase Día de esta manera como ejercicio.


Clases de activos incorporados:
ActionScript 3.0 utiliza clases especiales, denominadas clases de activos incorporados, para representar elementos incorporados. Un activo incorporado es un activo, como un sonido, una imagen o una fuente, que se incluye en un archivo SWF en tiempo de compilación. Incorporar un activo en lugar de cargarlo dinámicamente garantiza que estará disponible en tiempo de ejecución, pero a cambio el tamaño del archivo SWF será mayor.

Utilización de clases de activos incorporados en Flash:
Para incorporar un activo, hay que añadir primero el activo a una biblioteca de archivo FLA. A continuación, hay que usar la propiedad linkage del activo para asignar un nombre a la clase de activo incorporado del activo. Si no se encuentra una clase con ese nombre en la ruta de clases, se generará una clase automáticamente. Después se puede utilizar una instancia de la clase de activo incorporado y utilizar las propiedades y los métodos definidos por la clase.
Por ejemplo, se puede utilizar el código siguiente para reproducir un sonido incorporado vinculado a una clase de activo incorporado denominada PianoMusic:

var piano:PianoMusic = new PianoMusic();
var sndChannel:SoundChannel = piano.play();
































































































































lunes, 21 de febrero de 2011

El lenguaje ActionScript y su Sintaxis 4ª Parte (Final)



Condicionales

ActionScript 3.0 proporciona tres sentencias condicionales básicas que se pueden usar para controlar el flujo del programa.

if..else
La sentencia condicional if..else permite comprobar una condición y ejecutar un bloque de código si dicha condición existe, o ejecutar un bloque de código alternativo si la condición no existe. Por ejemplo, el siguiente fragmento de código comprueba si el valor de x es superior a 20 y genera una función trace() en caso afirmativo o genera una función trace() diferente en caso negativo:

if (x > 20)
{
trace("x es > 20");
}
else
{
trace("x es <= 20");
}


Si no desea ejecutar un bloque de código alternativo, se puede utilizar la sentencia if sin la sentencia else.

if..else if

Puede comprobar varias condiciones utilizando la sentencia condicional if..else if. Por ejemplo, el siguiente fragmento de código no sólo comprueba si el valor de x es superior a 20, sino que también comprueba si el valor de x es negativo:

if (x > 20)
{
trace("x is > 20");
}
else if (x < 0)
{
trace("x is negative");
}


Si una sentencia if o else va seguida de una sola sentencia, no es necesario escribir dicha sentencia entre llaves. Por ejemplo, en el código siguiente no se usan llaves:

if (x > 0)
trace("x es positivo");
else if (x < 0)
trace("x es negativo");
else
trace("x es 0");


No obstante, Adobe recomienda utilizar siempre llaves, ya que podría producirse un comportamiento inesperado si más adelante se añadieran sentencias a una sentencia condicional que no esté escrita entre llaves. Por ejemplo, en el código siguiente el valor de positivoNums aumenta en 1 independientemente de si la evaluación de la condición devuelve true:

var x:int;
var positivoNums:int = 0;
if (x > 0)
trace("x es positivo");
positivoNums++;
trace(positivoNums); // 1


switch
La sentencia switch resulta útil si hay varios hilos de ejecución que dependen de la misma expresión de condición. La funcionalidad que proporciona es similar a una serie larga de sentencias if..else if, pero su lectura resulta un tanto más sencilla. En lugar de probar una condición para un valor booleano, la sentencia switch evalúa una expresión y utiliza el resultado para determinar el bloque de código que debe ejecutarse. Los bloques de código empiezan por una sentencia case y terminan con una sentencia break. Por ejemplo, la siguiente sentencia switch imprime el día de la semana en función del número de día devuelto por el método Date.getDay():

var Fecha:Date = new Date();
var DiaNum:uint = Fecha.getDay();
switch(DiaNum)
{
    case 0:
        trace("Domingo");
    break;
    case 1:
        trace("Lunes");
    break;
    case 2:
        trace("Martes");
    break;
    case 3:
        trace("Miercoles");
    break;
    case 4:
        trace("Jueves");
    break;
    case 5:
        trace("Viernes");
    break;
    case 6:
        trace("Sábado");
    break;
    default:
        trace("Fuera de Rango");
    break;
}
Reproducir indefinidamente

Las sentencias de bucle permiten ejecutar un bloque específico de código repetidamente utilizando una serie de valores o variables. Adobe recomienda escribir siempre el bloque de código entre llaves ({}). Aunque puede omitir las llaves si el bloque de código sólo contiene una sentencia, no es recomendable que lo haga por la misma razón expuesta para las condicionales: aumenta la posibilidad de que las sentencias añadidas posteriormente se excluyan inadvertidamente del bloque de código. Si posteriormente se añade una sentencia que se desea incluir en el bloque de código, pero no se añaden las llaves necesarias, la sentencia no se ejecutará como parte del bucle.

for
El bucle for permite repetir una variable para un rango de valores específico. Debe proporcionar tres expresiones en una sentencia for: una variable que se establece con un valor inicial, una sentencia condicional que determina cuándo termina la reproducción en bucle y una expresión que cambia el valor de la variable con cada bucle. Por ejemplo, el
siguiente código realiza cinco bucles. El valor de la variable i comienza en 0 y termina en 4, mientras que la salida son los números 0 a 4, cada uno de ellos en su propia línea.

var i:int;
for (i = 0; i < 5; i++)
{
trace(i);
}


for..in
El bucle for..in recorre las propiedades de un objeto o los elementos de un conjunto. Por ejemplo, se puede utilizar un bucle for..in para recorrer las propiedades de un objeto genérico (las propiedades de un objeto no se guardan en ningún orden concreto, por lo que pueden aparecer en un orden aparentemente impredecible):

var miObj:Object = {x:20, y:30};
for (var i:String in miObj)
{
    trace(i + ": " + miObj[i]);
}
// Salida:
// x: 20
// y: 30


También se pueden recorrer los elementos de un conjunto:

var miArray:Array = ["uno", "dos", "tres"];
for (var i:String in miArray)
{
    trace(miArray[i]);
}
// Salida:
// uno
// dos
// tres


Lo que no se puede hacer es repetir las propiedades de un objeto si se trata de una instancia de una clase definida por el usuario, a no ser que la clase sea una clase dinámica. Incluso con instancias de clases dinámicas, sólo se pueden repetir las propiedades que se añadan dinámicamente.

for each..in
El bucle for each..in recorre los elementos de una colección, que puede estar formada por las etiquetas de un objeto XML o XMLList, los valores de las propiedades de un objeto o los elementos de un conjunto. Por ejemplo, como muestra el fragmento de código siguiente, el bucle for each..in se puede utilizar para recorrer las propiedades de un objeto genérico, pero al contrario de lo que ocurre con el bucle for..in, la variable de iteración de los bucles
for each..in contiene el valor contenido por la propiedad en lugar del nombre de la misma:

var miObj:Object = {x:20, y:30};
for each (var num in miObj)
{
    trace(num);
}
// Salida:
// 20
// 30


Se puede recorrer un objeto XML o XMLList, como se indica en el siguiente ejemplo:

var miXML:XML = <users>
<fname>Jane</fname>
<fname>Susan</fname>
<fname>John</fname>
</users>;

for each (var item in miXML.fname)
{
    trace(item);
}
/* output
Jane
Susan
John
*/


También se pueden recorrer los elementos de un conjunto, como se indica en este ejemplo:

var miArray:Array = ["uno", "dos", "tres"];
for each (var item in miArray)
{
    trace(item);
}
// Salida:
// uno
// dos
// tres


No se pueden recorrer las propiedades de un objeto si el objeto es una instancia de una clase cerrada. Tampoco se pueden recorrer las propiedades fijas (propiedades definidas como parte de una definición de clase), ni siquiera para las instancias de clases dinámicas.

while
El bucle while es como una sentencia if que se repite con tal de que la condición sea true. Por ejemplo, el código siguiente produce el mismo resultado que el ejemplo del bucle for:

var i:int = 0;
while (i < 5)
{
    trace(i);
    i++;
}


Una desventaja que presenta el uso de los bucles while frente a los bucles for es que es más probable escribir un bucle infinito con bucles while. El código de ejemplo de bucle for no se compila si se omite la expresión que aumenta la variable de contador, mientras que el ejemplo de bucle while sí se compila si se omite dicho paso. Sin la expresión que incrementa i, el bucle se convierte en un bucle infinito.

do..while
El bucle do..while es un bucle while que garantiza que el bloque de código se ejecuta al menos una vez, ya que la condición se comprueba después de que se ejecute el bloque de código. El código siguiente muestra un ejemplo simple de un bucle do..while que genera una salida aunque no se cumple la condición:

var i:int = 5;
do
{
    trace(i);
    i++;
} while (i < 5);
// Salida: 5


Funciones

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:
En esta sección se ofrece una definición básica de función y se describen las técnicas de invocación.
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, la función trace(), que es una función de nivel superior en ActionScript 3.0, se usa por todo el tutorial:

trace("Uso de seguimiento para ayudar a depurar el 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 invoca la función con la cadena "Hola" como valor del parámetro:

    function
    traceParametro(aParam:String)
    {
        trace(aParam);
    }
    traceParametro("Hola"); // Hola
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 traceParametro mediante una expresión de función:

    var
    traceParametro:Function = function (aParam:String)
    {
        trace(aParam);
    };
    traceParametro("Hola"); // Hola


    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]("Hola");
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 invocar 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 Ejemplo con dos métodos: MetodoExpresion(), que se declara con
una expresión de función, y MetodoSentencia(), que se declara con una sentencia de función. En modo estricto no se puede utilizar la sintaxis con punto para invocar el método MetodoExpresion().

class Ejemplo
{
    var MetodoExpresion = function() {}
    function MetodoSentencia() {}
}
var miEj:Ejemplo = new Ejemplo();
miEj.MetodoExpresion(); // error en modo estricto, bien en modo estándar
miEj.MetodoSentencia(); // bien en modo estricto y estándar


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 ([]) en lugar del operador punto (.). La siguiente llamada a método funciona
correctamente tanto en modo estricto como en modo estándar:

miEjemplo["MedotoLiteral"]();

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.

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 eliminación de datos innecesarios. 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 eliminación de datos innecesarios, 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 Prueba es dinámica, lo que significa que se puede añadir una propiedad denominada  funcionExp que contendrá una expresión de función. Se puede llamar a la función funcionExp() con el operador punto, pero cuando se elimina la propiedad funcionExp, la función deja de ser accesible.

dynamic class Prueba {}
var miPrueba:Prueba = new Prueba();
// Funcion Expresion
miPrueba.funcionExp = function () { trace("Funcion Expresion") };
miPrueba.funcionExp(); // Funcion Expresion
delete miPrueba.funcionExp;
miPrueba.funcionExp(); // 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 SentenciaFunc() no funciona.

dynamic class Prueba {}
var miPrueba:Prueba = new Prueba();
//Funcion Sentencia
function SentenciaFun() { trace("Funcion Sentencia") }
miPrueba.Sentencia = SentenciaFun;
miPrueba.Sentencia(); //Funcion Sentencia
delete miPrueba.Sentencia;
delete SentenciaFun; //Sin Efecto
SentenciaFun();//Funcion Sentencia
miPrueba.Sentencia(); //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 SentenciaPrueba() antes de que se defina:

SentenciaPrueba(); // Prueba Sentencia

function SentenciaPrueba():void
{
    trace("SentenciaPrueba");
}


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:

ExpresionPrueba(); // Error en tiempo de ejecucion

var ExpresionPrueba:Function = function ()
{
    trace("ExpresionPrueba");
}


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 DobleNum(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 DobleNum(baseNum:int):int
{
    return (baseNum * 2);
    trace("Despues de return"); // Esta sentencia trace no se ejecutará.

}

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 DobleNum(baseNum:int):int
{
    trace("Despues de 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 getNombreyVersion():

function getNombreyVersion():String
{
    function getVersion():String
    {
        return "10";
    }
    function getNombre():String
    {
        
return "Flash Player";
    }
    return (getNombre() + " " + getVersion());
}
trace(getNombreyVersion()); // 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.

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 pasaPrimitivos() 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 pasaPrimitivos().
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 pasaPrimitivos(xParam:int, yParam:int):void
{
    xParam++;
    yParam++;
    trace(xParam, yParam);
}
var xValue:int = 10;
var yValue:int = 15;
trace(xValue, yValue);// 10 15
pasaPrimitivos(xValue, yValue); // 11 16
trace(xValue, yValue);// 10 15


En la función pasaPrimitivos(), 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 pasaByRef(). 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 pasaByRef(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
pasaByRef(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 incluye como novedad la capacidad de 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 defaultValor(x:int, y:int = 3, z:int = 5):void
{
    trace(x, y, z);
}
defaultValor(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);
    // Salida:
    // 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);
    // Sin Salida

    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 al ú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);
// Salida:
// 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);
// Salida:
// 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);

El método Array.sort() también define un parámetro que acepta una función.




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:

// Solo se compila en modo estandar
function traceLength(x:uint, y:uint):void
{
trace("Argumentos recibidos: " + arguments.length);
trace("Argumentos esperados: " + traceLength.length);
}
traceLength(3, 5, 7, 11);
/* Salida:
Argumentos recividos: 4
Argumentos esperados: 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:


// Solo se compila en modo estandar
var AlgunaFuncion:Function = function ():void
{
    AlgunaFuncion.counter++;
}
AlgunaFuncion.counter = 0;AlgunaFuncion();AlgunaFuncion();
trace(
AlgunaFuncion.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 Flash Player o Adobe AIR 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 miProducto. Aunque la función bar() define su propia variable local x (con el valor 2), cuando se llama al cierre de función miProducto(), 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 de8.

function foo():Function
{
    var x:int = 40;
    function rectArea(y:int):int // Funcion de cierre definido
    {
        return x * y
    }
    return rectArea;
}
function bar():void
{
    var x:int = 2;
    var y:int = 4;
    var miProducto:Function = foo();
    trace(miProducto(4)); // Cierre de funcion llamada
}
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.

Brindanos tus Donaciones