Cadena de prototipos en JavaScript: cómo se buscan propiedades | Nicolás Garzón
La cadena de prototipos es la secuencia de objetos que JavaScript recorre cuando una propiedad no existe directamente en el objeto actual.
JavaScript
Copiar const animal = {
breathe ( ) {
return "Breathing" ;
} ,
} ;
const dog = Object. create ( animal) ;
dog. bark = function ( ) {
return "Woof" ;
} ;
const puppy = Object. create ( dog) ;
puppy. name = "Milo" ; Texto
Copiar puppy → dog → animal → Object.prototype → null
JavaScript
Copiar puppy. breathe ( ) ;
breathe en puppy.
Como no existe, en dog.
Como tampoco existe, en animal.
Allí encuentra la función y detiene la búsqueda.
Si ninguna parte de la cadena contiene la propiedad, el acceso devuelve undefined.
JavaScript
Copiar puppy. fly; JavaScript
Copiar Object. getPrototypeOf ( Object . prototype) ; null indica que ya no existe otro objeto donde buscar.
Un objeto puede comenzar directamente con null.
JavaScript
Copiar const dictionary = Object. create ( null ) ;
Object. getPrototypeOf ( dictionary) ; JavaScript
Copiar const user = {
name : "Nicolás" ,
} ; Texto
Copiar user → Object.prototype → nullPor eso user puede acceder a métodos como:
JavaScript
Copiar user. toString ( ) ;
user. valueOf ( ) ; Aunque no sean propiedades propias.
JavaScript
Copiar const products = [ ] ; Texto
Copiar products
→ Array.prototype
→ Object.prototype
→ nullMétodos como map, filter y push están en Array.prototype.
JavaScript
Copiar Object. hasOwn ( products, "map" ) ;
Array . prototype. hasOwnProperty ( "map" ) ; JavaScript
Copiar function greet ( ) { } Como objeto callable, la función suele tener esta cadena:
Texto
Copiar greet
→ Function.prototype
→ Object.prototype
→ nullJavaScript
Copiar greet . call ( null ) ;
greet . bind ( null ) ; call y bind se encuentran en Function.prototype.
Esto no debe confundirse con greet.prototype, que es otra propiedad utilizada cuando greet actúa como constructor.
JavaScript
Copiar function User ( name ) {
this . name = name;
}
User . prototype. greet = function ( ) {
return ` Hello, ${ this . name} ` ;
} ;
const user = new User ( "Nicolás" ) ; Texto
Copiar user
→ User.prototype
→ Object.prototype
→ nullY la propia función tiene otra cadena:
Texto
Copiar User
→ Function.prototype
→ Object.prototype
→ nullSon recorridos diferentes.
JavaScript
Copiar class User {
greet ( ) {
return ` Hello, ${ this . name} ` ;
}
}
const user = new User ( ) ; Los métodos de instancia se almacenan en User.prototype.
JavaScript
Copiar Object. hasOwn ( user, "greet" ) ;
Object. hasOwn ( User . prototype, "greet" ) ; Texto
Copiar user → User.prototype → Object.prototype → nullLas clases utilizan el mismo sistema prototípico; agregan una sintaxis y reglas más estrictas.
JavaScript
Copiar const base = {
label : "Base" ,
} ;
const child = Object. create ( base) ;
child. label = "Child" ; JavaScript
Copiar child. label; La propiedad propia oculta la heredada.
JavaScript
Copiar delete child. label;
child. label; La propiedad heredada vuelve a ser visible.
JavaScript
Copiar const animal = {
speak ( ) {
return "Sound" ;
} ,
} ;
const dog = Object. create ( animal) ;
dog. speak = function ( ) {
return "Woof" ;
} ; dog.speak() utiliza la versión propia y detiene la búsqueda antes de llegar al prototipo.
En clases, super.method() permite continuar explícitamente con el método del prototipo padre.
JavaScript
Copiar const base = {
status : "active" ,
} ;
const child = Object. create ( base) ;
child. status = undefined ; JavaScript
Copiar child. status; JavaScript encontró una propiedad propia y detuvo la búsqueda. No continúa hacia base solo porque su valor sea undefined.
Eliminarla sí permite continuar:
JavaScript
Copiar delete child. status;
child. status; JavaScript
Copiar "status" in child; in revisa toda la cadena.
JavaScript
Copiar Object. hasOwn ( child, "status" ) ; Solo revisa el objeto actual.
JavaScript
Copiar const base = { role : "viewer" } ;
const user = Object. create ( base) ;
"role" in user;
Object. hasOwn ( user, "role" ) ; JavaScript
Copiar user instanceof User ; De forma simplificada, instanceof pregunta si User.prototype aparece en la cadena de user.
JavaScript
Copiar User . prototype. isPrototypeOf ( user) ; JavaScript
Copiar const sameShape = {
name : "Nicolás" ,
} ;
sameShape instanceof User ; Una función o clase puede definir Symbol.hasInstance.
JavaScript
Copiar class EvenNumber {
static [ Symbol. hasInstance] ( value ) {
return Number. isInteger ( value) && value % 2 === 0 ;
}
}
2 instanceof EvenNumber ; Por eso instanceof puede tener comportamiento personalizado y no siempre debe interpretarse únicamente como una búsqueda convencional.
Objetos creados en otro realm, como un iframe, pueden tener constructores y prototipos distintos.
Un array externo puede fallar:
JavaScript
Copiar externalArray instanceof Array ; Para arrays, la comprobación portable es:
JavaScript
Copiar Array. isArray ( externalArray) ; No todos los tipos tienen una alternativa tan directa. Valida según el contrato real, no únicamente con instanceof.
JavaScript
Copiar Object. setPrototypeOf ( object, prototype) ; Es posible, pero suele ser una mala base de diseño porque:
Cambia el significado del objeto después de crearlo.
Puede invalidar optimizaciones del motor.
Dificulta rastrear dónde vive una propiedad.
Puede afectar código que ya conservaba la referencia.
Prefiere definir la relación al crear:
JavaScript
Copiar Object. create ( prototype) ; O utilizar constructores y clases.
JavaScript
Copiar const first = { } ;
const second = { } ;
Object. setPrototypeOf ( first, second) ;
Object. setPrototypeOf ( second, first) ; Una cadena prototípica debe terminar; no puede formar un ciclo.
Ambas buscan algo, pero buscan cosas distintas.
JavaScript
Copiar const value = 10 ;
function read ( ) {
return value;
} Busca propiedades sobre objetos.
JavaScript
Copiar object. property; No existe un salto entre ambas. Si property no está en el objeto ni en sus prototipos, JavaScript devuelve undefined; no busca una variable exterior con ese nombre.
Una cadena muy profunda puede dificultar entender de dónde viene el comportamiento.
Texto
Copiar instance
→ specializedPrototype
→ domainPrototype
→ basePrototype
→ Object.prototypeEl motor suele optimizar búsquedas comunes, pero el problema principal para el código de aplicación es la complejidad conceptual.
Prefiere jerarquías poco profundas y relaciones que representen claramente el dominio.
En lugar de una cadena larga:
JavaScript
Copiar class AdminManager extends Manager { } Puedes combinar capacidades:
JavaScript
Copiar function createManager ( { canApprove, canAssign } ) {
return {
approve : canApprove,
assign : canAssign,
} ;
} La composición no elimina los prototipos del lenguaje, pero puede evitar jerarquías rígidas en el diseño de la aplicación.
JavaScript
Copiar let current = object;
while ( current !== null ) {
console. log ( current) ;
current = Object. getPrototypeOf ( current) ;
} Las DevTools también permiten inspeccionar propiedades propias y el bloque [[Prototype]].
Para conocer dónde está definida una propiedad:
JavaScript
Copiar function findPropertyOwner ( object, key ) {
let current = object;
while ( current !== null ) {
if ( Object. hasOwn ( current, key) ) {
return current;
}
current = Object. getPrototypeOf ( current) ;
}
return null ;
} JavaScript
Copiar const baseUser = {
readDisplayName ( ) {
return this . name;
} ,
} ;
const staffUser = Object. create ( baseUser) ;
staffUser. canAccessDashboard = true ;
const administrator = Object. create ( staffUser) ;
administrator. name = "Nicolás" ;
administrator. canManageUsers = true ; Texto
Copiar administrator
→ staffUser
→ baseUser
→ Object.prototype
→ nullFunciona, pero una cadena construida con objetos que también guardan datos concretos puede compartir estado accidentalmente. Para modelos reales suelen ser más claras las funciones constructoras, clases o prototipos dedicados exclusivamente a métodos.
Confundir la cadena de una instancia con la cadena de su constructor.
Pensar que undefined hace continuar la búsqueda.
Usar instanceof como comparación de forma.
Ignorar problemas entre realms.
Confundir Function.prototype con la propiedad .prototype de una función.
Crear jerarquías profundas solo para reutilizar código.
Cambiar prototipos durante la ejecución sin necesidad.
Confundir la cadena prototípica con la scope chain.
Suponer que todos los objetos terminan directamente en Object.prototype.
Compartir datos mutables dentro de un prototipo por accidente.
La cadena conecta objetos hasta llegar a null.
JavaScript se detiene en la primera propiedad encontrada.
Un valor propio undefined también detiene la búsqueda.
Arrays heredan de Array.prototype y funciones de Function.prototype.
Instancias creadas con new heredan de Constructor.prototype.
instanceof suele revisar esa relación prototípica.
in incluye propiedades heredadas; Object.hasOwn no.
Scope chain y prototype chain no son lo mismo.
Las cadenas poco profundas suelen ser más fáciles de mantener.
JavaScript
Copiar const base = {
status : "active" ,
} ;
const child = Object. create ( base) ;
child. status = undefined ;
console. log ( child. status) ;
delete child. status;
console. log ( child. status) ; Respuesta Primero imprime undefined, porque la propiedad propia detiene la búsqueda. Después imprime "active", porque al eliminarla JavaScript continúa hacia el prototipo.
Clases presenta una sintaxis estructurada para crear instancias y compartir métodos sobre este mismo sistema de prototipos.