Objetos en Javascript (I). Prototipos

Conocer el funcionamiento básico y los fundamentos de los objetos en Javascript es necesario para poder entender cómo se utilizan las librerías que existen para este lenguaje que tanto recorrido está teniendo en los últimos años. Sirva esta pequeña guía para refrescar los conceptos.

En  Javascript, casi todo es un objeto. Todo aquello que no es una cadena de texto, un número, un valor boolean (true o false), null o undefined, es un objeto. Incluso las funciones son objetos. Cuando usamos bibliotecas como jQuery, Underscore o AngularJS podemos intuir que hacemos referencia a objetos que nos brindan multitud de operaciones que facilitan la vida. Si vamos más allá y revisamos el código fuente veremos la particular forma que tienen de declarar los objetos.

Los objetos en Javascript no siguen un paradigma similar al de otros lenguajes basados en programación orientada a objetos como por ejemplo Java, donde los objetos son instancias de una clase bien definida que contiene propiedades (datos) y métodos (operaciones)

En Javascript un objeto puede considerarse más como un conjunto de propiedades, es decir, de pares de identificadores y valores asociados. Se podría ver como una tabla hash por ejemplo o algo parecido a las estructuras (structs) de otros lenguajes de programación tipados.

Una cosa muy interesante de Javascript es que se pueden asignar funciones a variables, de manera que así se crean los métodos que operan sobre estos objetos.

Las propiedades de un objeto además son dinámicas, no sólo en la asignación de los valores, sino en su existencia: se pueden añadir o eliminar propiedades en cualquier momento. Por supuesto, los objetos en Javascript se manejan por referencia.

Para crear un objeto en Javascript simplemente se deben definir sus propiedades (y métodos, si es procedente) utilizando literales, esto es, indicando las propiedades entre llaves:

var obj1 = {prop1:2, prop2;3.14};

//Podemos incluir otra propiedad a posteriori
obj1.prop3=23;

//O también se pueden incluir métodos, gracias a la asignación de funciones anónimas a las propiedades
obj1.suma = function(){return this.prop1 + this.prop2};

//Invocamos a la propiedad, sabiendo que es una función - usando los paréntesis
obj1.suma();

//Es posible llamar de diferentes modos a una propiedad de un objeto
obj1.prop1;
obj1["prop1"];

//Y como siempre, está la notación JSON
obj2 = {"p1":2,"p2":3};
typeof(obj2);

//Se puede eliminar si se desea una propiedad mediante el comando delete:
delete(obj1.p1);

También hay otros métodos de creación, como es el caso del operador new, que crea un nuevo objeto y lo inicializa a partir de la llamada a una función que devuelve un objeto. Esto se parece mucho más al modo de funcionar de Java. Por ejemplo:

Date;    //Devuelve que Date es una función nativa del sistema
var fecha = new Date();  //Crea un objeto de la clase Date()

//Creamos una función que como sabemos, es un objeto en Javascript
function MiObjeto(){return {"prop1":1,"prop2":2}};

//Creamos un objeto con el operador new que llama a la función constructora anterior
var miob = new MiObjeto();

//Comprueba que realmente se trata de un objeto
typeof(miob);

//O simplemente hacemos uso de una función anónima que siempre nos devolverá un objeto nuevo al no poder hacer referencia desde otro lado
var unObj = function(){return {prop1:1}};
unObj instanceof Object  //Devuelve true

Hasta aquí todo sencillo y familiar (salvo quizá la posibilidad de definir los métodos como funciones asignadas a una propiedad). Pero ahora es donde surge la característica especial de Javascript: los Prototipos.

Todo objeto en Javascript tiene un objeto secundario asociado llamado Prototipo, y además hereda las propiedades (atributos y métodos) de él. Los objetos que están definidos de forma explícita con literales (al estilo JSON) tienen como objeto prototipo el objeto Object.prototype. Los objetos que se crean utilizando el operador new reciben el indicado en el constructor. Por ejemplo los creados con new Object reciben Object.prototype, y los creados con new Date, el objeto Date().

De este modo el objeto prototipo dota a los objetos a los que está asociado de funcionalidades más o menos básicas: el Object.prototype por ejemplo tiene el método toString().

Los objetos prototipos además pueden tener a su vez otros prototipos. Por ejemplo el objeto asociado a Date, que es el Date.prototype, a su vez tiene como prototipo Object.prototype, que le da el método toString por ejemplo. Sin embardo, el Object.prototype no tiene ningún otro prototipo asociado.

Es importante tener en la mente que los objetos heredan propiedades (atributos y métodos) de sus prototipos.

¿Cómo se crea un objeto y se especifica su prototipo? Utilizando la función Object.create(), que recibe como parámetro el objeto que queremos usar como prototipo.

//Creamos un objeto con dos atributos y un método que los suma
var obj1 = {"p1":1,"p2":2,"suma":function(){return this.p1+this.p2}}

//Vemos que devuelve el valor 3
obj1.suma();

//Creamos un objeto que tiene como prototipo a obj1 y que por lo tanto hereda sus propiedades (atributos y métodos)
var obj11 = Object.create(obj1);

//Vemos que tiene la función suma y que devuelve 3, puesto que los valores de sus atributos son 1 y 2
obj11.suma();

//Cambiamos el valor de p1
obj11.p1 = 6;

//Vemos que ahora devuelve 8
obj11.suma();

Se puede ver cómo el nuevo objeto obj11 tiene los atributos y operaciones de su prototipo. Si cambiamos las propiedades de obj11 que tienen el mismo nombre que las de obj1, se sobreescriben los valores en obj11, pero no cambian para obj1.

Si creamos un tercer objeto que tenga como prototipo a obj1

var obj12 = Object.create(obj1);

//Podremos ver que la suma sigue siendo 3 ya que obj1 permanece invariable
obj12.suma()

//Y que obj11 sigue valiendo 8
obj11.suma()

//Pero si cambiamos el valor de p2 de obj1, cambiarán los valores de p2 en obj11 y obj12, ya que se basan en estos:
obj1.p2 = 10;

obj11.suma();   //16

obj12.suma();  //10

//Pero si cambiamos el valor de p1 en obj1
obj1.p1 = 0

//El valor de obj11.suma() no varía con este cambio, puesto que antes se ha sobreesrcrito al hacer obj11.p1 = 6; Sigue dando 16:
obj11.suma();

//Siempre se puede comprobar si un objeto es el prototipo de otro mediante una función que viene en Object.prototype llamada isPrototypeOf:
var obj1 = {x:1};

var obj11 = Object.create(obj1);

obj1.isPrototypeOf(obj11);

Si se invoca Object.create() sin usar parámetros, se empleará el prototipo por defecto que es Object.prototype.

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