JS y Herencia I (Prototipos)

Para todos los que llegan de algún lenguaje OOP de los más utilizados dentro del desarrolo Web (Java, C#, PHP, por mencionar algunos), el sistema de jerarquía de objetos y herencia de JavaScript resulta confuso en extremo.

Prototipos

JavaScript al igual que otros pocos lenguajes (Io y Self entre otros) esta basado en prototipos, en el cual cada objeto en memoria tiene un prototipo, el cual consiste ni más ni menos en una referencia a otro objeto, del cual hereda  métodos y propiedades.

Prototipos

De esta manera, como se puede ver en el gráfico, el objeto (o clase, si queremos llamarlo de esa manera) Pirata heredará las propiedades y métodos de los objetos Persona y Object.
De tal manera, podemos definir el resultado final como:

Suma de prototipos

Esta forma de herencia resulta muy performante ya que los métodos heredados no son más que una referencia al método original, con lo cual el uso de memoria se reduce drásticamente.
En los casos que se sobre escriba un método heredado (acción que se conoce como override), no modifica el método referenciado, sino que crea un método en el objeto en sí, que tendrá precedencia sobre el heredado (en el gráfico puede observarse con los métodos toString y saludar).

En la práctica

Algunos browsers permiten manipular los prototipos de los objetos de manera directa mediante la propiedad __proto__ de los mismos:

var myObj = {};

myObj.__proto__ = {
	checkPrototype: function() {
		return true;
	}
}

myObj.checkPrototype(); // true

Dejando de lado esta característica non-standard, JavaScript provee de una funcionalidad “similar” a la de las clases, se hace uso de las funciones como constructores, a las cuales se les modifica el prototipo para que todas sus instancias hereden los métodos.

/*
 * Definimos el constructor
 */
function Persona(nombre) {
	this.nombre = nombre;
};

/*
 * Modificamos su prototipo
 */
Persona.prototype.saludar = function() {
	return "Hola!";
};

Persona.prototype.correr = function() {
	this.corriendo = true;
	// devolvemos la instancia para tener chaining sobre
	// sus metodos
	return this;
};

Persona.prototype.comer = function() {
	this.ultimaComida = new Date;
	return this;
};

var luis = new Persona("Luis");

/*
 * Verificamos que sean instancias de
 */
luis instanceof Persona; // true
luis instanceof Object; // true

luis.comer().correr();
luis.corriendo; // true

Ahora, supongamos que queremos implementar la clase Pirata, heredando los métodos de Persona:

/*
 * Definimos el constructor
 */
function Pirata(nombre) {
	// Ejecutamos el constructor de Persona
	Persona.apply(this, arguments);
};

/*
 * Generamos el protipo, en otras palabras
 * extendemos la "clase"
 */
Pirata.prototype = new Persona;

/*
 * Otra forma de modificar el prototipo
 */
(function(p) { //p es el protipo de Pirata
	p.atacar = function() {
		//...
		return this;
	};

	p.toString = function() {
		return this.nombre + " aaarrrrhhh!";
	};

	p.saludar = function() {
		return this.toString();
	}
})(Pirata.prototype);

var sandokan = new Pirata("Sandokan");

sandokan instanceof Pirata; // true
sandokan instanceof Persona; // true
sandokan instanceof Object; // true

sandokan.toString(); // "Sandokan aaarrrrhhh!"

En el próximo post del tema vamos a analizar las implementaciones que realizan los distintos frameworks para lograr un sistema de clases más amigable así como tambien generar nuestro propio sistema de clases.

Leer más:



Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Comment

You may use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>