Por qué se dice que Javascript es orientado a prototipos. Cómo funciona la orientación a prototipos de Javascript. Cómo cambiar el prototype de los objetos de Javascript para cambiar las propiedades o métodos existentes en los objetos de un tipo.
A veces se dice que Javascript es un lenguaje "orientado a prototipos", en lugar de orientado a objetos. ¿Qué quiere decir? ¿Cómo afecta eso a mi día a día en el desarrollo con Javascript? ¿Qué debo saber sobre los prototipos en Javascript? Todas esas preguntas las vamos a responder en este artículo.
Qué es la programación orientada a objetos
Bueno, para explicar la orientación a prototipos es ideal que conozcas la orientación a objetos tradicional, pues la orientación a prototipos es un pequeño cambio y lo vamos a estudiar en contraste con lo que ya conocemos.
La orientación a objetos es un paradigma de la programación que se basa en la creación de clases que son definiciones de los objetos de un tipo en particular. Para programar defines una "clase" que es como un plano o plantilla o definición, y luego creas objetos basados en esas clases.
Cada objeto se denomina una "instancia" de esa clase. Es decir, es un ejemplar concreto de una clase que tiene un conjunto de propiedades y funcionalidades que se ha definido por medio de una clase. Una clase define las propiedades y métodos que tendrán las instancias o ejemplares creados mediante esa clase. Cada instancia puede tener valores específicos y concretos de las propiedades definidas en la clase.
Como no es nuestro objetivo explicar con detalle este paradigma te vamos a recomendar que obtengas más información por medio del artículo sobre qué es la programación orientada a objetos.
Qué es la orientación a prototipos
Tradicionalmente la orientación a objetos se realiza por clases. Sin embargo, en los lenguajes orientados a prototipos tenemos un modelo de trabajo un poco distinto a la hora de crear los objetos y a la hora de definir cómo unos objetos pueden heredar sus características.
En la orientación a prototipos, en lugar de tener clases, tienes objetos individuales que pueden ser clonados o extendidos para crear nuevos objetos. Los objetos pueden heredar propiedades y métodos de otros objetos. Es como si en lugar de crear casas mediante un plano se creasen mediante la copia de otra casa existente. Este proceso es llamado "cadena de prototipos".
Cómo es la orientación a prototipos en Javascript
En JavaScript, cada objeto tiene un enlace a un objeto prototipo que es esa especie de molde que se usó para crear el objeto. En otras palabras, el prototipo es todo aquello en común que tienen los objetos de un tipo. Cuando se crean objetos de un tipo se hacen en base a un prototipo, el cual permite heredar todas las características de ese tipo de objetos. Pero incluso, cuando nosotros modificamos el prototipo de un objeto, automáticamente modificamos todos los objetos existentes de ese tipo.
En resumen, un "prototipo" es simplemente un objeto del que otros objetos heredarán las propiedades y las funcionalidades al momento de crearse. Pero además, si una vez creados los objetos modificamos el prototipo, los cambios se harán patentes en todos los objetos existentes que se hayan creado en base a ese prototipo. Esto es una característica muy potente.
Como en Javascript existen muchas maneras de crear objetos, podemos encontrar distintas maneras de trabajo que vamos a revisar.
Prototipo con la función constructora
El mecanismo más común para crear objetos en Javascript, y el más tradicional que existe desde el inicio del lenguaje de programación, es mediante lo que llamamos función constructora.
Por ejemplo tenemos esta función que nos permitirá crear objetos Perro que son capaces de ladrar.
function Perro(nombre) {
this.nombre = nombre;
this.ladrar = function() {
console.log('guau guau');
}
}
const miPerro = new Perro('Jade');
miPerro.ladrar();
Ahora podemos ver cómo modificar el prototipo del perro para asignar un nuevo método a esta clase.
Perro.prototype.saludar = function() {
this.ladrar()
console.log(`Soy ${this.nombre}`);
}
Simplemente hemos asignado una nueva propiedad con el valor de una función a Perro.prototype
. A partir de ahora todos los perros son capaces de saludar también. Incluso aunque se trate de perros que ya habían sido instanciados.
miPerro.saludar();
La magia que ha ocurrido aquí consiste simplemente en el comportamiento basado en prototipos de Javascript, que funciona así: Cuando intentas acceder a una propiedad que no está en el objeto actual, el intérprete buscará en la cadena de prototipos hasta que encuentre esa propiedad que se intenta acceder (o se llegue al final de la cadena de prototipos y no se encuentre nada).
Prototipos con un objeto prototipo
Quizás es más fácil todavía de ver cómo funciona el prototipo con un ejemplo de creación de objetos basado en el clonado de otros objetos existentes.
Vamos a tener un objeto común de Javascript que luego usaremos como prototipo para crear otro objeto:
const CochePrototype = {
modelo: 'BMW',
arrancar: function() {
console.log('rum rum');
}
}
const renault = Object.create(CochePrototype);
renault.modelo = 'Renault';
Ahora podemos ajustar el prototipo CochePrototype
para asignar nuevos métodos de esta manera:
CochePrototype.parar = function() {
console.log(`pof pof, tu ${this.modelo} ha parado`);
}
Con ello conseguimos que todos los coches creados mediante este prototipo tengan el método nuevo, incluso si estaban creados antes de haber modificado el prototipo.
renault.arrancar();
renault.parar();
Ejemplo de utilidad de prototipos en Javascript
Quizás no hemos visto que el prototipo sirva para gran cosa, pero sí que hay ejemplos clave que nos pueden ayudar a hacer cosas interesantes en el lenguaje.
Por ejemplo, sabemos que en Javascript las variables de tipo cadena tienen una serie de métodos de utilidad que podemos hacer con ellas. Ahora imaginemos que deseamos ampliar la funcionalidad de Javascript para las cadenas y queremos crear un nuevo método que aplique a todas las cadenas nuevas y todas las variables de cadena que hayan sido creadas ya.
Esto lo podemos ver en este pedazo de código.
let x = "Javascript";
String.prototype.alertar = function() {
alert(this);
}
Ahora la cadena x tendrá un nuevo método llamado "alertar" el cual mostrará la cadena en una caja de diálogo de tipo alert.
x.alertar();
Además, todas las cadenas creadas a partir de ahora tendrán también ese nuevo método.
let y = "Prototipo";
y.alertar();
En este ejemplo hemos hecho uso de la función
alert()
que está solamente disponible en el navegador, por lo que este último ejemplo no funcionará en NodeJS.
Prototipos en las clases de ECMAScript 2015
Como quizás sabrás, en la versión de Javascript ES6 (ECMAScript 2015) se introdujo una nueva sintaxis para crear clases. Si no la conoces puedes estudiarla en el artículo Clases en ES6.
Lo que queremos mencionar es que JavaScript esta utilidad no es más que un "azúcar sintáctico". Por ello, aunque usemos clases con una sintaxis más tradicional para luego crear objetos en base a ellas, no difiere en nada lo que ya sabes sobre los prototipos de Javascript. Es decir, estos objetos seguirán basándose en prototipos.
Por tanto la herencia de propiedades y métodos que te ofrecen los prototipos sigue aplicándose a este tipo de clases. Veamos un ejemplo con este tipo de declaración de clases y sus prototipos.
class Computador {
constructor(marca, precio) {
this.marca = marca;
this.precio = precio;
}
comprar(importe) {
if(importe >= this.precio) {
console.log('Ahora el ordenador es tuyo!!');
} else {
console.log('Uppsss... debes seguir ahorrando');
}
}
}
Ahora creamos un objeto de la clase Computador.
const miOrdenador = new Computador("Toshiba", 300);
Es interesante ver que si inspeccionamos este objeto (o cualquier otro objeto de Javascript) en la consola, podemos ver que se menciona el hecho de tener un prototipo. Quizás lo habías visto más de una vez y no sabías a qué se refería exactamente.
Ahora vamos a crear un nuevo método en el prototipo de los computadores, usando la propiedad prototype
de la clase Computador
.
Computador.prototype.rebajar = function(rebaja) {
this.precio -= rebaja;
}
Simplemente ese método ahora existirá en todos los computadores creados a partir de ahora e incluso en todos los computadores que se hayan creado anteriormente.
miOrdenador.rebajar(100);
miOrdenador.comprar(200);
Conclusión
Hemos conocido una de las características más marcantes del lenguaje Javascript. Este sistema de prototipos permite una gran flexibilidad en la estructura de los objetos y un dinamismo impactante, permitiendo cosas increíbles a la hora de heredar y compartir propiedades y funcionalidades de los objetos.
Sin embargo, también puede ser un punto oscuro y difícil de entender para los programadores que están acostumbrados a la orientación a objetos en lenguajes más tradicionales como Java, C# o PHP.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...