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.
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.
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.
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.
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.
C'est tout simplement faux. Certains objets ne sont pas créés en appelant new Fune fonction F. 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.
- Chaque instance d'objet a un objet prototype.
- Dans certains cas, ce prototype peut être
null.
- Si vous accédez à un membre sur une instance d'objet et que l'objet n'a pas ce membre, alors l'objet passe à son prototype ou s'arrête si le prototype est nul.
- Le
prototypemembre d'un objet n'est généralement pas le prototype de l'objet.
- Au contraire, le
prototypemembre d'un objet fonction F est l'objet qui deviendra le prototype de l'objet créé par new F().
- Dans certaines implémentations, les instances obtiennent un
__proto__membre qui donne vraiment leur prototype. (Ceci est désormais obsolète. Ne comptez pas dessus.)
- Les objets fonction reçoivent un tout nouvel objet par défaut affecté
prototypelors de leur création.
- Le prototype d'un objet fonction est, bien sûr
Function.prototype.
Résumons.
- Le prototype de
ObjectestFunction.prototype
Object.prototype est l'objet prototype d'objet.
- Le prototype de
Object.prototypeestnull
- Le prototype de
Functionest Function.prototype- c'est l'une des rares situations où Function.prototypeest en fait le prototype de Function!
Function.prototype est l'objet prototype de la fonction.
- Le prototype de
Function.prototypeestObject.prototype
Supposons que nous faisons une fonction Foo.
- Le prototype de
Foois Function.prototype.
Foo.prototype est l'objet prototype Foo.
- Le prototype de
Foo.prototypeis Object.prototype.
Supposons que nous disions new Foo()
- Le prototype du nouvel objet est
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", soit prototype"la prototypeproprié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.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
Qu'est-ce que cette impression?
Eh bien, qu'est-ce que cela instanceofsignifie? honda instanceof Carsignifie "est Car.prototypeégal à n'importe quel objet sur hondala chaîne prototype de?"
Oui, ça l'est. hondale prototype est Car.prototype, donc nous avons terminé. Cela s'imprime vrai.
Et le deuxième?
honda.constructorn'existe pas donc nous consultons le prototype, qui l'est Car.prototype. Lorsque l' Car.prototypeobjet a été créé, il lui a été automatiquement attribué une propriété constructorégale à Car, c'est donc vrai.
Et maintenant?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
Qu'est-ce que ce programme imprime?
Encore une fois, lizard instanceof Reptilesignifie "est Reptile.prototypeégal à n'importe quel objet sur lizardla chaîne prototype de?"
Oui, ça l'est. lizardle prototype est Reptile.prototype, donc nous avons terminé. Cela s'imprime vrai.
Et maintenant
print(lizard.constructor == Reptile);
Vous pourriez penser que cela s'imprime également, car a lizardété construit avec new Reptilemais vous vous trompez. Raisonnez-le.
- A
lizardune constructorpropriété? Non. Par conséquent, nous examinons le prototype.
- Le prototype de
lizardis Reptile.prototype, qui est Animal.
- A
Animalune constructorpropriété? Non. Donc on regarde son prototype.
- Le prototype de
Animalest Object.prototype, et Object.prototype.constructorest créé par le runtime et est égal à Object.
- Donc, cela est faux.
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.
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.prototypeune fonction alors qu'elle était censée être un objet?
Le prototype de fonction est défini comme une fonction qui, lorsqu'elle est appelée, retourne undefined. Nous savons déjà que Function.prototypec'est le Functionprototype, curieusement. C'est donc Function.prototype()légal et quand vous le faites, vous undefinedrevenez. C'est donc une fonction.
Le Objectprototype n'a pas cette propriété; ce n'est pas appelable. Ce n'est qu'un objet.
quand vous console.log(Function.prototype.constructor)c'est encore une fonction.
Function.prototype.constructorest juste Function, évidemment. Et Functionc'est une fonction.
Maintenant, comment pouvez-vous utiliser quelque chose pour le créer vous-même (Mind = blown).
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__, constructoret 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 constructorbords. 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" et myprototypepour signifier "la propriété prototype de".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
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 .mycallablepropriété à chaque objet qui indique s'il peut être appelé ou non. C'est aussi simple que ça.
Function.prototypepeut ê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.prototypeetObject.prototypepourraient simplement être interprétés de manière spéciale par le moteur.