Estoy tratando de entender detrás de las escenas de la cortina de Javascript y estoy atascado en la comprensión de la creación de objetos integrados, especialmente Objeto y Función y la relación entre ellos.
Es complicado, es fácil de entender mal, y muchos libros de JavaScript para principiantes se equivocan, así que no confíes en todo lo que lees.
Fui uno de los implementadores del motor JS de Microsoft en la década de 1990 y en el comité de estandarización, y cometí varios errores al reunir esta respuesta. (Aunque como no he trabajado en esto durante más de 15 años, tal vez pueda ser perdonado). Es algo complicado. Pero una vez que entiendes la herencia del prototipo, todo tiene sentido.
Cuando leí que todos los objetos integrados como Array, String, etc. son extensiones (heredadas) de Object, supuse que Object es el primer objeto incorporado que se crea y el resto de los objetos hereda de él.
Comience desechando todo lo que sabe sobre la herencia basada en clases. JS utiliza herencia basada en prototipos.
Luego, asegúrese de tener una definición muy clara en su cabeza de lo que significa "herencia". Las personas acostumbradas a lenguajes OO como C # o Java o C ++ piensan que la herencia significa subtipo, pero la herencia no significa subtipo. La herencia significa que los miembros de una cosa también son miembros de otra . ¡No significa necesariamente que haya una relación de subtipo entre esas cosas! Tantos malentendidos en la teoría de tipos son el resultado de que las personas no se dan cuenta de que hay una diferencia.
Pero no tiene sentido cuando se llega a saber que los Objetos solo pueden ser creados por funciones, sino que las funciones no son más que objetos de Función.
Esto es simplemente falso. Algunos objetos no se crean llamando new F
a alguna función F
. Algunos objetos son creados por el tiempo de ejecución JS de la nada. Hay huevos que no fueron puestos por ningún pollo . Fueron creados por el tiempo de ejecución cuando se inició.
Digamos cuáles son las reglas y quizás eso ayude.
- Cada instancia de objeto tiene un objeto prototipo.
- En algunos casos ese prototipo puede ser
null
.
- Si accede a un miembro en una instancia de objeto y el objeto no tiene ese miembro, el objeto se desvía a su prototipo o se detiene si el prototipo es nulo.
- El
prototype
miembro de un objeto generalmente no es el prototipo del objeto.
- Más bien, el
prototype
miembro de un objeto de función F es el objeto que se convertirá en el prototipo del objeto creado por new F()
.
- En algunas implementaciones, las instancias obtienen un
__proto__
miembro que realmente da su prototipo. (Esto ahora está en desuso. No confíe en ello).
- Los objetos de función obtienen un nuevo objeto predeterminado asignado
prototype
cuando se crean.
- El prototipo de un objeto de función es, por supuesto
Function.prototype
.
Resumamos
- El prototipo de
Object
esFunction.prototype
Object.prototype
es el objeto prototipo de objeto.
- El prototipo de
Object.prototype
esnull
- El prototipo de
Function
es Function.prototype
: ¡esta es una de las raras situaciones en las Function.prototype
que en realidad es el prototipo de Function
!
Function.prototype
es el objeto prototipo de la función.
- El prototipo de
Function.prototype
esObject.prototype
Supongamos que hacemos una función Foo.
- El prototipo de
Foo
es Function.prototype
.
Foo.prototype
es el prototipo de objeto Foo.
- El prototipo de
Foo.prototype
es Object.prototype
.
Supongamos que decimos new Foo()
- El prototipo del nuevo objeto es
Foo.prototype
Asegúrate de que tenga sentido. Dibujémoslo. Los óvalos son instancias de objeto. Los bordes __proto__
significan "el prototipo de" o prototype
" prototype
propiedad de".
Todo lo que el tiempo de ejecución tiene que hacer es crear todos esos objetos y asignar sus diversas propiedades en consecuencia. Estoy seguro de que puedes ver cómo se haría eso.
Ahora veamos un ejemplo que pone a prueba sus conocimientos.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
¿Qué imprime esto?
Bueno, que instanceof
significa? honda instanceof Car
significa "es Car.prototype
igual a cualquier objeto en honda
la cadena de prototipo?"
Sí lo es. honda
El prototipo es Car.prototype
, así que hemos terminado. Esto imprime cierto.
¿Qué hay del segundo?
honda.constructor
no existe, por lo que consultamos el prototipo, que es Car.prototype
. Cuando Car.prototype
se creó el objeto, se le asignó automáticamente una propiedad constructor
igual a Car
, por lo que esto es cierto.
¿Y qué hay de esto?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
¿Qué imprime este programa?
Nuevamente, lizard instanceof Reptile
significa "¿es Reptile.prototype
igual a cualquier objeto en lizard
la cadena de prototipo?"
Sí lo es. lizard
El prototipo es Reptile.prototype
, así que hemos terminado. Esto imprime cierto.
Ahora, que hay de
print(lizard.constructor == Reptile);
Puede pensar que esto también se imprime como cierto, ya que lizard
fue construido con, new Reptile
pero estaría equivocado. Razonarlo.
- ¿
lizard
Tiene una constructor
propiedad? No. Por lo tanto, miramos el prototipo.
- El prototipo de
lizard
es Reptile.prototype
, que es Animal
.
- ¿
Animal
Tiene una constructor
propiedad? No. Entonces miramos su prototipo.
- El prototipo de
Animal
es Object.prototype
, y Object.prototype.constructor
es creado por el tiempo de ejecución e igual a Object
.
- Entonces esto imprime falso.
Deberíamos haber dicho Reptile.prototype.constructor = Reptile;
en algún momento, ¡pero no nos acordamos!
Asegúrate de que todo tenga sentido para ti. Dibuja algunos cuadros y flechas si todavía es confuso.
La otra cosa extremadamente confusa es si console.log(Function.prototype)
imprimo una función pero cuando imprimo console.log(Object.prototype)
imprime un objeto. ¿Por qué es Function.prototype
una función cuando estaba destinada a ser un objeto?
El prototipo de la función se define como una función que, cuando se llama, regresa undefined
. Ya sabemos que ese Function.prototype
es el Function
prototipo, por extraño que parezca. Por lo tanto, Function.prototype()
es legal, y cuando lo haces, undefined
regresas. Entonces es una función.
El Object
prototipo no tiene esta propiedad; No es invocable. Es solo un objeto.
cuando de console.log(Function.prototype.constructor)
nuevo es una función.
Function.prototype.constructor
es solo Function
, obviamente. Y Function
es una función.
Ahora, ¿cómo puedes usar algo para crearlo tu mismo? (Mente = soplado).
Estás pensando demasiado en esto . Todo lo que se requiere es que el tiempo de ejecución cree un montón de objetos cuando se inicie. Los objetos son solo tablas de búsqueda que asocian cadenas con objetos. Cuando el tiempo de ejecución se pone en marcha, todo lo que tiene que hacer es crear algunos objetos docena de blanco, y luego comenzar a asignar el prototype
, __proto__
, constructor
, y así sucesivamente propiedades de cada objeto hasta que se haga la gráfica que tienen que hacer.
Será útil si tomas el diagrama que te di arriba y le agregas constructor
bordes. Verá rápidamente que este es un gráfico de objetos muy simple y que el tiempo de ejecución no tendrá problemas para crearlo.
Un buen ejercicio sería hacerlo usted mismo. Aquí, te comenzaré. Usaremos my__proto__
para significar "el objeto prototipo de" y myprototype
para significar "la propiedad prototipo de".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
Y así. ¿Puede completar el resto del programa para construir un conjunto de objetos que tenga la misma topología que los objetos incorporados "reales" de Javascript? Si lo hace, encontrará que es extremadamente fácil.
Los objetos en JavaScript son solo tablas de búsqueda que asocian cadenas con otros objetos . ¡Eso es! No hay magia aquí. Te estás haciendo nudos porque estás imaginando restricciones que en realidad no existen, como si cada objeto tuviera que ser creado por un constructor.
Las funciones son solo objetos que tienen una capacidad adicional: ser llamados. Así que revise su pequeño programa de simulación y agregue una .mycallable
propiedad a cada objeto que indique si es invocable o no. Es tan simple como eso.
Function.prototype
puede ser una función y tener campos internos. Entonces no, no ejecutas la función prototipo cuando pasas por su estructura. Por último, recuerde que hay un motor de interpretación de Javascript, por lo Objeto y función se crearon probablemente dentro del motor y no de Javascript y referencia especial comoFunction.prototype
yObject.prototype
sólo podrían ser interpretados de una manera especial por el motor.