Cadena de prototipos en JavaScript

Es el sistema de herencia que permite a los objetos compartir métodos y propiedades eficientemente. Comprender este mecanismo de búsqueda jerárquica es la llave para dominar el comportamiento del DOM y optimizar el rendimiento de tus aplicaciones.

cadena de prototipos en javascript

1. ¿Qué es exactamente?

En JavaScript, casi todo es un objeto. Cuando intentas acceder a una propiedad o un método de un objeto de JavaScript (por ejemplo, miUsuario.saludar()), el motor de JS hace lo siguiente:

  1. Busca la propiedad dentro del propio objeto.
  2. Si no la encuentra, mira en el prototipo (ver que es prototipo en JS) de ese objeto (su «padre»).
  3. Si sigue sin encontrarla, mira en el prototipo del padre, y así sucesivamente.

Esta «escalera» de conexiones se llama Cadena de Prototipos. La cadena termina siempre en Object.prototype (el abuelo de todos) y, finalmente, en null.

1.1. Ejemplo — Objeto simple:

const persona = {
  nombre: "Manuel"
};

console.log(persona.nombre); // "Manuel"
console.log(persona.toString()); // OJO! toString() no lo declaramos en personaLenguaje del código: JavaScript (javascript)

¿Qué pasa aquí?

  • nombre existe directamente en persona → se encuentra inmediatamente.
  • toString() NO está definido en persona.

Entonces JavaScript hace esto:

  1. ¿Existe toString en persona? ❌
  2. ¿Existe en su prototipo? ✅
  3. Lo encuentra en Object.prototype.

¿Qué es Object.prototype?

Object.prototype es el prototipo base que tienen todos los objetos normales en JS por defecto, y contiene alguno métodos básicos cómo:

  • persona.toString();
  • persona.hasOwnProperty("saludar");
  • persona.valueOf();

VISUALMENTE sería algo así:

persona
   ↓
Object.prototype
   ↓
nullLenguaje del código: JavaScript (javascript)

Ahí está la cadena o (Prototype Chain).

🔹OTRO EJEMPLO BÁSICO:

Imagina que creas un array:

const misNotas = [10, 8, 9];

// ¿De dónde sale .push() si tú no lo escribiste en 'misNotas'?
misNotas.push(7); Lenguaje del código: JavaScript (javascript)

¿Qué pasó ahí?

  1. JS busca .push dentro de misNotas. No existe (allí solo están los números).
  2. Sube por la cadena al prototipo: Array.prototype (que contiene métodos nativos).
  3. ¡Ahí está! JS encuentra el método y lo ejecuta.

1.2. ¿Cómo decide JavaScript dónde buscar esa propiedad?

JavaScript NO “decide” dinámicamente según contexto. Sigue un algoritmo fijo de búsqueda basado en la estructura de la cadena de prototipos.

🔎 El algoritmo real (simplificado)

Cuando hacés:

obj.propLenguaje del código: CSS (css)

El motor hace esto internamente:

  1. Busca la propiedad en el objeto actual.
  2. Si no existe, obtiene el [[Prototype]] del objeto.
  3. Repite el proceso en el prototipo.
  4. Continúa hasta llegar a null.
  5. Si nunca la encuentra → devuelve undefined.

No hay magia.
Es un recorrido lineal hacia arriba.

2. ¿Cómo crear la cadena de prototipo?

Hasta aquí vimos que es la cadena de prototipos y cómo sería su visualización (el ejemplo del punto anterior) y que podemos heredar propiedades de un prototipo padre como Object.prototype.

Ahora bien, Object.prototype es un prototipo que viene por defecto, pero que pasa si queremos crear prototipo manualmente para dicha cadena!!!

Vamos a verlo en los 3 escenarios importantes:

2.1. Con Object.create() (forma explícita y clara)

Este método crea un objeto nuevo y le asigna como prototipo el objeto que le pasamos.

Ejemplo:

const animal = {
  comer() {
    return "Comiendo...";
  }
};

const perro = Object.create(animal);
perro.nombre = "Firulais";

console.log(perro.nombre);  // "Firulais"
console.log(perro.comer()); // "Comiendo..."Lenguaje del código: JavaScript (javascript)

¿Qué pasó internamente?

Cuando hacemos:

const perro = Object.create(animal);Lenguaje del código: JavaScript (javascript)

JavaScript crea algo equivalente a:

perro.[[Prototype]] → animalLenguaje del código: CSS (css)

Entonces la cadena queda:

perro // Que es el último objeto 
   ↓
animal // Ahora pasa a ser prototipo padre de perroObject.prototype // Pasaría a ser prototipo abuelonullLenguaje del código: JavaScript (javascript)

Cuando pedimos perro.comer():

  1. ¿Está en perro? ❌
  2. ¿Está en animal? ✅
  3. Lo ejecuta.

Aquí la cadena la creamos manualmente y de forma directa.

2.2. Con funciones constructoras + new (forma clásica)

Aquí la cadena se crea automáticamente.

Ejemplo

function Usuario(nombre) {
  this.nombre = nombre;
}

Usuario.prototype.saludar = function() {
  return "Hola " + this.nombre;
};

const user1 = new Usuario("Manuel");

console.log(user1.saludar());Lenguaje del código: JavaScript (javascript)

¿Qué hace new internamente?

