Inicio Prototipos en JavaScript
Artículo
Cancelar

Prototipos en JavaScript

Introducción

En JavaScript, todos los objetos tienen un prototipo. Un prototipo es también un objeto, y a su vez, tiene su propio prototipo. Este concepto crea una cadena de prototipos, conocida como prototype chain. A través de los prototipos, un objeto puede delegar propiedades y métodos a otros objetos, permitiendo la reutilización de código y la herencia.

(Voluntario) Lee el artículo Herencia y la cadena de prototipos.

(Voluntario) Mira el siguiente vídeo:

La Cadena de Prototipos (Prototype Chain)

Todos los objetos en JavaScript están conectados a un prototipo común llamado Object. Esto permite que los objetos hereden propiedades y métodos definidos en Object.prototype.

1
2
3
4
let homework = {
    topic: "JS"
};
console.log(homework.toString()); // [object Object]

Salida:

1
[object Object]

En el ejemplo anterior, el objeto homework no tiene una propiedad o método toString. Sin embargo, JavaScript lo encuentra en Object.prototype, lo que permite llamar a homework.toString().

Mira el siguiente vídeo:

Vínculo de Objetos (Object Linkage)

Se pueden crear nuevos objetos que hereden de otros objetos utilizando Object.create(). Esto permite crear una cadena de prototipos donde el objeto hijo puede acceder a las propiedades y métodos del objeto padre:

1
2
3
4
5
let homework = {
    topic: "JS"
};
var otherHomework = Object.create(homework);
console.log(otherHomework.topic); // "JS"

Salida:

1
JS

En este ejemplo, otherHomework hereda la propiedad topic de homework a través de la cadena de prototipos.

Cuando hacemos un Object.create(homework) creamos un objeto VACÍO con el [[Prototype]] que le indiquemos como parámetro (homework en este caso).

Prototype en Objetos y Funciones

Las funciones en JavaScript tienen una propiedad llamada prototype, que es un objeto con una propiedad constructor (que apunta a la propia función) y un prototipo que es Object.

1
2
3
4
5
6
7
8
9
10
11
function Apple(type) {
    this.type = type;
    this.color = "red";
}

Apple.prototype.getInfo = function() {
    return this.color + ' ' + this.type + ' apple';
};

let myApple = new Apple("Granny Smith");
console.log(myApple.getInfo()); // "red Granny Smith apple"

Salida:

1
red Granny Smith apple

Cuando se crea un nuevo objeto utilizando new Apple("Granny Smith"), este objeto hereda las propiedades y métodos definidos en Apple.prototype.

La propiedad prototype de las funciones constructoras funciona a modo de plantilla que se copiará en el [[Prototype]] del objeto que se cree al utilizar new.

Los objetos creados con literales o con new NO tienen una propiedad prototype, pero se puede acceder a su prototipo ([[Prototype]]) utilizando Object.getPrototypeOf(objeto).

1
2
let obj = {};
console.log(Object.getPrototypeOf(obj));

Salida:

1
[Object: null prototype] {}

(Voluntario) Mira el siguiente vídeo:

Prototype en Objetos Predefinidos

Es posible extender los prototipos de objetos predefinidos como String, Array, y Object añadiendo métodos adicionales. Esto permite que todos los objetos de ese tipo en la aplicación tengan acceso a los nuevos métodos. Sin embargo, esta práctica puede ser peligrosa en aplicaciones grandes o cuando se utilizan múltiples bibliotecas, ya que puede causar conflictos.

Por ejemplo, se puede añadir un método a Array.prototype:

1
2
3
4
5
6
7
8
9
Array.prototype.forEachLog = function() {
    for (let i of this) {
        console.log(i);
    }
};

let a = [1, 2, 3, 4];
a.forEachLog();
// Output: 1 2 3 4

Salida:

1
2
3
4
1
2
3
4

Aunque esta técnica puede ser útil, también puede llevar a problemas de compatibilidad y mantenimiento en aplicaciones complejas.

Una alternativa más segura es usar Object.defineProperty para definir métodos no enumerables, lo que evita que el método sea iterado en un bucle for…in:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Object.defineProperty(Array.prototype, 'forEachLog', {
    value: function() {
        for (let i of this) {
            console.log(i);
        }
    },
    enumerable: false
});

//Esto asegura que `forEachLog` no aparezca en iteraciones `for...in`:


let a = [1, 2, 3, 4];
a.forEachLog(); // Output: 1 2 3 4

for (let key in a) {
    console.log(key); // Output: 0 1 2 3
}
1
2
3
4
5
6
7
8
1
2
3
4
0
1
2
3

En cualquier caso es una práctica no recomendada y fácilmente sustituible con técnicas de programación funcional.

Bibliografía

Este artículo está licenciado bajo CC BY 4.0 por el autor.

Clases en JavaScript

Clases en JavaScript (ES6)