prototype vs __proto__: Diferencias Clave
Entiende la diferencia fundamental entre la propiedad prototype y __proto__, y cuándo usar cada una en JavaScript moderno.
TL;DR - Resumen rápido
- __proto__ es propiedad de instancias, prototype es propiedad de funciones
- __proto__ apunta al prototipo del objeto, prototype define el prototipo de instancias
- Object.getPrototypeOf() es la forma moderna de acceder al prototipo
- Object.setPrototypeOf() es la forma moderna de cambiar prototipos (evitar)
- __proto__ está deprecated, usa Object.getPrototypeOf() en su lugar
- prototype se usa en constructores para definir métodos compartidos
Introducción a prototype vs __proto__
Dos de las propiedades más confusas en JavaScript son __proto__ y prototype. Aunque están relacionadas, tienen propósitos y usos completamente diferentes. Entender la diferencia entre ambas es fundamental para escribir código JavaScript correcto y moderno.
Por qué es importante
La confusión entre estas propiedades puede llevar a errores sutiles en tu código. Conocer la diferencia te ayuda a evitar antipatrones y escribir código más mantenible.
¿Qué es __proto__?
__proto__ es una propiedad que tienen TODOS los objetos en JavaScript (instancias). Esta propiedad apunta al prototipo del objeto, es decir, al objeto del cual hereda propiedades y métodos. Es como un enlace interno que conecta el objeto con su "padre" en la cadena de prototipos.
En este ejemplo, creamos dos objetos: persona y animal. Cada uno tiene su propia propiedad __proto__ que apunta a Object.prototype. Cuando accedemos a persona.__proto__, obtenemos Object.prototype, que es el prototipo compartido de todos los objetos literales.
__proto__ está deprecated
Aunque __proto__ funciona, está oficialmente deprecated. En código moderno, usa Object.getPrototypeOf(obj) para obtener el prototipo de un objeto.
¿Qué es prototype?
prototype es una propiedad especial que tienen SOLO las FUNCIONES (especialmente las funciones constructoras). Esta propiedad define el prototipo que será asignado a todas las instancias creadas con esa función usando el operador new.
En este ejemplo, creamos una función constructora Persona y agregamos métodos a Persona.prototype. Cuando creamos instancias con new Persona(), cada instancia tiene su __proto__ apuntando a Persona.prototype, permitiéndoles acceder a los métodos compartidos. Las instancias NO tienen la propiedad prototype, solo funciones la tienen.
Eficiencia de memoria
Agregar métodos a prototype en lugar del constructor es más eficiente. Todas las instancias comparten los mismos métodos en memoria en lugar de tener copias duplicadas.
Diferencias Clave
Las diferencias entre __proto__ y prototype son fundamentales para entender cómo funciona la herencia en JavaScript. Aunque ambas se relacionan con prototipos, operan en contextos completamente diferentes.
- <strong>__proto__</strong>: Existe en TODAS las instancias (objetos), apunta a su prototipo
- <strong>prototype</strong>: Existe SOLO en FUNCIONES, define el prototipo de instancias futuras
- <strong>__proto__</strong>: Se establece automáticamente al crear un objeto con new
- <strong>prototype</strong>: Se configura manualmente agregando propiedades/métodos
- <strong>Relación</strong>: instancia.__proto__ === Constructor.prototype (siempre true)
- <strong>Uso</strong>: __proto__ para inspección (deprecated), prototype para definir métodos
Este ejemplo muestra las diferencias clave entre __proto__ y prototype. La propiedad __proto__ de la instancia apunta al prototipo, mientras que Persona.prototype define las propiedades que heredan las instancias.
Para entender mejor las diferencias, considera esta comparación directa: __proto__ es como un puntero que tiene cada objeto hacia su "plantilla", mientras que prototype es la "plantilla" misma que las funciones usan para crear objetos. Cuando haces new Persona(), JavaScript automáticamente conecta instancia.__proto__ con Persona.prototype.
Regla simple
Si estás trabajando con una función constructora, usa prototype. Si estás inspeccionando un objeto ya creado, usa Object.getPrototypeOf() (no __proto__).
APIs Modernas
JavaScript moderno proporciona APIs estandarizadas y más seguras para trabajar con prototipos. Estas APIs reemplazan el uso de __proto__, que está deprecated y no debería usarse en código nuevo.
Este ejemplo muestra las APIs modernas para trabajar con prototipos. Object.getPrototypeOf() obtiene el prototipo de un objeto de forma estandarizada. Object.setPrototypeOf() permite cambiar el prototipo, pero tiene implicaciones de rendimiento y debe evitarse. Object.create() es la forma preferida de crear objetos con prototipos específicos.
Object.getPrototypeOf() vs __proto__
Object.getPrototypeOf(obj) es la forma moderna y recomendada de obtener el prototipo de un objeto. Es más explícita, está estandarizada, y funciona consistentemente en todos los entornos. Reemplaza completamente el uso de obj.__proto__.
Object.create() para crear objetos
Object.create(proto) es la forma más eficiente y clara de crear objetos con un prototipo específico. Es mejor que usar Object.setPrototypeOf() porque establece el prototipo en el momento de la creación, lo cual es más rápido y menos propenso a errores.
Object.setPrototypeOf() es lento
Cambiar el prototipo de un objeto existente con Object.setPrototypeOf() tiene un impacto negativo significativo en el rendimiento. Los motores JavaScript no pueden optimizar objetos que cambian de prototipo. Usa Object.create() en su lugar.
¿Cuándo Usar Cada Uno?
Saber cuándo usar cada API es crucial para escribir código JavaScript eficiente y mantenible. La regla general es: usa prototype para definir métodos en constructores, y usa Object.getPrototypeOf() (nunca __proto__) cuando necesites inspeccionar prototipos.
- <strong>Usa prototype</strong>: Para definir métodos compartidos en funciones constructoras
- <strong>Usa Object.getPrototypeOf()</strong>: Para inspeccionar el prototipo de un objeto
- <strong>Usa Object.create()</strong>: Para crear objetos con un prototipo específico
- <strong>Usa class</strong>: Sintaxis moderna preferida para herencia (usa prototype internamente)
- <strong>NUNCA uses __proto__</strong>: Está deprecated, usa las APIs modernas en su lugar
- <strong>EVITA Object.setPrototypeOf()</strong>: Es lento, crea el objeto con el prototipo correcto desde el inicio
Este ejemplo muestra cuándo usar cada propiedad. Usamos prototype para agregar un método compartido a todas las instancias de Persona. Usamos __proto__ para inspeccionar la cadena de prototipos. Usamos Object.create() para crear un objeto con un prototipo diferente.
JavaScript moderno: prefiere class
En código moderno, la sintaxis de class es preferida sobre funciones constructoras. Las clases son más legibles y previenen errores comunes, aunque internamente usan prototype.
Resumen: prototype vs __proto__
Conceptos principales:
- •__proto__ existe en instancias, prototype existe en funciones
- •instancia.__proto__ === Constructor.prototype (relación clave)
- •Object.getPrototypeOf() es la API moderna para acceder a prototipos
- •Object.create() crea objetos con prototipo específico
- •Object.setPrototypeOf() es lento, evitar en producción
- •__proto__ está deprecated, usar APIs modernas
Mejores prácticas:
- •Usa prototype para definir métodos en constructores
- •Usa Object.getPrototypeOf() en lugar de __proto__
- •Usa Object.create() para objetos con prototipos específicos
- •Prefiere class en lugar de funciones constructoras
- •NUNCA uses __proto__ en código nuevo
- •Evita Object.setPrototypeOf() por razones de rendimiento