“Clases” y objetos en Javascript. La gran mentira.

No es la primera vez que escribo sobre esto y no será la última. Esta vez sé sobre el tema mucho más que las anteriores, y probablemente después sabre más que ahora, y de eso precisamente es de lo que se trata, de aprender.

Es posible que esta vez levante ampollas, pero ¡ey! los comentarios están abiertos y me encanta discutir, siempre que sea civilizadamente.

Continuar leyendo ““Clases” y objetos en Javascript. La gran mentira.”

Fundamentos de AS3: Definición de métodos

action
Los métodos son una parte fundamental de las clases. Sin ellos sólo servirían para albergar datos.

Una clase es mucho más que una base de datos. Definen lo que un objeto puede hacer, con sus datos y con los de otros objetos.

Si las propiedades son la información, los métodos son la acción.

En este artículo vamos a ver como definir los métodos de una clase. También aprenderemos como pasarles la información que necesitan, si ésta no está en las propiedades de dicha clase.

También estudiaremos qué hacer para que nos devuelvan información cuando terminan de ejecutar sus acciones, si es que ésto es necesario.

Continuar leyendo “Fundamentos de AS3: Definición de métodos”

Todos los objetos en Javascript son dinámicos

Arrays, funciones, constructores, prototipos, … Todos son dinámicos y si no tenemos cuidado sufriremos las consecuencias.
Todos los objetos en javascript son dinámicos, lo que quiere decir que en cualquier momento podemos añadir propiedades y métodos a nuestro objeto:

var persona = {
    nombre : 'persona',
    getNombre : function() {
        return this.nombre;
    }
};
// ...
persona.telefono:'91 XXX XX XX';

Las variables de tipo objeto son referencias a un objeto, y se pasan a las funciones por referencia, no por valor. Esto hace que para copiar un objeto nos tengamos que inventar algún que otro truco.
Pero sobre todo, nos obliga a ser especialmente cuidadosos:

var pedro = persona;
pedro.nombre = 'pedro';
console.log(pedro.nombre);    // >>> 'pedro'
// Pero, sorpresa !!
console.log(persona.nombre);  // >>> 'pedro'

La variable pedro es una referencia al objeto persona, al cambiar la propiedad nombre en pedro estamos cambiando, en realidad, el objeto persona.

Pregunta: ¿Dónde están las clases en Javascript? (I)

Respuesta: No hay.
Javascript es un lenguaje basado en prototipos, no en clases. Eso lo tengo claro. Lo que son los prototipos ya es otro cantar, y de eso es de lo que quiero hablar.

Antes de empezar debe quedar claro que estas líneas son el resultado de mi intento por entender el tema; y sin ser ningún experto y teniendo en cuenta que el cacao de tutoriales, guías y manuales que hay desperdigados por la red son en su mayoría una soberana $&@#~… espero que estas notas despejen algunas dudas. Al menos a mi me han ayudado a centrar mi compresión del lenguaje. Sobre todo para lo que era mi objetivo: ser capaz de hacer lo que ya era capaz de hacer en actionscript.

Para empezar, parece que la mejor manera de entender la herencia basada en prototipos es olvidarse de todo lo que sabemos de herencia basada en clases y empezar de cero.

Continuar leyendo “Pregunta: ¿Dónde están las clases en Javascript? (I)”

Interfaces en Javascript

Una de las primeras cosas que he echado de menos en javascript son las interfaces. Ya sé que la mayoría de los programadores de Javascriot no sabéis lo que son. Seguramente en algún rato libre escribiré sobre ellas con más detalle.
De momento, baste decir que son tremendamente útiles cuando ouno intenta implementar patrones de diseño como el Strategy o el Command. Y son, de hecho, una de las piedras angulares de la programación orientada a objetos en cualquier lenguaje.
En javascript no existen :-(. Pero hay formas, más o menos retorcidas, de implementar algo parecido. (Ver Pro Javascript Design Patterns, de Ross Harmes y Dustin Diaz, Apress 2007).

// Constructor.
var Interface = function(name, methods) {
    if(arguments.length != 2) {
        throw new Error("Interface constructor called with " + arguments.length +
            "arguments, but expected exactly 2.");
    }
    this.name = name;
    this.methods = [];
    for(var i = 0, len = methods.length; i < len; i++) {
        if(typeof methods[i] !== 'string') {
            throw new Error("Interface constructor expects method names to be "
                + "passed in as a string.");
        }
        this.methods.push(methods[i]);
    }
};
// Static class method.
Interface.ensureImplements = function(object) {
    if(arguments.length < 2) {
        throw new Error("Function Interface.ensureImplements called with " +
            arguments.length + "arguments, but expected at least 2.");
    }
    for(var i = 1, len = arguments.length; i < len; i++) {
        var interface = arguments[i];
        if(interface.constructor !== Interface) {
            throw new Error("Function Interface.ensureImplements expects arguments"
                + "two and above to be instances of Interface.");
        }
        for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) {
            var method = interface.methods[j];
            if(!object[method] || typeof object[method] !== 'function') {
                throw new Error("Function Interface.ensureImplements: object "
                    + "does not implement the " + interface.name
                    + " interface. Method " + method + " was not found.");
            }
        }
    }
};

Para utilizarlas hay que instanciar primero la interfaz, pasando los métodos que ha de implementar cualquier clase que la utilice:


var IPajaro =  new Interface('IPajaro', ['anda', 'vuela', 'diAlgo']);

Cuando creemos la clase Pato deberemos implementar, como mínimo, los métodos especificados en el Array del segundo parámetro:


var Pato =  function () {	// Implementa la interfaz IPajaro
	var cuack = function () {
		// Implementación del método cuack
	}
	
	this.anda = function () {
		// Implementación del método andar
	};
	this.nada = function () {
		// Implementación del método nadar
	};
	this.vuela = function () {
		// Implementación del método volar
	};
	this.diAlgo = cuack;
}

¿Por qué querríamos hacer esto? Porque si queremos hacer nuestro código realmente flexible querremos utilizar cualquier tipo de pájaro en nuestro código, y querremos comprobar que cuando usemos un pájaro implemente realmente los métodos que queramos lanzar.
Si no utilizamos la interfaz, la única manera eficiente de hacerlo es comprobar si el presunto objeto pajaro es una instancia de Pato, o de Aguila, o de Cuervo,… mal asunto, porque podemos tener muchos pájaros definidos, ¿Los vamos a comprobar todos? ¿Tendremos que escribir código específico para cada uno de ellos cada vez que queramos utilizar una de sus instancias?…

Para eso sirve una interfaz: establece un contrato para la API de un conjunto de objetos con características comunes, de forma que todos los que implementan la misma se puedan utilizar indistintamente en las partes de nuestro código que no necesiten saber cuál de ellos es exactamente. Así que si queremos hacer volar cualquier pájaro (y en nuestro caso concreto lo que tenemos es un pato) sólo tendremos que hacer lo siguiente:

var vuelaPajaro =  function (pajaro) {
	// Lanza un error si pajaro no implementa los métodos de IPajaro
	Interface.ensureImplements(pajaro, IPajaro);
	pajaro.vuela();
};

var patitoFeo = new Pato();

vuelaPajaro(patitoFeo);

De esta forma la implementación de la función vuelaPajaro() no depende de si el pájaro es un Aguila, un Pato, un Grajo, etc. y al mismo tiempo nos estamos asegurando de que al menos implementa los métodos necesarios para poder utilizarlo como un pájaro.