J'essaie de comprendre les coulisses de Javascript et je suis un peu coincé dans la compréhension de la création d'objets intégrés, spécialement Object and Function et la relation entre eux.
Quand j'ai lu que tous les objets intégrés comme Array, String, etc. sont une extension (héritée) d'Object, j'ai supposé que Object était le premier objet intégré créé et le reste des objets en héritait. Mais cela n'a pas de sens lorsque vous apprenez que les objets ne peuvent être créés que par des fonctions, mais alors les fonctions ne sont rien d'autre que des objets de fonction. Cela a commencé à ressembler à un dilemme de poule et de poulet.
L'autre chose extrêmement déroutante est que si console.log(Function.prototype)
j'imprime une fonction, mais lorsque j'imprime, console.log(Object.prototype)
elle imprime un objet. Pourquoi Function.prototype
une fonction alors qu'elle était censée être un objet?
De plus, selon la documentation de Mozilla, chaque javascript function
est une extension d' Function
objet, mais quand vous console.log(Function.prototype.constructor)
le faites à nouveau, c'est une fonction. Maintenant, comment pouvez-vous utiliser quelque chose pour le créer vous-même (Mind = blown).
La dernière chose Function.prototype
est une fonction mais je peux accéder à la constructor
fonction en utilisant Function.prototype.constructor
ce que cela signifie Function.prototype
est une fonction qui renvoie l' prototype
objet
la source
Function.prototype
peut être une fonction et avoir des champs internes. Donc non, vous n'exécutez pas la fonction prototype lorsque vous parcourez sa structure. Enfin, n'oubliez pas qu'il existe un moteur interprétant Javascript, donc l'objet et la fonction sont probablement créés dans le moteur et non à partir de Javascript et de références spéciales commeFunction.prototype
etObject.prototype
pourraient simplement être interprétés de manière spéciale par le moteur.Réponses:
C'est compliqué, il est facile de se méprendre, et un grand nombre de livres Javascript pour débutants se trompent, alors ne faites pas confiance à tout ce que vous lisez.
J'ai été l'un des implémenteurs du moteur JS de Microsoft dans les années 1990 et membre du comité de normalisation, et j'ai fait un certain nombre d'erreurs en préparant cette réponse. (Même si je n'ai pas travaillé là-dessus depuis plus de 15 ans, je peux peut-être être pardonné.) C'est un truc délicat. Mais une fois que vous comprenez l'héritage des prototypes, tout devient logique.
Commencez par jeter tout ce que vous savez sur l'héritage basé sur les classes. JS utilise l'héritage basé sur un prototype.
Ensuite, assurez-vous d'avoir une définition très claire dans votre tête de ce que signifie «héritage». Les gens habitués aux langages OO comme C # ou Java ou C ++ pensent que l'héritage signifie le sous-typage, mais l'héritage ne signifie pas le sous-typage. L'héritage signifie que les membres d'une chose sont également membres d'une autre chose . Cela ne signifie pas nécessairement qu'il existe une relation de sous-typage entre ces choses! Tant de malentendus dans la théorie des types sont le résultat de personnes ne réalisant pas qu'il y a une différence.
C'est tout simplement faux. Certains objets ne sont pas créés en appelant
new F
une fonctionF
. Certains objets sont créés par le runtime JS à partir de rien du tout. Il y a des œufs qui n'ont été pondus par aucun poulet . Ils ont juste été créés par le runtime au démarrage.Disons quelles sont les règles et peut-être que cela aidera.
null
.prototype
membre d'un objet n'est généralement pas le prototype de l'objet.prototype
membre d'un objet fonction F est l'objet qui deviendra le prototype de l'objet créé parnew F()
.__proto__
membre qui donne vraiment leur prototype. (Ceci est désormais obsolète. Ne comptez pas dessus.)prototype
lors de leur création.Function.prototype
.Résumons.
Object
estFunction.prototype
Object.prototype
est l'objet prototype d'objet.Object.prototype
estnull
Function
estFunction.prototype
- c'est l'une des rares situations oùFunction.prototype
est en fait le prototype deFunction
!Function.prototype
est l'objet prototype de la fonction.Function.prototype
estObject.prototype
Supposons que nous faisons une fonction Foo.
Foo
isFunction.prototype
.Foo.prototype
est l'objet prototype Foo.Foo.prototype
isObject.prototype
.Supposons que nous disions
new Foo()
Foo.prototype
Assurez-vous que cela a du sens. Dessinons-le. Les ovales sont des instances d'objets. Les arêtes
__proto__
signifient soit "le prototype de", soitprototype
"laprototype
propriété de".Le runtime n'a qu'à créer tous ces objets et à affecter leurs différentes propriétés en conséquence. Je suis sûr que vous pouvez voir comment cela se ferait.
Voyons maintenant un exemple qui teste vos connaissances.
Qu'est-ce que cette impression?
Eh bien, qu'est-ce que cela
instanceof
signifie?honda instanceof Car
signifie "estCar.prototype
égal à n'importe quel objet surhonda
la chaîne prototype de?"Oui, ça l'est.
honda
le prototype estCar.prototype
, donc nous avons terminé. Cela s'imprime vrai.Et le deuxième?
honda.constructor
n'existe pas donc nous consultons le prototype, qui l'estCar.prototype
. Lorsque l'Car.prototype
objet a été créé, il lui a été automatiquement attribué une propriétéconstructor
égale àCar
, c'est donc vrai.Et maintenant?
Qu'est-ce que ce programme imprime?
Encore une fois,
lizard instanceof Reptile
signifie "estReptile.prototype
égal à n'importe quel objet surlizard
la chaîne prototype de?"Oui, ça l'est.
lizard
le prototype estReptile.prototype
, donc nous avons terminé. Cela s'imprime vrai.Et maintenant
Vous pourriez penser que cela s'imprime également, car a
lizard
été construit avecnew Reptile
mais vous vous trompez. Raisonnez-le.lizard
uneconstructor
propriété? Non. Par conséquent, nous examinons le prototype.lizard
isReptile.prototype
, qui estAnimal
.Animal
uneconstructor
propriété? Non. Donc on regarde son prototype.Animal
estObject.prototype
, etObject.prototype.constructor
est créé par le runtime et est égal àObject
.Nous aurions dû dire
Reptile.prototype.constructor = Reptile;
à un moment donné, mais nous ne nous en souvenions pas!Assurez-vous que tout a du sens pour vous. Dessinez des cases et des flèches si cela prête à confusion.
Le prototype de fonction est défini comme une fonction qui, lorsqu'elle est appelée, retourne
undefined
. Nous savons déjà queFunction.prototype
c'est leFunction
prototype, curieusement. C'est doncFunction.prototype()
légal et quand vous le faites, vousundefined
revenez. C'est donc une fonction.Le
Object
prototype n'a pas cette propriété; ce n'est pas appelable. Ce n'est qu'un objet.Function.prototype.constructor
est justeFunction
, évidemment. EtFunction
c'est une fonction.Vous pensez trop à cela . Tout ce qui est requis, c'est que le runtime crée un tas d'objets au démarrage. Les objets ne sont que des tables de recherche associant des chaînes à des objets. Lorsque l'exécution démarre, tout ce qu'il a à faire est de créer quelques dizaines d' objets en blanc, puis commencer à attribuer le
prototype
,__proto__
,constructor
et ainsi sur les propriétés de chaque objet jusqu'à ce qu'ils fassent le graphique qu'ils doivent faire.Il sera utile de prendre le diagramme que je vous ai donné ci-dessus et d'y ajouter des
constructor
bords. Vous verrez rapidement qu'il s'agit d'un graphe d'objet très simple et que le runtime n'aura aucun problème à le créer.Un bon exercice serait de le faire vous-même. Ici, je vais commencer. Nous utiliserons
my__proto__
pour signifier "l'objet prototype de" etmyprototype
pour signifier "la propriété prototype de".Etc. Pouvez-vous remplir le reste du programme pour construire un ensemble d'objets qui ont la même topologie que les "vrais" objets intégrés Javascript? Si vous le faites, vous constaterez que c'est extrêmement facile.
Les objets en JavaScript ne sont que des tables de recherche qui associent des chaînes à d'autres objets . C'est ça! Il n'y a pas de magie ici. Vous vous faites nouer parce que vous imaginez des contraintes qui n'existent pas réellement, comme si chaque objet devait être créé par un constructeur.
Les fonctions ne sont que des objets qui ont une capacité supplémentaire: être appelés. Parcourez donc votre petit programme de simulation et ajoutez une
.mycallable
propriété à chaque objet qui indique s'il peut être appelé ou non. C'est aussi simple que ça.la source
__proto__
. Le__proto__
prototype de l'objet est nul. Le__proto__
denew X()
estX.prototype
. Tous les objets fonction ont le prototype de fonction à l'__proto__
exception du prototype de fonction lui-même.Object
etFunction
et le prototype de fonction sont des fonctions. Ces règles sont toutes simples et déterminent la topologie du graphe des objets initiaux.Vous avez déjà beaucoup d'excellentes réponses, mais je veux juste donner une réponse courte et claire à votre réponse sur la façon dont tout cela fonctionne, et cette réponse est:
LA MAGIE!!!
Vraiment, c'est tout.
Les personnes qui mettent en œuvre les moteurs d'exécution ECMAScript doivent mettre en œuvre les règles de ECMAScript, mais pas respecter en leur sein de leur mise en œuvre.
La spécification ECMAScript dit que A hérite de B mais B est une instance de A? Aucun problème! Créez d'abord A avec un pointeur prototype de
NULL
, créez B comme une instance de A, puis fixez le pointeur prototype de A pour pointer vers B par la suite. Peasy facile.Vous dites, mais attendez, il n'y a aucun moyen de changer le pointeur prototype dans ECMAScript! Mais voici le problème: ce code ne fonctionne pas sur le moteur ECMAScript, ce code est le moteur ECMAScript. Il ne accès à des objets internes que le code ECMAScript fonctionnement sur le moteur n'a pas. En bref: il peut faire ce qu'il veut.
Soit dit en passant, si vous le voulez vraiment, vous n'avez qu'à le faire une fois: par la suite, vous pouvez par exemple vider votre mémoire interne et charger ce vidage chaque fois que vous démarrez votre moteur ECMAScript.
Notez que tout cela s'applique toujours, même si le moteur ECMAScript lui-même a été écrit en ECMAScript (comme c'est en fait le cas pour Mozilla Narcissus, par exemple). Même dans ce cas, le code ECMAScript qui implémente le moteur a toujours un accès complet au moteur qu'il implémente , bien qu'il n'ait bien sûr pas accès au moteur sur lequel il tourne .
la source
D'après la spécification ECMA 1
Je ne vois pas comment cela pourrait être plus clair !!!
</sarcasm>
Plus bas, nous voyons:
On voit donc qu'un prototype est un objet, mais pas nécessairement un objet fonction.
En outre, nous avons ce titbit intéressant
http://www.ecma-international.org/ecma-262/8.0/index.html#sec-object-objects
et
la source
sarcasm
surnom sinon, ce texte est vraiment assez opaque pour un débutant.Les types suivants englobent chaque valeur dans JavaScript:
boolean
number
undefined
(qui inclut la valeur uniqueundefined
)string
symbol
(«choses» uniques abstraites qui sont comparées par référence)object
Chaque objet (c'est-à-dire tout) en JavaScript a un prototype, qui est une sorte d'objet.
Le prototype contient des fonctions, qui sont aussi une sorte d'objet 1 .
Les objets ont également un constructeur, qui est une fonction, et donc une sorte d'objet.
Tout est récursif, mais l'implémentation est capable de le faire de manière automatique car, contrairement au code JavaScript, il peut créer des objets sans avoir à appeler des fonctions JavaScript (car les objets ne sont que de la mémoire que l'implémentation contrôle).
La plupart des systèmes d'objets dans de nombreux langages typés dynamiquement sont circulaires 2 comme celui-ci. Par exemple, en Python, les classes sont des objets, et la classe des classes l'est
type
, donctype
est donc une instance d'elle-même.La meilleure idée est d'utiliser simplement les outils fournis par la langue et de ne pas trop penser à la façon dont ils y sont arrivés.
1 Les fonctions sont assez spéciales car elles sont appelables et ce sont les seules valeurs pouvant contenir des données opaques (leur corps et éventuellement une fermeture).
2 Il s'agit en fait plutôt d'un ruban tortueux et ramifié plié en arrière sur lui-même, mais "circulaire" est assez proche.
la source