preloader

La Propiedad «prototype» en JavaScript

La Propiedad «prototype»

La propiedad «prototype» es extensamente utilizada por el núcleo de JavaScript. Todas las funciones constructoras incorporadas la emplean.

Primero examinaremos los detalles y luego veremos cómo agregar nuevas capacidades a los objetos incorporados.

Object.prototype

Supongamos que tenemos un objeto vacío y lo mostramos en pantalla:

				
					let obj = {};
alert(obj); // "[object Object]" ?

				
			

¿De dónde proviene la cadena «[object Object]»? Es el método nativo toString, pero ¿dónde se encuentra? ¡El objeto está vacío!

La notación abreviada obj = {} es equivalente a obj = new Object(), donde Object es una función constructora incorporada con su propio prototype, que apunta a un objeto con el método toString y otros métodos.

¿Qué Ocurre con Object.prototype?

Cuando se llama a new Object() (o se crea un objeto literal {...}), el [[Prototype]] se asigna a Object.prototype conforme a la regla discutida anteriormente. Por eso, cuando se invoca obj.toString(), el método se obtiene de Object.prototype.

Podemos verificarlo así:

				
					let obj = {};

console.log(obj.__proto__ === Object.prototype); // true
console.log(obj.toString === obj.__proto__.toString); // true
console.log(obj.toString === Object.prototype.toString); // true

				
			

Tenga en cuenta que no hay más [[Prototype]] en la cadena después de Object.prototype:

				
					console.log(Object.prototype.__proto__); // null

				
			

Otros Prototipos Incorporados

Otros objetos integrados, como Array, Date y Function, también almacenan sus métodos en sus respectivos prototipos.

Por ejemplo, cuando creamos un array [1, 2, 3], se utiliza internamente el constructor predeterminado new Array(). Así, Array.prototype se convierte en su prototipo y proporciona sus métodos. Esto es muy eficiente en términos de memoria.

Según la especificación, todos los prototipos incorporados tienen Object.prototype en su parte superior. Por eso se dice que «todo hereda de los objetos».

Verificando Prototipos

Podemos verificar manualmente los prototipos:

				
					let arr = [1, 2, 3];

// ¿hereda de Array.prototype?
console.log(arr.__proto__ === Array.prototype); // true

// ¿y después de Object.prototype?
console.log(arr.__proto__.__proto__ === Object.prototype); // true

// Y null en el tope.
console.log(arr.__proto__.__proto__.__proto__); // null

				
			

Algunos métodos en los prototipos pueden superponerse. Por ejemplo, Array.prototype tiene su propia versión de toString que enumera los elementos separados por comas:

				
					let arr = [1, 2, 3];
console.log(arr); // 1,2,3 <-- el resultado de Array.prototype.toString

				
			

Herramientas del Navegador

Las herramientas en el navegador, como la consola de desarrollador de Chrome, también muestran la herencia (puede que necesite usar console.dir para los objetos incorporados).

Funciones y sus Prototipos

Las funciones también son objetos creados por el constructor Function incorporado, y sus métodos (call, apply, entre otros) provienen de Function.prototype. Las funciones también tienen su propia versión de toString.

				
					function f() {}

console.log(f.__proto__ === Function.prototype); // true
console.log(f.__proto__.__proto__ === Object.prototype); // true, hereda de Object

				
			

Primitivos

Lo más complicado sucede con las cadenas, números y booleanos.

Como recordamos, no son objetos. Pero si tratamos de acceder a sus propiedades, se crean temporalmente objetos contenedores usando los constructores String, Number y Boolean. Estos objetos proporcionan los métodos y luego desaparecen.

Estos objetos se crean de manera invisible para nosotros y la mayoría de los motores los optimizan, pero la especificación los describe exactamente de esta manera. Los métodos de estos objetos también residen en prototipos disponibles como String.prototype, Number.prototype y Boolean.prototype.

Los valores null y undefined no tienen objetos contenedores ni prototipos asociados, por lo que no tienen métodos ni propiedades disponibles.

Modificando Prototipos Nativos

Los prototipos nativos pueden ser modificados. Por ejemplo, si añadimos un método a String.prototype, estará disponible para todas las cadenas:

				
					String.prototype.show = function() {
  alert(this);
};

"BOOM!".show(); // BOOM!

				
			

Sin embargo, modificar prototipos nativos puede causar conflictos si diferentes bibliotecas intentan añadir el mismo método al mismo prototipo. Por lo tanto, modificar un prototipo nativo se considera generalmente una mala práctica.

Polyfills

En la programación moderna, el único caso en el que se permite modificar prototipos nativos es cuando implementamos un polyfill. Esto ocurre cuando un método existe en la especificación de JavaScript, pero aún no está soportado por algunos motores de JavaScript.

				
					if (!String.prototype.repeat) { // si no hay tal método
  String.prototype.repeat = function(n) {
    return new Array(n + 1).join(this);
  };
}

console.log("La".repeat(3)); // LaLaLa

				
			

Préstamo de Métodos

En el capítulo de Decoradores y Redirecciones, discutimos el préstamo de métodos. Esto ocurre cuando tomamos un método de un objeto y lo utilizamos en otro.

Por ejemplo, si estamos creando un objeto similar a un array, podemos querer copiar algunos métodos de Array.

				
					let obj = {
  0: "Hola",
  1: "mundo!",
  length: 2,
};

obj.join = Array.prototype.join;

console.log(obj.join(',')); // Hola,mundo!

				
			

Esto funciona porque el método join integrado solo se preocupa por los índices y la propiedad length. No verifica si el objeto es realmente un array. Muchos métodos integrados funcionan de esta manera.

Otra opción es heredar estableciendo obj.__proto__ en Array.prototype, de modo que todos los métodos de Array estén disponibles automáticamente en obj. Sin embargo, esto no es posible si obj ya hereda de otro objeto, ya que solo se puede heredar de un objeto a la vez.

Resumen

Todos los objetos incorporados siguen el mismo patrón:

  • Los métodos se almacenan en el prototipo (Array.prototype, Object.prototype, Date.prototype, etc.).
  • El objeto en sí solo almacena los datos (elementos del array, propiedades del objeto, la fecha).
  • Los primitivos también almacenan métodos en prototipos de objetos contenedores: Number.prototype, String.prototype y Boolean.prototype. Solo undefined y null no tienen objetos contenedores.
  • Los prototipos incorporados se pueden modificar o extender con nuevos métodos. Sin embargo, generalmente no se recomienda hacerlo, salvo para implementar un polyfill para un método estándar que aún no esté soportado por todos los motores de JavaScript.

Related Post

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *