Sto cercando di capire dietro le quinte di Javascript e in qualche modo bloccato nella comprensione della creazione di oggetti incorporati, specialmente Oggetto e Funzione e la relazione tra loro.
È complicato, è facile fraintendere e molti libri Javascript per principianti sbagliano, quindi non fidarti di tutto ciò che leggi.
Sono stato uno degli implementatori del motore JS di Microsoft negli anni '90 e nel comitato di standardizzazione e ho commesso numerosi errori nel mettere insieme questa risposta. (Anche se da quando non ci lavoro più da 15 anni posso forse essere perdonato.) È roba difficile. Ma una volta che hai capito l'eredità del prototipo, tutto ha un senso.
Quando ho letto che tutti gli oggetti incorporati come Array, String etc sono estensioni (ereditate) da Object, ho pensato che Object fosse il primo oggetto incorporato che viene creato e il resto degli oggetti eredita da esso.
Inizia buttando via tutto ciò che sai sull'eredità di classe. JS utilizza l'ereditarietà basata sui prototipi.
Quindi, assicurati di avere una definizione molto chiara nella testa di ciò che significa "eredità". Le persone abituate a linguaggi OO come C # o Java o C ++ pensano che l'ereditarietà significhi sottotipizzazione, ma l'ereditarietà non significa sottotipizzazione. Eredità significa che i membri di una cosa sono anche membri di un'altra cosa . Non significa necessariamente che esiste una relazione di sottotitolo tra queste cose! Tanti equivoci nella teoria dei tipi sono il risultato di persone che non si rendono conto che c'è una differenza.
Ma non ha senso quando si arriva a sapere che gli oggetti possono essere creati solo da funzioni, ma anche le funzioni non sono altro che oggetti di funzione.
Questo è semplicemente falso. Alcuni oggetti non vengono creati chiamando new Falcune funzioniF . Alcuni oggetti vengono creati dal runtime JS dal nulla. Ci sono uova che non sono state deposte da alcun pollo . Sono stati appena creati dal runtime all'avvio.
Diciamo quali sono le regole e forse questo aiuterà.
- Ogni istanza di oggetto ha un oggetto prototipo.
- In alcuni casi quel prototipo può essere
null .
- Se si accede a un membro su un'istanza di oggetto e l'oggetto non ha quel membro, l'oggetto passa al suo prototipo o si interrompe se il prototipo è null.
- Il
prototypemembro di un oggetto in genere non lo è il prototipo dell'oggetto.
- Piuttosto, il
prototypemembro di un oggetto funzione F è l'oggetto che diventerà il prototipo dell'oggetto creato danew F() .
- In alcune implementazioni, le istanze ottengono a
__proto__ membro che dà davvero il loro prototipo. (Questo è ora deprecato. Non fare affidamento su di esso.)
- Agli oggetti funzione viene assegnato un nuovissimo oggetto predefinito
prototype momento della loro creazione.
- Il prototipo di un oggetto funzione è, ovviamente
Function.prototype.
Riassumiamo.
- Il prototipo di
ObjectèFunction.prototype
Object.prototype è l'oggetto prototipo oggetto.
- Il prototipo di
Object.prototypeènull
- Il prototipo di
Functionè Function.prototype- questa è una delle rare situazioni in cui Function.prototypeè effettivamente il prototipo di Function!
Function.prototype è l'oggetto prototipo di funzione.
- Il prototipo di
Function.prototypeèObject.prototype
Supponiamo di fare una funzione Foo.
- Il prototipo di
Fooè Function.prototype.
Foo.prototype è l'oggetto prototipo Foo.
- Il prototipo di
Foo.prototypeè Object.prototype.
Supponiamo di dire new Foo()
- Il prototipo del nuovo oggetto è
Foo.prototype
Assicurati che abbia senso. Disegniamolo. Gli ovali sono istanze di oggetti. I bordi __proto__significano "il prototipo di" o prototype" prototypeproprietà di".

Tutto ciò che il runtime deve fare è creare tutti quegli oggetti e assegnare le loro varie proprietà di conseguenza. Sono sicuro che puoi vedere come sarebbe fatto.
Ora diamo un'occhiata a un esempio che mette alla prova le tue conoscenze.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
Cosa stampa questo?
Bene, cosa instanceofsignifica? honda instanceof Carsignifica "è Car.prototypeuguale a qualsiasi oggetto sulla hondacatena di prototipi?"
Sì. hondaIl prototipo è Car.prototype, quindi abbiamo finito. Questo stampa vero.
E la seconda?
honda.constructornon esiste quindi consultiamo il prototipo, che è Car.prototype. Quando l' Car.prototypeoggetto è stato creato, gli è stata automaticamente assegnata una proprietà constructoruguale a Car, quindi questo è vero.
E adesso?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
Cosa stampa questo programma?
Ancora una volta, lizard instanceof Reptilesignifica "è Reptile.prototypeuguale a qualsiasi oggetto sulizard catena di prototipi?"
Sì. lizardIl prototipo èReptile.prototype, quindi abbiamo finito. Questo stampa vero.
Ora, che dire
print(lizard.constructor == Reptile);
Potresti pensare che anche questo sia vero, dato che è lizardstato costruito con new Reptilema ti sbaglieresti. Ragionare fuori.
- Ha
lizardunconstructor proprietà? No. Quindi guardiamo al prototipo.
- Il prototipo di
lizardè Reptile.prototype, che èAnimal .
- Ha
Animalunconstructor proprietà? No. Quindi guardiamo al suo prototipo.
- Il prototipo di
Animalè Object.prototypeed Object.prototype.constructorè creato dal runtime ed è uguale a Object.
- Quindi questo stampa falso.
Avremmo dovuto dirlo Reptile.prototype.constructor = Reptile;ad un certo punto, ma non ci ricordavamo di!
Assicurati che tutto abbia senso per te. Disegna alcune caselle e frecce se è ancora confuso.
L'altra cosa estremamente confusa è, se console.log(Function.prototype)stampo una funzione ma quando stampo console.log(Object.prototype)stampa un oggetto. Perché èFunction.prototype una funzione quando doveva essere un oggetto?
Il prototipo di funzione è definito come una funzione che, quando viene chiamata, ritorna undefined. Sappiamo già che Function.prototypeè il Functionprototipo, stranamente. Quindi Function.prototype()è legale, e quando lo fai, undefinedtorni indietro. Quindi è una funzione.
Il Objectprototipo non ha questa proprietà; non è richiamabile. È solo un oggetto.
quando console.log(Function.prototype.constructor)è di nuovo una funzione.
Function.prototype.constructorè solo Function, ovviamente. Ed Functionè una funzione.
Ora come puoi usare qualcosa per crearlo da solo (Mind = blown).
Ci stai pensando troppo . Tutto ciò che serve è che il runtime crei un mucchio di oggetti all'avvio. Gli oggetti sono solo tabelle di ricerca che associano le stringhe agli oggetti. Quando il runtime si avvia, tutto quello che deve fare è creare alcuni oggetti dozzina vuoti, e quindi avviare assegnare il prototype, __proto__, constructor, e così via proprietà di ogni oggetto fino a fanno il grafico che hanno bisogno di fare.
Sarà utile se prendi quel diagramma che ti ho dato sopra e aggiungi constructor bordi ad esso. Vedrai rapidamente che questo è un grafico a oggetti molto semplice e che il runtime non avrà problemi a crearlo.
Un buon esercizio sarebbe farlo da soli. Ecco, ti inizio. Useremo my__proto__per indicare "l'oggetto prototipo di" e myprototypeper indicare "la proprietà prototipo di".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
E così via. Puoi compilare il resto del programma per costruire un insieme di oggetti con la stessa topologia degli oggetti "reali" incorporati in Javascript? Se lo fai, scoprirai che è estremamente facile.
Gli oggetti in JavaScript sono solo tabelle di ricerca che associano stringhe con altri oggetti . Questo è tutto! Non c'è magia qui. Ti stai annodando perché stai immaginando vincoli che in realtà non esistono, come se ogni oggetto dovesse essere creato da un costruttore.
Le funzioni sono solo oggetti che hanno una capacità aggiuntiva: essere chiamati. Quindi passa attraverso il tuo piccolo programma di simulazione e aggiungi una .mycallableproprietà a ogni oggetto che indica se è richiamabile o meno. E 'così semplice.
Function.prototypepuò essere una funzione e avere campi interni. Quindi no, non esegui la funzione prototipo quando attraversi la sua struttura. Infine, ricorda che esiste un motore che interpreta Javascript, quindi Object e Function sono probabilmente creati all'interno del motore e non da Javascript e riferimenti speciali comeFunction.prototypeeObject.prototypepotrebbero essere interpretati in modo speciale dal motore.