Cuando ejecutás:

new Usuario("Manuel")Lenguaje del código: JavaScript (javascript)

JavaScript hace 4 cosas invisibles:

  1. Crea un objeto vacío: {}
  2. Enlaza ese objeto a Usuario.prototype
  3. Ejecuta la función con this apuntando al nuevo objeto
  4. Devuelve el objeto

El paso clave es el 2:

user1.[[Prototype]] → Usuario.prototypeLenguaje del código: CSS (css)

La cadena queda:

user1
   ↓
Usuario.prototype
   ↓
Object.prototype
   ↓
nullLenguaje del código: JavaScript (javascript)

Cuando llamamos:

user1.saludar()Lenguaje del código: CSS (css)

La búsqueda funciona así:

  1. ¿Está en user1? ❌
  2. ¿Está en Usuario.prototype? ✅
  3. Se ejecuta.

La cadena se crea automáticamente al usar new.

2.3. Con class (azúcar sintáctico moderno)

Las class usan exactamente el mismo sistema por debajo.

Ejemplo

class Persona {
  constructor(nombre) {
    this.nombre = nombre;
  }

  saludar() {
    return "Hola " + this.nombre;
  }
}

const p1 = new Persona("Ana");
console.log(p1.saludar());Lenguaje del código: JavaScript (javascript)

Internamente es equivalente a:

Persona.prototype.saludar = function() {
  return "Hola " + this.nombre;
};Lenguaje del código: JavaScript (javascript)

La cadena queda:

p1
   ↓
Persona.prototype
   ↓
Object.prototype
   ↓
nullLenguaje del código: JavaScript (javascript)

class no cambia el modelo. Solo lo hace más elegante.

NOTA!! La cadena de prototipos NO se crea cuando agregás métodos. Se crea en el momento en que el objeto es enlazado a su prototipo.

3. ¿Cómo se aplica la cadena de prototipos en el DOM?

En el DOM (Document Object Model), la Cadena de Prototipos es la razón por la cual un simple botón de HTML tiene cientos de métodos disponibles (como .addEventListener(), .appendChild(), o .querySelector()) sin que tú los hayas programado.

En el navegador, los elementos no son solo «etiquetas»; son objetos que forman parte de una jerarquía de herencia profunda.

3.1. La Jerarquía de un Elemento (El «Árbol Genealógico»)

Cuando seleccionas un botón en JS, ese objeto hereda de una cadena específica. Mirá este ejemplo.

Supongamos que seleccionás un botón:

const boton = document.querySelector("button");Lenguaje del código: JavaScript (javascript)

Visualmente, la cadena prototípica sería algo así:

botonHTMLButtonElement.prototypeHTMLElement.prototypeElement.prototypeNode.prototypeEventTarget.prototypeObject.prototypenullLenguaje del código: CSS (css)

¿Qué significa esto?

Cada nivel le agrega capacidades al botón.

  • HTMLButtonElement
    • Métodos y propiedades específicas del botón
    • (Ej: formtype, etc.)
  • HTMLElement
    • Propiedades comunes a elementos HTML
    • (Ej: innerHTMLstyleclassList)
  • Element
    • Métodos generales de elementos
    • (Ej: querySelectorgetAttribute)
  • Node
    • Propiedades estructurales del DOM
    • (Ej: parentNodechildNodes)
  • EventTarget
    • Capacidad de manejar eventos
    • (Ej: addEventListenerremoveEventListener)
  • Object.prototype
    • Métodos básicos de todos los objetos
    • (Ej: toStringhasOwnProperty)

Esta cadena la podríamos visualizar por ejemplo si desde la consola de la herramienta inspeccionar hacemos lo siguiente:

const boton = document.querySelector('button');

// ¿De dónde sale .addEventListener? 
// No está en el botón, está en el prototipo EventTarget.
console.log(boton.__proto__); // HTMLButtonElement
console.log(boton.__proto__.__proto__); // HTMLElement
console.log(boton.__proto__.__proto__.__proto__); // Element
console.log(boton.__proto__.__proto__.__proto__.__proto__); // Node
console.log(boton.__proto__.__proto__.__proto__.__proto__.__proto__); // EventTargetLenguaje del código: JavaScript (javascript)

¿De que nos sirve esto?

A veces, quieres que todos tus elementos del DOM tengan una función nueva.

OJO! Aunque no se recomienda modificar prototipos nativos en proyectos grandes, entenderlo te ayuda a ver el poder de JS:

// Agregamos un "superpoder" al prototipo de todos los Elementos
Element.prototype.desaparecer = function() {
    this.style.display = 'none';
    console.log(`El elemento con ID "${this.id}" se ha ocultado.`);
};
// Fijémonos que aquí modificamos Element.prototype de la cadena

// Ahora cualquier etiqueta puede usarlo:
const titulo = document.querySelector('h1');
titulo.desaparecer(); // ¡Funciona porque h1 hereda de Element!Lenguaje del código: JavaScript (javascript)

Explicación del flujo de búsqueda:

  1. JS busca .desaparecer() en el objeto titulo. No está.
  2. Sube a HTMLHeadingElement. No está.
  3. Sube a HTMLElement. No está.
  4. Sube a Element. ¡Lo encuentra! Lo ejecuta usando this como el elemento actual.

Deja un comentario

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