Objetos en Javascript (II). Clases y su relación con prototipos y constructores

En la entrada anterior sobre objetos en Javascript se trató la peculiaridad de los prototipos: cada objeto, además de sus propiedades, tiene asociado un objeto prototipo que le dota de todas sus propiedades. A su vez un objeto prototipo de otro puede tener sus propios prototipos, hasta llegar al objeto prototipo final que es Object.prototype.

Es fácil suponer que este mecanismo de objectos prototipos y su herencia pueden simular de algún modo el sistema de clases de los lenguajes orientados a objetos como Java: si se crea un objeto con el fin de que sea prototipo de otros podríamos considerarlo como una clase; a su vez, los objetos que tengan por prototipo una clase común, podrán ser asimilados como instancias de esa clase.

Para crear objetos “de una clase” se puede usar la función Object.create que está reconocida en ECMAScript 5 o también una función con el operador new. En este primer ejemplo vamos a emplear Object.create() para generar nuevos objetos. Si además creamos una función “factory” que genera objetos de la clase indicada, separando los métodos y los valores nos quedará un código mucho más adecuado.

Veámoslo con un ejemplo:


function operadorNumeros(x, y)
{
	var on = Object.create(operadorNumeros.operaciones);	//Se crea un nuevo objeto con ese prototipo
	on.x = x;
	on.y = y;

	return on;
}

//Prototipo con las operaciones que van a compartir los objetos de esta "clase"
operadorNumeros.operaciones = {
	suma:function()
	{
		return (this.x + this.y);
	},

	multiplica: function()
	{
		return(this.x * this.y);
	},

	toString:function()
	{
		return ("operador con valores x:" + this.x + "; y:" + this.y);
	}
}

var opNum = operadorNumeros(2,3);	//Factory que devuelve un nuevo objeto
opNum.suma();		//Devuelve 5
opNum.multiplica();	//Devuelve 6
opNum.toString();

A pesar de que pueda parecer extraño, no es mala idea que las operaciones de la clase queden como una propiedad de la función factory que genera los objetos de la clase. Nótese que recibe el nombre de operaciones, pero no prototype. Estas asignación es interna, y la hace al invocar el Object.create(prototipo);

Es importante destacar que en este caso la clase de la cual parten los objetos creados es realmente un objeto, y que solamente se le han dotado de métodos. Esto es así porque si se hubieran creado atributos serían compartidos entre todas las instancias. No es como en Java o C++: aquí el concepto de clase se emula mediante compartir un objeto prototipo, pero que no deja de ser un objeto y por tanto una instancia única que comparten los objetos generados.

También es posible utilizar constructores para la creación de los objetos de la clase, mediante el empleo de la palabra reservada new precediendo a la función generadora que actúa como constructor. En este caso no se usa Object.create porque con new se genera el objeto, y la asignación del prototipo debe hacerse a mano.

En esta ocasión, la función constructura no hace ningún llamamiento a Object.create


function OperadorNumeros(x, y)
{
	on.x = x;
	on.y = y;
}

OperadorNumeros.prototype = {
	suma:function()
	{
		return (this.x + this.y);
	},

	multiplica: function()
	{
		return(this.x * this.y);
	},

	toString:function()
	{
		return ("operador con valores x:" + this.x + "; y:" + this.y);
	}
}

//
var opNum = new OperadorNumeros(2,3);	//Genera el nuevo objeto con el constructor
opNum.suma();		//Devuelve 5
opNum.multiplica();	//Devuelve 6
opNum.toString();

Comparado con el ejemplo anterior los cambios son claros. En primer lugar se emplea mayúscula como convención para indicar que se trata de una clase. Esto es algo habitual y si no se hace no tiene por qué dar ningún problema, pero es una buena práctica. Por otra parte la función constructora no genera ningún objecto internamente con Object.create(prototipo) ya que se emplea el operador new para generarlo: es el intérprete el que crea un nuevo objeto basado en ese. Y finalmente, la más clara es la definición del prototipo de forma explícita escribiendo directamente la propiedad prototype.

Otro aspecto a destacar es que como en Javascript, como hemos visto, las funciones son objetos y como cada objeto tiene un prototipo asociado (aunque sea el básico – Object.prototype-), cada función tiene asignada una propiedad prototype. En el ejemplo anterior se ha definido de forma explícita con las operaciones del operador de números, pero es posible dejarla sin definir.

function suma(a,b){return a+b;}
Object.prototype.isPrototypeOf(suma.prototype)	//Devuelve true

Finalmente, para rizar más el rizo cada objeto prototype tiene asociada una propiedad constructor cuyo valor es la función que genera el objeto. En el ejemplo OperadorNumeros se ha definido la función al principio y luego el prototipo. Pues la propiedad constructora del prototipo es la propia función definida más arriba ya que coinciden los nombres y la asignación se realizar de forma automática. No ponerlo sería equivalente a esto:

OperadorNumeros.prototype.constructor = OperadorNumeros;

Si vamos al último ejemplo de la función suma, la propiedad constructor del prototipo tiene como valor la función suma de forma automática:

function suma(a,b){return a+b;}
suma.prototype.constructor   //Devuelve la función suma

De este modo tan enrevesado podemos conocer la clase de la cual proviene un objeto: si comprobamos el constructor del prototipo de un objeto obtendremos su clase. Y claro, está como las priedades del prototipo son heredadas por el objeto, su constructor directamente (sin pasar por el prototipo) nos da de igual modo la clase a la que pertenece.

En resumen, es importante conocer la relación existente entre los prototipos, las clases y los constructores. Si vamos al código fuente de muchas librerías de Javascript, nos encontraremos con cosas del tipo Objeto.prototype.operación = … que ahora podremos interpretar como que se está dotando a la “clase” de los objetos reales que usamos con nuevas operaciones que pueden utilizar. Y del mismo modo podremos añadir nosotros las nuestras.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s