JavaScript: POO — Programación basada en prototipos

Mauricio Garcia
9 min readNov 2, 2019

--

Antes de iniciar quiero agradecer a mi compañero Yhoshua, por ayudarme a que este post haya quedado más completo.

Temario

  • Introducción
  • Cadena de prototipos
  • Objetos (Object Literals, Prototype, Function constructors)
  • Conceptos (Clase, constructor, propiedades, métodos, herencia, encapsulamiento, abstracción, polimorfismo)

i. Introducción

Ahora que entendemos los conceptos de POO, vamos a ver mas a fondo la POO en JavaScript.

JavaScript es diferente a los otros lenguajes, ya que los conceptos como herencia, polimorfismo y encapsulamiento debido a su naturaleza no existen como tal; por ejemplo el encapsulamiento en java se maneja declarando una propiedad o método como private, qué se le conoce como modificadores de acceso, bueno en JS no existen modificadores de acceso, entonces, ¿no hay forma de encapsular en JS? la respuesta es sí, pero no de esa forma, para eso se utilizan las closures (pero ya hablaremos después de ellas, pero si eres impaciente y quieres saber mas [ref]).

En los fundamentos de POO vimos un poco de clases, herencia, etc.; a pesar de que nos referimos a ellas como clases, realmente son objetos, por eso se les conoce como fake class, pero vamos por pasos… primero veamos cómo funciona JS, y para entenderlo es viendo cadena de prototipos.

ii. Cadena de prototipos

Aplicado en la vida real:

Para entender la cadena de prototipos podemos hacer alusión al ADN, sin meternos en detalle sabemos que el ADN almacena información que nos identifica, que ha sido recopilada a lo largo de generaciones, información tomada de nuestros antepasados y que mucha de esa información persiste en nosotros hoy en día. Todos partimos de una base como seres humanos, todos respiramos, comemos, tenemos una estructura ósea, sistemas nerviosos, etc., pero también cada uno de nosotros somos diferentes debido a nuestro ADN y características particulares.

Veamos un ejemplo:

Si nosotros tuviéramos los ojos azules, quizás alguno de nuestros padres también los pueden tener, si no es así quizás nuestros abuelos o algún antepasado más remoto seguro que sí lo tenía, ya que se encontró en algún punto esta característica; entonces, siguiendo este ejemplo entendemos que a nuestro ADN se le va adhiriendo información y si no encuentra un color de ojos en nuestra línea de ancestros directa buscará a la siguiente hasta encontrarlo, el modelo basado en prototipos de JavaScript y la cadena de prototipos funciona de una forma muy parecida al ADN.

Teniendo el ejemplo anterior en mente, veamos cómo aplica en JavaScript; pero antes veamos primero que dice la documentación de MDN:

JavaScript sólo tiene una estructura: objetos. Cada objeto tiene una propiedad privada (referida como su [[Prototype]]) que mantiene un enlace a otro objeto llamado su prototipo. Ese objeto prototipo tiene su propio prototipo, y así sucesivamente hasta que se alcanza un objeto cuyo prototipo es null. Por definición, null no tiene prototipo, y actúa como el enlace final de esta cadena de prototipos. [ref]

Entonces en JavaScript tenemos el objeto global Object y la propiedad Object.prototype representa al objeto prototipo de Object, donde todos los objetos vienen de Object donde heredan las propiedades de Object.prototype, algo así:

Y seguiré diciéndolo hasta el cansancio TODOS los objetos en JavaScript tienen su propio prototype.

Si imprimimos en consola Object.prototype, String.prototype, Array.prototype, podemos observar que tienen sus propios métodos y propiedades:

Los métodos y propiedades de Object.prototype son la base de cualquier objeto en JavaScript.

Un ejemplo cuando creamos un arreglo en JS:

iii. Objetos

Y lo volvemos a mencionar, en JavaScript TODO son objetos

Ahora veamos las formas de crear un objeto:

  • Object Literals
  • Prototype
  • Function constructors

— Objects literals

Crear un objeto literalmente con sus propiedades y este asignarlo a una variable.

Ejemplo:

Supongamos que tenemos objeto llamado person

Podemos observar que solo asignamos a la variable person un objeto {} directamente, donde declaramos propiedades (name y age), así como métodos (getName, getAge, presented), esta es una forma de crear un objeto o fake clase, aunque no es la mejor, ya que al momento de hacer una aplicación compleja tendríamos que ir creando n objetos y no podríamos reutilizar el código, veamos el siguiente ejemplo para entender su límite:

Supongamos que queremos tener dos personas, realmente tendríamos que tener algo así:

Si observamos tendríamos que tener para cada persona el objeto completo, y si quisiéramos cambiar una propiedad o método, ufffff, creo que ha quedado claro que no es lo mejor… por suerte en JavaScript existen formas de hacerlo sin que tengamos que hacer nada raro.

— Prototype

Otra forma de crear objetos (que no soluciona nuestro problema anterior), es mediante el prototipo del mismo, veamos el ejemplo anterior aplicado a Prototype:

Si observamos el ejemplo, vamos a tener el mismo problema que con Object Literals de tener que asignar uno por uno a cada objeto sus propiedades, para este caso lo hacemos directamente a las propiedades al prototipo, otra desventaja que tiene es que cuando el objeto tiene muchos métodos y propiedades es un poco ilegible al momento de actualizarlo o saber que hace.

— Function constructors

Por fortuna existe otra forma (esta sí soluciona nuestro problema anterior) es mediante una función que crea un objeto a partir de un prototipo vacío.

