Programación Orientada a Objetos PARTE I : Prototipos

Programación Orientada a Objetos PARTE I : Prototipos

En esta série miraremos conceptos como prototipos,clases,constructores, herencia y más aspectos de la programación orientada a objetos.

Introducción

Prototipos

Los prototipos son un mecanismo por el cual los objetos heredan características entre sí en JavaScript.

Los objetos heredan métodos y propiedades a través de un prototipo de objeto o "object prototype" (que a su vez, también es un objeto, como veremos más adelante), por lo que cada vez que creamos un valor primitivo (string,number,boolean...) o una estructura de datos (array,objeto), éstos heredan métodos y propiedades de su objeto prototipo correspondiente.

Prototipos de Objeto y Herencia

Para entender los prototipos de objeto vamos a crear un objeto literal:

const miObjeto = {
  ciudad: "Málaga",

  saludo(){
    console.log(`Hola ${this.ciudad}`)
  }

}

En este objeto hemos declarado dos propiedades: ciudad y saludo, pero si escribimos miObjeto. en la consola, además de estas propiedades,se nos mostrará una serie de otros métodos y propiedades que nosotros no hemos declarado.

objeto prototype.png

¿Qué son estos métodos y propiedades adicionales? ¿De dónde vienen?

Cuando creamos instancias ya sean de valores primitivos o de estructuras de datos, éstas dispondrán de una propiedad "prototype" o "proto"* donde se almacenará todos los métodos y propiedades que se han heredado y que se podrán acceder desde dicha instancia.

Esta propiedad "prototype" es un enlace/referencia entre la instancia del objeto y su prototipo.

Para encontrar de qué prototipo ha heredado miObjeto estos métodos podemos usar el método Object.getPrototypeOf(miObjeto) o acceder a su propiedad .__proto__

__proto__.png

Como podemos ver, miObjeto hereda de un objeto llamado Object.prototype y, éste es un prototipo básico del que todos los objetos heredan por defecto.

*Nota: La propiedad de un objeto que referencia a un prototipo no es llamada "prototype". El nombre no es un estándar, aunque todos los browsers usan __proto__. La manera estándar de acceder a un prototipo de un objeto es con el métodoObject.getPrototypeOf()

Además, conviene señalar que no debemos confundir la propiedad __proto__ o prototype(dado que ésta es solo una referencia al prototipo) de una instancia con el objeto Object.prototype

Prototipos en cadena o anidamiento de prototipos (prototype chain)

Como hemos visto, dado que los prototipos en sí son objetos, también heredan de otros prototipos, formando lo que llamamos cadena de prototipos. Dicha cadena termina cuando un prototipo tiene null como su propio prototipo (como es el caso de Object.prototype).

Cuando intentamos acceder a la propiedad de un objeto, si no se encuentra la propiedad en el objeto en sí, se busca la propiedad en su prototipo. Si la propiedad no se encuentra, se busca en el prototipo del su prototipo, y así hasta que encuentre la propiedad o se termine la cadena, en este caso retornaría undefined.

Por lo que si por ejemplo llamamos a miObjeto.toString(), el browser:

  • Buscará a toString() en miObjeto
  • No lo encuentra, por lo que busca dentro del prototipo de objeto de miObjeto
  • Lo encuentra en este y lo llama.

miObjeto-prototype-chain.png

No todas las instancias heredan directamente de Object.prototype:

const miArray = [1,2,3];

let objeto = miArray;

do {
  objeto = Object.getPrototypeOf(objeto);

  console.log(objeto);

} while (objeto)

//Array []
//Object { … }
//null

Aquí declaramos un objeto miArray (arrays son un tipo especial de objetos en JavaScript), después recorremos la cadena de prototipos y hacemos console.log de ellos. Entonces nos muestra que el prototipo de miArray es Array.prototype, y el prototipo de éste es Object.prototype.

Cuando llamamos un método de array por ejemplo miArray.map(), éste en realidad está definido en Array.prototype.

miArray-prototype-chain.png