Herencia prototípica en JavaScript.
La herencia prototípica es el sistema de delegación de objetos que define la esencia de JavaScript. A diferencia de otros lenguajes, aquí los objetos se vinculan directamente entre sí a través de una cadena de prototipos, permitiendo compartir métodos y propiedades de forma eficiente y dinámica sin necesidad de copiar código en memoria.

Índice
1. ¿Que es la herencia prototípica en JavaScript?
La herencia prototípica es el mecanismo por el cual un objeto puede acceder a propiedades y métodos de otro objeto a través de una cadena de prototipos.
En JavaScript:
- No existen clases tradicionales como en Java o C++ (aunque hoy exista la sintaxis
class). - Todo objeto puede heredar directamente de otro objeto.
- Esa herencia ocurre a través de una propiedad interna llamada:
[[Prototype]]Lenguaje del código: JSON / JSON con comentarios (json)
🧠IDEA CLAVE:
Cuando intentás acceder a una propiedad:
obj.propLenguaje del código: CSS (css)
JavaScript hace esto internamente:
- Busca
propdentro deobj. - Si no la encuentra…
- Va al objeto prototipo (
obj.[[Prototype]]). - Si tampoco está ahí…
- Sigue subiendo en la cadena de prototipos.
- Si llega a
null, devuelveundefined.
Eso es la herencia prototípica.
1.1. Ejemplo — Herencia básica con Object.create():
Esta es la forma más pura de ver la herencia
// 1. Definimos un objeto base (el prototipo)
const mentorJS = {
saludar: function() {
console.log("¡Hola, futuro dev! Sigue practicando.");
}
};
// 2. Creamos un nuevo objeto que HEREDA de mentorJS
const estudiante = Object.create(mentorJS);
// 3. Agregamos una propiedad propia al estudiante
estudiante.nombre = "Lucas";
// 4. Probamos la herencia
console.log(estudiante.nombre); // "Lucas" (Propiedad propia)
estudiante.saludar(); // "¡Hola, futuro dev!..." (Propiedad heredada)Lenguaje del código: JavaScript (javascript)
¿Qué pasa cuando ejecutamos estudiante.saludar();?
estudianteno tiene un métodosaludaren su propio cuerpo.- Cuando llamamos a
estudiante.saludar(), el motor de JS busca enestudiante. - Al no encontrarlo, sube por la cadena de prototipos hacia
mentorJSy lo ejecuta desde allí.
En otras palabras:
- ¿
estudiantetienesaludar? ❌ No. - Va a su prototipo (
mentorJS). - ¿
mentorJStiene? ✅ Sí.saludar - Lo ejecuta.
2. Entender el [[Prototype]] de un objeto y cómo se relaciona con __proto__
Estos conceptos son claves para entender a profundidad la herencia prototípica.
2.1. ¿Qué es [[Prototype]]? (El concepto)
Es una propiedad interna (u oculta) que tienen casi todos los objetos en JavaScript. Piensa en ella como un puntero o una dirección que apunta a otro objeto.
- No puedes acceder a ella escribiendo
objeto.[[Prototype]]en tu código (los corchetes dobles indican que es una propiedad interna del motor de JS). - Su única función es servir de guía: si buscas una propiedad en un objeto y no está, JS mira a dónde apunta su
[[Prototype]].
2.2. ¿Qué es __proto__? (La herramienta)
Históricamente, los navegadores necesitaban una forma de que los desarrolladores pudieran ver o cambiar ese [[Prototype]] oculto. Así nació __proto__ (pronunciado «dunder proto»).
- Es un «getter/setter»:
- No es el prototipo en sí, sino una vía de acceso para leer o modificar el
[[Prototype]].
- No es el prototipo en sí, sino una vía de acceso para leer o modificar el
- Estado actual: Aunque casi todos los navegadores lo soportan, hoy se considera una mala práctica usarlo en código de producción. Lo correcto es usar:
Object.getPrototypeOf().Object.setPrototypeOf().
Ejemplo para entender diferencia entre [[Prototype]] y __proto__
Vamos a crear un vínculo de herencia manualmente para ver cómo interactúan:
const programador = {
lenguaje: "JavaScript",
codear: function() {
console.log(`Escribiendo código en ${this.lenguaje}...`);
}
};
const luthierDev = {
nombre: "Tu Nombre",
// Queremos que luthierDev sepa codear como un programador
};
// Vinculamos luthierDev con programador usando __proto__
luthierDev.__proto__ = programador;
// En otras palabras aquí hacemos que programador sea prototipo de luthierDev.
// Sería lo mismo que hicimos en el ejemplo anterior con Object.create(mentorJS); (Recomendado)
// 1. Acceso directo (Existe en el objeto)
console.log(luthierDev.nombre); // "Tu Nombre"
// 2. Acceso por herencia (No existe en luthierDev, busca en su [[Prototype]])
luthierDev.codear(); // "Escribiendo código en JavaScript..."
// 3. Verificación de la relación
console.log(luthierDev.__proto__ === programador); // trueLenguaje del código: JavaScript (javascript)
Explicación del flujo:
- Llamas a
luthierDev.codear(). - JS busca dentro de
luthierDev. No lo encuentra. - JS dice: «A ver, ¿a dónde apunta el
[[Prototype]]de este objeto?». - Gracias a que asignamos
__proto__, el puntero apunta aprogramador. - JS encuentra
codearenprogramadory lo ejecuta.
🚨 ANTERIORMENTE dijimos que actualmente es mejor usarObject.setPrototypeOf(obj, p) y Object.getPrototypeOf(obj).
Pero ¿Qué son exactamente?
2.3. Object.getPrototypeOf(obj)
Este método es el «lector de ADN». Sirve para preguntarle a un objeto: «¿Quién es tu prototipo (tu padre)?».
- Para qué sirve: Para inspeccionar la cadena de herencia de un objeto de forma segura.
- Por qué usarlo: Es más fiable que
__proto__y funciona correctamente incluso en objetos que no tienen un prototipo (objetos creados conObject.create(null)).
2.3.1. Ejemplo:
const animal = { comer: true };
const gato = Object.create(animal);
// Queremos saber de quién hereda 'gato'
const prototipoDeGato = Object.getPrototypeOf(gato);
console.log(prototipoDeGato === animal); // true
console.log(prototipoDeGato.comer); // trueLenguaje del código: JavaScript (javascript)
Explicación: Object.getPrototypeOf(gato) nos devuelve el objeto animal. Es como mirar el mapa de conexiones que JavaScript tiene guardado internamente.
2.4. Object.setPrototypeOf(obj, nuevoProto)
Este método es el «trasplante de ADN». Sirve para cambiar el prototipo de un objeto que ya existe.
- Para qué sirve: Para decirle a un objeto: «A partir de ahora, ya no heredas de ‘A’, ahora heredas de ‘B'».
- Advertencia de profesor: Aunque existe, es un método muy costoso en rendimiento. Los motores de JavaScript (como V8 de Chrome) optimizan los objetos basándose en su estructura; si le cambias el prototipo «en caliente», el motor tiene que re-optimizar todo. Úsalo solo si es estrictamente necesario.
2.4.1. Ejemplo:
const programador = { sabeCodear: true };
const musico = { sabeTocarInstrumento: true };
const persona = { nombre: "Lucas" };
// Inicialmente, persona solo tiene su nombre
console.log(persona.sabeCodear); // undefined
// Cambiamos su "ADN" para que herede de 'programador'
Object.setPrototypeOf(persona, programador);
console.log(persona.sabeCodear); // true
// ¡Cambio de carrera! Ahora que herede de 'musico'
Object.setPrototypeOf(persona, musico);
console.log(persona.sabeCodear); // undefined (ya no hereda de programador)
console.log(persona.sabeTocarInstrumento); // trueLenguaje del código: JavaScript (javascript)
Explicación: Al usar setPrototypeOf, desconectamos a persona de su prototipo anterior y lo conectamos a uno nuevo. Automáticamente, persona gana acceso a todas las propiedades del nuevo objeto.
3. Shadowing y relación con la herencia prototípica:
3.1. ¿Qué es el Shadowing?
El Shadowing (o «Sombreado de propiedades») es un concepto fundamental para entender cómo JavaScript decide qué valor mostrar cuando un objeto y su prototipo tienen una propiedad con el mismo nombre.
En otras palabras:
El shadowing ocurre cuando agregas una propiedad a un objeto que ya existe en su cadena de prototipos. Al hacerlo, la propiedad del objeto «oculta» o «sombra» a la del prototipo. La propiedad del prototipo no desaparece, simplemente queda inaccesible de forma directa a través de ese objeto hijo.
3.2. Ejemplo:
Imagina que tenemos un prototipo base y creamos un objeto a partir de él.
// 1. Definimos el prototipo (El "ADN" base)
const mentor = {
lenguaje: "JavaScript",
saludar: function() {
console.log(`Hola, soy experto en ${this.lenguaje}`);
}
};
// 2. Creamos al estudiante que hereda del mentor
const estudiante = Object.create(mentor);
// 3. Aplicamos SHADOWING
// El estudiante decide tener su propio lenguaje
estudiante.lenguaje = "TypeScript";
// 4. Probamos los resultados
console.log(estudiante.lenguaje); // "TypeScript" (Propiedad propia)
console.log(mentor.lenguaje); // "JavaScript" (El prototipo sigue intacto)
estudiante.saludar(); // "Hola, soy experto en TypeScript"Lenguaje del código: JavaScript (javascript)
Explicación del proceso:
- Búsqueda:
- Cuando pides
estudiante.lenguaje, el motor de JS mira primero en el objetoestudiante.
- Cuando pides
- Encuentro inmediato:
- Como definimos
lenguajedirectamente en el estudiante, JS lo usa y detiene la búsqueda.
- Como definimos
- Sombreado (shadowing):
- La propiedad
lenguajedel objetomentorquedó «en la sombra». JS ya no necesitó subir por la cadena de prototipos para encontrarla.
- La propiedad
- Contexto (
this):- Nota que al llamar a
saludar(), elthisapunta aestudiante, por lo que usa el valor «sombreado» (TypeScript).
- Nota que al llamar a
3. La herencia prototípica en el DOM.
3.1. ¿Qué es la herencia prototípica en el DOM?
Es el mecanismo por el cual los elementos del DOM (como div, p, button, etc.) heredan propiedades y métodos desde objetos más generales a través de una cadena de prototipos.
Un elemento del DOM no tiene todos sus métodos propios, sino que los hereda desde niveles superiores como Element, Node o EventTarget.
Cuando hacés:
const div = document.createElement("div");Lenguaje del código: JavaScript (javascript)
Ese div no es un objeto simple.
Tiene una cadena de prototipos bastante grande.
🔎 Cadena real simplificada:
Un <div> hereda así:
div
↓
HTMLDivElement.prototype
↓
HTMLElement.prototype
↓
Element.prototype
↓
Node.prototype
↓
EventTarget.prototype
↓
Object.prototype
↓
nullLenguaje del código: CSS (css)
Eso significa que div puede usar métodos que están definidos en cualquiera de esos niveles.
3.2. Ejemplo:
const div = document.createElement("div");div.addEventListener("click", () => {
console.log("Click");
});Lenguaje del código: JavaScript (javascript)
Explicación:
El método addEventListener():
- ❌ No está definido directamente en
div - ❌ Tampoco en
HTMLDivElement - ✅ Está definido en
EventTargetde la cadena.
Pero como existe una cadena de prototipos, JavaScript lo encuentra subiendo niveles.
Cuando haces:
div.addEventListener(...)Lenguaje del código: CSS (css)
JavaScript:
- Busca en
div - No lo encuentra
- Sube por la cadena
- Lo encuentra en
EventTarget - Lo ejecuta
Eso es herencia prototípica aplicada al DOM.
🎯 IDEA A TENER EN MENTE:
Los elementos del DOM no copian métodos.
Los heredan mediante delegación en la cadena de prototipos.