Primero se ha creado una función a través de una Function statement, dentro de esta función estamos utilizando la palabra reservada this (mas adelante veremos a fondo), donde se agregan las propiedades name, age así como el método presented.

El truco está en la palabra reservada new, en palabras de Bruce Burton:

Cuando anteponemos la palabra clave new, JS se encarga primero de crear un objeto vacío, es decir, cuando ejecutamos el operador (función) new, se crea un nuevo objeto el cual va a envolver (Lexical environment) la función que se invoca después de esta palabra y cuando termina de ejecutarse la función en este caso Person(), new, se encarga de devolver el objeto pero con las definiciones que le dimos a this. Si no anteponemos new a nuestra función veremos que nuestras variables valen undefined, por lo tanto si tratase de usar la propiedad name, de dicho objeto mandaría un error. [ref]

La ventaja que tenemos con esto, es que podemos generar n instancias, y con esto aprovechamos la reutilización de código, y ahora sí un cambio en la función afectaría a todos aquellos que estén haciendo la instancia de objeto.

Antes de adentramos a las clases, veamos unos concepto clave…

iv. Conceptos

— Clase

Las clases son una representación de algo en el mundo real, donde se definen propiedades y comportamientos (métodos) que va a tener.

Como lo hemos mencionado anteriormente en JavaScript, no existen como tal las clases (recordemos que esta basado en prototipos y todo son objetos), ahora en ES6 ya existe la palabra reservada class, a pesar de que ya existe esa palabra no es una clase como tal, digamos que es (como a muchos les gusta decirlo) un sustituto de azúcar, visualmente nos va a ayudar a entenderlo como clase, pero por detrás seguirá basado en prototipos, pero pero ahora vamos a enfocarnos en ES5 con Function constructors, así que todos los ejemplos que veamos van a estar orientados a dicho tema.

Entonces veamos un ejemplo con Function constructors:

— Constructor

En POO es el método encargado de crear nuestro objeto e inicializar sus propiedades, en el momento que instanciamos una clase automáticamente se ejecuta su constructor.

Un ejemplo más completo:

— Propiedades

Las propiedades definen el estado de nuestro objeto, en las clases tenemos dos formas de inicializarlas:

  1. Cuando se ejecuta el constructor
  2. Por medio de un método set (previamente declarado)

En esta versión de JavaScript (ES5) no se contaba con modificadores de acceso para indicar que la propiedad es privada.

En el ejemplo anterior declaramos la propiedad privateProperty como privada (no la agregamos al this), al intentar acceder a ella como persona1.privateProperty nos regresa undefined, la solución es crear un método para que nos regrese el valor (de esta forma podemos crear propiedades locales que se convierten en privadas).

— Métodos

Los métodos son muy parecidos a las propiedades, solo que estas son funciones lo cual se encarga de modelar el comportamiento que va a tener nuestro objeto, al igual que las propiedades los métodos se pueden acceder directamente desde cualquier parte.

Hemos generado un método para acceder a la propiedad “privada”.

— Herencia

La herencia nos ayuda a modelar los objetos de una mejor manera, cuando aplicamos herencia de una clase a otra, lo que realmente estamos haciendo es que nuestra clase contenga todos los atributos y métodos de la clase padre.

Uno de los límites que tiene JavaScript, es que sólo permite herencia simple.

Como vimos el ejemplo en los fundamentos de POO, tenemos una clase Person donde contiene propiedades y métodos, ahora como bien sabemos todos los seres humanos somos diferentes (altura, peso, edad, color de piel, color de ojos, etc.) siguiendo el ejemplo de fundamentos, sabemos que un maestro no tiene las mismas obligaciones que un alumno y viceversa, pero lo que sí sabemos es que son personas, la herencia nos ayuda con esta parte ya que los maestros Teacher y los alumnos Alumn heredan todas las propiedades y métodos que tiene persona Person.

En JavaScript no olvidemos que el modelo es prototípico, por lo que herencia también va a ser prototípica.

— Encapsulamiento

Se le llama encapsulamiento cuando la clase principal hereda los métodos de la clase padre, sin necesidad de saber cómo funcionan, sólo tiene que definir las cosas que desea cambiar.

En el ejemplo anterior, Alumn no tiene que saber cómo se aplica el método walk() de la clase Person, pero, sin embargo, puede utilizar ese método. La clase Alumn no tiene que definir explícitamente ese método, a menos que se necesite cambiar.

— Abstracción

Veamos la definición que nos da wikipedia:

La abstracción consiste en aislar un elemento de su contexto o del resto de los elementos que lo acompañan. En programación, el término se refiere al énfasis en el “¿que hace?” más que en el “¿cómo lo hace?”. [ref]

Se refiere a poder tomar un objeto de la realidad y poderlo representar mediante una clase, el poder tomar ese objeto y el abstraer su comportamiento y sus propiedades para programáticamente poder crearlo y modificarlo.

— Polimorfismo

El polimorfismo: En la POO es la capacidad de crear una propiedad, método o un objeto que tenga más de una forma.

Pero en JavaScript realmente no tenemos interfaces como en otros lenguajes de programación, pero tenemos superclases y subclases, para entenderlo veamos el siguiente ejemplo:

Tenemos la clase Person, donde agregamos el método walk, y además hemos creado la clase Alumn y también le hemos agregado el método walk.

Cuando hacemos la instancia de objeto de la clase Person y la clase Alumn, podemos ver que hemos sobrescrito el método walk en la clase Alumn, eso es exactamente polimorfismo.

En la siguiente entrega vamos a ver POO —Tipos de valores

La entrega pasada vimos POO — Programación Orientado a Objetos — Fundamentos

--

--

Responses (1)