Desarrollo orientado a objetos eficaz
Primer enfoque
1. Recordatorios
Antes de abordar el desarrollo de objetos en JavaScript, vamos a recordar algunos aspectos sobre este modo de desarrollo. En primer lugar, se debe entender que este tipo de desarrollo se construyó con el objetivo de mejorar la calidad del mismo, distinguiendo varios niveles de diseño. El primer nivel es establecer un plan de construcción y el último nivel, permitir obtener un conjunto de objetos que brinden el servicio deseado. Por otro lado, los métodos de análisis y diseño han intentado seguir este modelo para garantizar una transición de desarrollo más simple, desde el modelado de lo real a la etapa de construcción.
a. Clase
Generalmente, un objeto se construye a partir de una clase. Esta clase actúa como una fábrica de objetos. Tiene un plan de construcción y puede suministrar objetos bajo demanda.
En los planes de fabricación, tenemos propiedades que almacenan información específica de cada objeto y métodos que permiten interactuar con estos objetos.
Una propiedad es, en realidad, una variable cuyo contexto de uso se limita a un objeto en particular. En cierto modo, solo existe dentro del objeto, por lo que le confiere un estado.
Un método de un objeto también es una función de JavaScript, pero que solo existe en el contexto de este objeto y tiene acceso a las propiedades del objeto (influyendo así en el estado del objeto).
Si hacemos una analogía con los objetos que nos rodean, podemos tomar el ejemplo de un teléfono móvil. Estas propiedades están formadas por su configuración y la información que hemos almacenado en ellas, podemos ver que están vinculadas solo a un dispositivo. Los métodos son el conjunto de acciones disponibles en el móvil, como encenderlo, ajustar el volumen, hacer una llamada, etc. Si están presentes para todos los dispositivos, podemos ver que deberían estar integrados en una clase Mobile.
Un objeto puede estar formado por otros objetos, es un conjunto de objetos. Nuestro teléfono móvil tiene batería, placa base, pantalla, etc. La placa base se puede dividir en placa de cobre, transistores, resistencias, etc. Una propiedad de un objeto es realmente un objeto nuevo, aunque puede ser incorrecto suponer que algunos valores no pueden ser objetos...
Contexto de ejecución
1. this
En JavaScript, this no se refiere al objeto resultante de la instancia únicamente, sino que en realidad se refiere al objeto que se está utilizando. Esto significa que el intérprete de JavaScript evalúa this cuando llama a un método; este es un argumento adicional que se agrega automáticamente.
En otras palabras, cuando se referencia a this, nunca se está seguro de que se aplicará a su instancia. Esta característica es bastante confusa para un desarrollador de objetos "clásico", pero en realidad ofrece un gran poder, como veremos.
Ejemplo
function Persona( apellido, nombre ) {
this.apellido = apellido;
this.nombre = nombre;
this.hola = function() {
alert( "hola " + this.apellido );
};
}
function Perro( nombre ) {
this.nombre = nombre;
}
var p = new Persona( "brillant", "alexandre" );
var c = new Perro( "tobby" );
c.hola = p.hola;
c.hola();
En el ejemplo, tenemos dos clases: Persona y Perro. Estas clases no tienen ninguna relación básica entre sí. Asociamos a la clase Perro el método hola de la clase Persona, después ejecutamos este método a través de una instancia. Recibimos el mensaje "hola tobby".
this representa la instancia en la que se realiza una llamada al método. Esto se puede traducir de la siguiente manera:
miInstancia.miMetodo( ... )
En realidad, el comportamiento interno es este:
this = miInstancia
miMetodo( this, ... )
Ahora imaginemos que tenemos que pasar una referencia a una función de nuestro objeto. Como recordatorio, JavaScript es un lenguaje que fue diseñado originalmente para funcionar en relación con una página HTML. En una página HTML, tenemos todo tipo de eventos que se relacionarán con interacciones directas o indirectas del usuario y que requerirán procesamiento en forma de llamada a un método. Este método bien podría provenir de uno de nuestros objetos.
Ejemplo:
setTimeout( function() { alert( "hola" ); }, 1000 );
Esta función setTimeout presente por defecto...
Clases predefinidas
Ahora presentaremos algunas clases de uso común. El objetivo no es presentar de forma exhaustiva las propiedades y métodos de cada clase, sino centrarse en los más útiles. Para obtener una vista más completa, puede consultar los enlaces al final del libro.
1. Object
Todos los objetos creados en JavaScript comparten las propiedades y métodos de la clase Object.
La propiedad principal es prototype que analizaremos más adelante y que tiene la ventaja de brindar acceso a propiedades y métodos que están presentes de manera única en todas las instancias.
Ejemplo
function Persona( apellido, nombre ) {
this.apellido = apellido;
this.nombre = nombre;
}
var p1 = new Persona( "brillant", "alexandre" );
var p2 = new Persona( "juan", "puente" );
Persona.prototype.hola = function() {
alert( "hola " + this.nombre );
};
p1.hola();
p2.hola();
En este ejemplo, añadimos un método hola a la clase Persona que compartimos con todas las instancias. Prueba de ello es que lo añadimos después de crear los objetos y, sin embargo, está disponible con p1 y p2.
El método toString también está presente en la clase Object, para dar una forma más legible del objeto.
Ejemplo
function Persona( apellido, nombre ) {
this.apellido = apellido;
this.nombre = nombre;
}
var p1 = new Persona( "brillant", "alexandre" );
alert( p1 ) ;
Obtenemos el resultado poco agradable [object object], que significa simplemente que nuestro objeto en sí está formado por dos objetos (sus propiedades). El método alert realmente usa el método toString, presente por defecto, que devuelve este tipo de resultado. Para mejorar la visualización, ampliaremos un poco nuestra clase Persona.
function Persona( apellido, nombre ) {
this.apellido = apellido;
this.nombre = nombre;
this.toString = function() {
return this.apellido + " " + this.nombre ;
...
Nociones avanzadas
1. Prototipado
a. Simple
Como hemos visto en la clase Object, todas las clases tienen una propiedad prototype que es, en sí misma, un objeto. Esta propiedad está disponible dentro de cada instancia de la clase. Tiene la particularidad de ser usada durante la invocación de métodos o propiedades. De hecho, si no se encuentra un método o una propiedad en la instancia, se busca en la propiedad prototype.
La propiedad prototype será importante tan pronto como tengamos varias instancias porque, de lo contrario, las definiciones de función se duplican dentro de cada instancia y, por lo tanto, ocupan más espacio de memoria.
Ejemplo
function Persona( apellido, nombre ) {
this.apellido = apellido;
this.nombre = nombre;
this.hola = function() {
alert( "hola " + this.nombre );
};
}
var p1 = new Persona( "brillant", "alexandre" );
var p2 = new Persona( "brillant", "alexandre" );
alert( p1.hola == p2.hola );
Creamos dos instancias y luego comparamos la función hola, resulta ser diferente para cada instancia. Esto no es aceptable en términos de recursos y términos de objeto, porque nuestros métodos no deben variar de una instancia a otra. Gracias a la propiedad prototype, podremos remediarlo.
Si reescribimos nuestro ejemplo, ahora tenemos:
function Persona( apellido, nombre ) {
this.apellido = apellido;
this.nombre = nombre;
}
Persona.prototype.hola = function() {
alert( "hola " + this.nombre );
};
var p1 = new Persona( "brillant", "alexandre" );
var p2 = new Persona( "brillant", "alexandre" );
alert( p1.hola == p2.hola );
Ahora tenemos dos métodos idénticos con nuestra prueba de igualdad. Cuantos más objetos tengamos, más importante será el ahorro de memoria.
Dado que la propiedad prototype también es un objeto (pero sin una propiedad prototype), podríamos haber usado la otra forma con llaves.
Ejemplo
Persona.prototype = {
hola: function() {
alert(...
Framework para el desarrollo orientado a objetos
1. Prototype
Prototype (http://www.prototypejs.org) es un framework JavaScript que ofrece diferentes extensiones para el uso de Ajax, DOM y JSON, pero también para el desarrollo de objetos, como vamos a ver.
a. Creación de una clase
Prototype esconde toda la mecánica de la gestión de objetos. La clase Class se utiliza para crear sus clases. Pasamos como argumento el cuerpo de la clase (lo que se corresponde con nuestro objeto prototype). El método initialize se corresponde con el constructor.
Ejemplo
var Persona = Class.create(
{
initialize: function( apellido, nombre ) {
this.apellido = apellido;
this.nombre = nombre;
},
hola: function() {
alert( "Hola “ + this.nombre );
}
}
);
En nuestro ejemplo, hemos construido una clase Persona, que recibe como argumento del constructor un nombre y un apellido. Hay pocos cambios con respecto a la operación predeterminada. El concepto de prototipado está oculto.
b. Herencia
La herencia se realiza simplemente especificando la clase padre en el método create. Si necesita sobrecargar un método, solo necesita reescribir la firma del método agregando como primer parámetro $super. Este último contiene una referencia al método padre.
Ejemplo
var Empleado = Class.create(
Persona,
{
initialize: function( $super, apellido, nombre, puesto ) {
$super( apellido, nombre );
this.puesto = puesto;
},
hola: function( $super ) {
$super();
alert( "Usted es " + this.puesto );
}
}
);
var p = new Persona( "brillant", "alexandre" );
p.hola();
var e = new Empleado( "brillant", "alexandre", "desarrollador" );
e.hola();
La clase Empleado hereda de la clase Persona. El constructor también utiliza...