Prototipos en Javascript

jueves, 3 de julio de 2014

Logo JavaScript

En lenguajes orientados a objetos tenemos herencia basada en clases y en JavaScript tenemos herencia basada en prototipos. De esta forma vamos a poder escribir código más reutilizable y más escalable.

Todos los objetos en JavaScript descienden de Object y tienen un prototipo asociado, todos los objetos que compartan un mismo prototipo van a heredar sus propiedades y métodos.

Vamos a ver algunas de las opciones que tenemos para prototipos según de la forma como creamos un objeto.

Función constructora

Cuando nos creamos un objeto mediante una función constructora, podemos asignar a la función un prototipo y entonces todos los objetos creados con la función constructora usando el operador new van a compartir el mismo tipo de prototipo y por lo tanto van a heredar las propiedades y métodos que contiene el prototipo. Pero incluso si asignamos el mismo tipo de prototipo a funciones constructoras diferentes, los objetos que se creen después van a compartir prototipo como podemos ver en este ejemplo.

function Person(){
 this.fullName = function(){
  return this.name + ' ' + this.lastname;
 }
}

function Employee(name,lastname,dateOfBirth){
 this.name = name;
 this.lastname = lastname;
 this.dateOfBirth = dateOfBirth;
}

function Contact(name,lastname,company){
 this.name = name;
 this.lastname = lastname;
 this.company = company; 
}

Employee.prototype = new Person();
Contact.prototype = new Person();

var xurxo = new Employee("Jorge", "Sánchez", "14/04/1982");
var nikeContact = new Employee("nombre contacto", "apellido contacto", "Nike");

alert(xurxo.fullName());
alert(nikeContact.fullName());

En este caso tenemos una función Person, otra Employee y otra Customer, al asignar como prototipo de Employee y Customer un objeto Person, estas van a compartir el mismo prototipo y heredan la función fullName.

Si nos creamos un objeto de una función constructora antes de asignar a esta un prototipo, este objeto no se vera afectado por este prototipo solo las nuevas instancias de objetos creadas después de modificar el prototipo de la función se ven afectados.

Método Object.create()

Podemos conseguir lo mismo utilizando la función create de Object de esta forma.

function Person(){
 this.fullName = function(){
  return this.name + ' ' + this.lastname;
 }
}

var xurxo = Object.create(new Person(), {
  name:{value:"Jorge"},
  lastname:{value:"Sánchez"},
  dateOfBirth:{value:"14/04/1982"}}); 

var nikeContact = Object.create(new Person(), {
  name:{value:"nombre contacto"},
  lastname:{value:"apellido contacto"},
  dateOfBirth:{value:"Nike"}}); 

alert(xurxo.fullName());
alert(nikeContact.fullName());

Nos estamos creando un objeto con la función create de Object y le indicamos como primer parámetro el prototipo.

Cadena de prototipos

Como ya hemos visto todo objeto en JavaScript tiene un prototipo y este prototipo es el elemento Object por defecto, el cual no tiene prototipo. En el siguiente ejemplo podemos ver como el prototipo de un objeto creado con notación literal es Object.

var xurxo = {name:"jorge"};

El prototipo por defecto de un objeto es object

Si nos creamos el objeto con un prototipo

var xurxoProto = {DateOfBirth:"14/04/1982"};

var xurxo = Object.create(xurxoProto, {name:{value:"Jorge"}})

Se va generando una cadena de prototipos donde siempre termina con el Object por defecto.


De forma que cuando se invoca una propiedad en un objeto  si no se encuentra, va buscando en su cadena de prototipos y si no lo encuentra en toda la cadena de prototipos se produce un error indicando que el objeto no contiene ese método o propiedad.

En lenguajes orientados a objetos en el momento de creación del objeto ya es conocida su cadena de clases base, sin embargo en JavaScript la cadena de prototipos es algo que se puede modificar incluso después de la creación de un objeto

Constructores padre con parámetros

Si la función constructora del objeto padre requiere parámetros, es decir, la función del objeto asignado como prototipo, podemos invocarla desde la función constructora del hijo utilizando la función call,pasando como primer parámetro this para que la propiedad la cree en el objeto hijo, de esta forma se pueden crear varias instancias de employee cada una con el valor que se necesite para la propiedad que se crea en el padre..
function Person(name) {
   this.name=name;
}
 
function Employee(name, dateOfBirth) {
   Person.call(this,name);
   this.dateOfBirth=dateOfBirth;
}
Hijo.prototype = new Padre;
 
a = new Hijo('Jorge','14/04/1982');

alert (a.name);
alert (a.dateOfBirth);

Accediendo al prototipo

Hay que distinguir entre prototipo de función y prototipo de objeto.

El prototipo de función tiene como objetivo ser el objeto que se va ha asignar como prototipo a cada objeto creado invocando a la función con el operador new. A este prototipo se accede utilizando la propiedad prototype.

function Person(name,lastname){
 this.fullName = function(){
  return this.name + ' ' + this.lastname;
 }
}

function Employee(name,lastname,dateOfBirth){
 this.name = name;
 this.lastname = lastname;
 this.dateOfBirth = dateOfBirth;
}

function Contact(name,lastname,company){
 this.name = name;
 this.lastname = lastname;
 this.company = company; 
}

Employee.prototype = new Person();
Contact.prototype = new Person();

El prototipo de objeto es una copia del prototipo de la función a partir de la cual es creado y del que se hereda sus propiedades y métodos. Sin embargo para acceder al prototipo de un objeto no existe la propiedad prototype. Para acceder al prototipo de un objeto existe la propiedad __proto__ pero está actualmente deprecada y se aconseja para acceder al prototipo de un objeto la función Object.getPrototypeOf().

function Employee(name,lastname,dateOfBirth){
 this.name = name;
 this.lastname = lastname;
 this.dateOfBirth = dateOfBirth;
}

var xurxo = new Employee("Jorge", "Sánchez", "14/04/1982");

//Uncaught TypeError: Cannot set property 'text' of undefined 
xurxo.prototype.text = "Lorem Ipsum";
alert(xurxo.prototype.text);

//deprecado
xurxo.__proto__.text = "Lorem Ipsum";
alert(xurxo.__proto__.text);

//función recomendada
Object.getPrototypeOf(xurxo).text = "Lorem Ipsum";
alert(Object.getPrototypeOf(xurxo).text);

Resumen

Hemos repasado en este post los prototipos y hemos visto como podemos utilizarlos para reutilización de código, de la mima forma que la herencia en los lenguajes orientados a objetos.

Libros Relacionados

JavaScript & jQuery: The Missing Manual

JavaScript & JQuery: Interactive Front-End Web Development

Javascript: the Good Parts

No hay comentarios:

Publicar un comentario