Herencia Prototípica con Funciones Constructoras
Introducción a F.prototype
En JavaScript, podemos crear nuevos objetos utilizando una función constructora con el operador new
. Si F.prototype
es un objeto, el operador new
lo utilizará para establecer el [[Prototype]] del nuevo objeto creado.
Es importante notar que JavaScript ha tenido herencia prototípica desde su inicio. Originalmente, la única forma confiable de manejar esto era a través de una propiedad «prototype» en las funciones constructoras, lo que explica por qué muchos scripts antiguos todavía la utilizan. Aquí, F.prototype
se refiere a una propiedad regular llamada «prototype» dentro de F
.
Ejemplo Básico
Consideremos el siguiente ejemplo:
let animal = {
come: true
};
function Conejo(nombre) {
this.nombre = nombre;
}
Conejo.prototype = animal;
let conejo = new Conejo("Conejo Blanco"); // conejo.__proto__ == animal
alert(conejo.come); // true
Al establecer Conejo.prototype = animal
, decimos: «Cuando se cree un nuevo Conejo
, asigna animal
a su [[Prototype]]». Aquí es cómo se ve:
Conejo.prototype
es una propiedad regular que apunta aanimal
.conejo
hereda deanimal
a través de [[Prototype]].
Uso de F.prototype en el Momento de Creación
La propiedad F.prototype
se usa únicamente durante la creación de un objeto nuevo con new F()
. Si cambiamos F.prototype
después de la creación del objeto, los nuevos objetos creados tendrán el nuevo prototipo, pero los ya existentes mantendrán el antiguo.
Prototipo Predeterminado y la Propiedad Constructor
Cada función tiene una propiedad prototype
por defecto. Este prototipo predeterminado es un objeto que contiene una única propiedad constructor
, que apunta de nuevo a la función misma:
function Conejo() {}
/* prototipo por defecto
Conejo.prototype = { constructor: Conejo };
*/
console.log(Conejo.prototype.constructor === Conejo); // true
De este modo, los objetos creados con new Conejo
heredarán de { constructor: Conejo }
:
function Conejo() {}
let conejo = new Conejo(); // hereda de {constructor: Conejo}
console.log(conejo.constructor === Conejo); // true
Uso del Constructor para Crear Nuevos Objetos
Podemos utilizar la propiedad constructor
para crear un nuevo objeto utilizando el mismo constructor:
function Conejo(nombre) {
this.nombre = nombre;
console.log(nombre);
}
let conejo1 = new Conejo("Conejo Blanco");
let conejo2 = new conejo1.constructor("Conejo Negro");
Este enfoque es útil cuando trabajamos con objetos de bibliotecas externas y necesitamos crear nuevos objetos del mismo tipo sin conocer el constructor específico.
Problemas con el Constructor
JavaScript no garantiza siempre el valor correcto de constructor
. Si reemplazamos completamente el prototipo predeterminado, la propiedad constructor
puede perderse:
function Conejo() {}
Conejo.prototype = {
salta: true
};
let conejo = new Conejo();
console.log(conejo.constructor === Conejo); // false
Para evitar esto, podemos agregar o modificar propiedades en el prototipo predeterminado en lugar de reemplazarlo por completo, o reasignar manualmente la propiedad constructor
:
function Conejo() {}
Conejo.prototype.salta = true;
// Conejo.prototype.constructor se conserva
// O reasignar manualmente
Conejo.prototype = {
salta: true,
constructor: Conejo
};
Resumen
En este capítulo, hemos explorado cómo establecer [[Prototype]] para objetos creados mediante funciones constructoras. Aquí hay algunos puntos clave:
F.prototype
(no confundir con [[Prototype]]) define el [[Prototype]] de nuevos objetos cuando se utilizanew F()
.- El valor de
F.prototype
debe ser un objeto onull
. Otros valores no funcionarán. - La propiedad
prototype
tiene un efecto especial solo cuando se trata de una función constructora y se invoca connew
. - En objetos normales,
prototype
es simplemente una propiedad común sin efectos especiales:
let usuario = {
nombre: "John",
prototype: "Bla-bla" // sin magia en absoluto
};
- Todas las funciones tienen
F.prototype = { constructor: F }
por defecto, lo que nos permite obtener el constructor de un objeto a través de su propiedadconstructor
.
Con esta base, estamos preparados para explorar patrones de programación más avanzados que aprovechan la herencia prototípica.