In che modo __proto__ differisce da constructor.prototype?


163
function Gadget(name, color)
{
   this.name = name;
   this.color = color;
}

Gadget.prototype.rating = 3

var newtoy = new Gadget("webcam", "black")

newtoy.constructor.prototype.constructor.prototype.constructor.prototype 

Restituisce sempre l'oggetto con rating = 3.

Ma se faccio quanto segue:

newtoy.__proto__.__proto__.__proto__

La catena finisce per tornare null.

Anche in Internet Explorer come verificherei il null se non ci fosse una __proto__proprietà?


30
Questo schema grafico vi aiuterà a capire la differenza tra il prototipo e il proto . Puoi seguire la catena di proto dall'oggetto newtoy, e poi capirai perché il 3 ° Proto da newtoy è nullo.
bit

Inoltre è chiaro dal diagramma che newtoy.prototypenon è uguale newtoy.constructor.prototypee quindi newtoy.constructor.prototypenon avrà proprietà chiamata rating. Allo stesso modo non newtoy.constructor.prototype.constructor.propertyavrà anche la proprietà chiamata rating.
bit

Errore di battitura nell'ultimo commento: pertanto newtoy.constructor.prototypeavrà proprietà chiamata rating. Allo stesso modo newtoy.constructor.prototype.constructor.propertyavrà anche proprietà chiamata rating.
Bit


1
@Royi Namir Ho caricato jsViz su github. Ecco il sito demo . Per favore, non preoccuparti di quanto non sia mantenuto (e sporco) il codice attuale. Il suo progetto super vecchio che non ho toccato per sempre.
punte l'

Risposte:


210

Ho cercato di avvolgere la mia testa di recente e alla fine ho trovato questa "mappa" che penso getta piena luce sulla questione

http://i.stack.imgur.com/KFzI3.png inserisci qui la descrizione dell'immagine

So di non essere il primo a inventarlo, ma è stato più interessante capire che trovarlo :-). Comunque, dopo quello ho trovato ad esempio questo un altro diagramma che penso dice sostanzialmente lo stesso:

Layout degli oggetti Javascript

La cosa più sorprendente per me è stata scoprire che Object.__proto__punta Function.prototypeinvece Object.prototype, ma sono sicuro che ci sia una buona ragione per farlo :-)

Ho incollato il codice menzionato nell'immagine anche qui se qualcuno vuole testarlo. Nota che alcune proprietà vengono aggiunte agli oggetti per rendere più semplice sapere dove siamo dopo alcuni salti:

Object.O1='';
Object.prototype.Op1='';

Function.F1 = '';
Function.prototype.Fp1 = '';

Cat = function(){};
Cat.C1 = '';
Cat.prototype.Cp1 = '';

mycat = new Cat();
o = {};

// EDITED: using console.dir now instead of console.log
console.dir(mycat);
console.dir(o);

2
@utsaina Molto bello. Scopri un'altra rappresentazione grafica del codice pubblicato da OP. E penso che i nostri diagrammi siano d'accordo in termini di dettagli tecnici.
bit

43
Il motivo per cui Object.__proto__punta Function.prototypeè perché Object()da solo è una funzione nativa che crea un'istanza di un oggetto vuoto. Pertanto, Object()è una funzione. Scoprirai che __proto__puntano tutte le proprietà degli altri principali tipi nativi Function.prototype. Object, Function, String, Number, E Arraytutto ereditano il prototipo di funzione.
Girevolezza

@drodsou il tuo secondo link è fantastico. Dai un'occhiata ora per favore;) mollypages.org/misc/js.mp Bella spiegazione: D
abhisekp

@Swivel "Pertanto, Object () è una funzione" - intendevi dire che Object è una funzione? senza ()
giorgim

2
@GiorgiMoniava Correct. Objectstesso è una funzione; il risultato dell'esecuzione richiamabile Object(cioè, valore di ritorno di esecuzione Object()) non è una funzione.
Parte girevole

67

constructorè una proprietà [[DontEnum]] predefinita definita dall'oggetto puntato dalla prototypeproprietà di un oggetto funzione e inizialmente punta all'oggetto funzione stesso.

__proto__ è equivalente alla proprietà interna [[Prototype]] di un oggetto, cioè al suo prototipo reale.

Quando si crea un oggetto con l' newoperatore, la sua proprietà [[Prototype]] interna verrà impostata sull'oggetto puntato dalla prototypeproprietà della funzione di costruzione .

Ciò significa che .constructorvaluterà .__proto__.constructor, ovvero la funzione di costruzione utilizzata per creare l'oggetto e, come abbiamo appreso, la protoypeproprietà di questa funzione è stata utilizzata per impostare l'oggetto [[Prototype]].

Ne consegue che .constructor.prototype.constructorè identico a .constructor(purché queste proprietà non siano state sovrascritte); vedi qui per una spiegazione più dettagliata.

Se __proto__disponibile, è possibile percorrere l'attuale catena di prototipi dell'oggetto. Non c'è modo di farlo in un semplice ECMAScript3 perché JavaScript non è stato progettato per gerarchie di ereditarietà profonde.


3
Quel link "qui" è il gold standard. Vai lì se vuoi la descrizione completa.
Ricalsin,

Bella cattura con .constructor.prototypeincatenamento. Inoltre non ero chiaro per me, mentre non ho visto che .constructorè uguale .__proto__.constructor. Il che significa semplicemente andare in bicicletta tra la funzione del costruttore e il suo prototipo.
Johnny_D,

30

L'ereditarietà prototipale in JavaScript si basa sulla __proto__proprietà, nel senso che ogni oggetto eredita il contenuto dell'oggetto a cui fa riferimento la sua __proto__proprietà.

La prototypeproprietà è speciale solo per gli Functionoggetti e solo quando si utilizza l' newoperatore per chiamare un Functioncostruttore. In questo caso, l'oggetto creato __proto__verrà impostato su Costruttore Function.prototype.

Ciò significa che l'aggiunta a Function.prototypesi rifletterà automaticamente su tutti gli oggetti a cui __proto__fa riferimento il file Function.prototype.

La sostituzione del costruttore Function.prototypecon un altro oggetto non aggiornerà la __proto__proprietà di nessuno degli oggetti già esistenti.

Si noti che __proto__non è possibile accedere direttamente alla proprietà, utilizzare invece Object.getPrototypeOf (oggetto) .

Per rispondere alla prima domanda, ho creato un diagramma su misura __proto__e prototyperiferimenti, sfortunatamente stackoverflow non mi consente di aggiungere l'immagine con "meno di 10 reputazione". Magari un altra volta.

[Modifica] La figura usa [[Prototype]]invece __proto__che perché è così che la specifica ECMAScript si riferisce agli oggetti interni. Spero che tu possa capire tutto.

Ecco alcuni suggerimenti per aiutarti a capire la figura:

red    = JavaScript Function constructor and its prototype
violet = JavaScript Object constructor and its prototype
green  = user-created objects
         (first created using Object constructor or object literal {},
          second using user-defined constructor function)
blue   = user-defined function and its prototype
         (when you create a function, two objects are created in memory:
          the function and its prototype)

Si noti che la constructorproprietà non esiste negli oggetti creati, ma è ereditata dal prototipo.

inserisci qui la descrizione dell'immagine


@xorcus Per favore, puoi spiegarlo: new MyFunction()crea un'istanza di oggetto che __proto__dovrebbe fare riferimento al suo prototipo ctor che è MyFunction.prototype.Quindi perché si MyFunction.prototype.__proto__riferisce a Object.prototype? dovrebbe riferirsi (come il mio primo campione) al prototipo del suo ctor che è MyFunction.prototype(si noti che MyFunction.prototypeè un'istanza di Myfunction)
Royi Namir,

@Royi Namir: MyFunction.prototype .__ proto__ fa riferimento a Object.prototype perché MyFunction.prototype è un oggetto. Object.prototype è ereditato da tutti gli oggetti (normalmente è qui che termina la catena di prototipi dell'ereditarietà). Non sono d'accordo sul fatto che MyFunction.prototype sia un'istanza di MyFunction. obj instanceof MyFunction <=> MyFunction.prototype.isPrototypeOf (obj) <=> MyFunction.prototype esiste nella catena di prototipi obj. Questo non è il caso dell'oggetto
MyFunction.prototype

14

Objectè Eva, ed Functionè Adam, Adam ( Function) usa il suo bone ( Function.prototype) per creare Eve ( Object). Allora chi ha creato Adam ( Function)? - L'inventore del linguaggio JavaScript :-).

Secondo la risposta di utsaina, voglio aggiungere ulteriori informazioni utili.

La cosa più sorprendente per me è stata scoprire che Object.__proto__ punta Function.prototypeinvece Object.prototype, ma sono sicuro che ci sia una buona ragione per farlo :-)

Non dovrebbe essere. Object.__proto__NON dovrebbe puntare a Object.prototype. Invece, l'istanza di Object o, o.__proto__dovrebbe puntare a Object.prototype.

(Perdonami per aver usato i termini classe instancein JavaScript, ma tu lo sai :-)

Penso che la classe Objectstessa sia un esempio di Function, ecco perché Object.__proto__ === Function.prototype. Pertanto: Objectis Eve, ed Functionè Adam, Adam ( Function) usa il suo bone ( Function.prototype) per creare Eve ( Object).

Inoltre, anche la classe Functionstessa è un'istanza di Functionse stessa, cioè è Function.__proto__ === Function.prototypeanche per questoFunction === Function.constructor

Inoltre, la classe regolare Catè un'istanza di Function, cioè Cat.__proto__ === Function.prototype.

Il motivo di cui sopra è, quando creiamo una classe in JavaScript, in realtà, stiamo solo creando una funzione, che dovrebbe essere un'istanza di Function. Objecte Functionsono solo speciali, ma sono ancora classi, mentre Catè una classe regolare.

Di fatto, nel motore JavaScript di Google Chrome, i seguenti 4:

  • Function.prototype
  • Function.__proto__
  • Object.__proto__
  • Cat.__proto__

Sono tutti ===(assolutamente uguali) per gli altri 3 e il loro valore èfunction Empty() {}

> Function.prototype
  function Empty() {}
> Function.__proto__
  function Empty() {}
> Object.__proto__
  function Empty() {}
> Cat.__proto__
  function Empty() {}
> Function.prototype === Function.__proto__
  true
> Function.__proto__ === Object.__proto__
  true
> Object.__proto__ === Cat.__proto__
  true

OK. Quindi chi crea lo speciale function Empty() {}( Function.prototype)? Pensaci :-)


Concordo con questo, ad eccezione dell'ultima cosa: a cosa function Empty() {}ti riferisci per essere uguale a Function.prototype, ecc?, Qual è il codice che hai usato nella console di Chrome?
Drodsou,

2
Ho corretto l'ultima cosa che hai sottolineato. Il loro valore è function Empty() {}in Google Chrome. Ho anche aggiunto l'output della console.
Peter Lee,

tutte le funzioni sono istanza di Function e, pertanto, tutte le funzioni ereditano ( _ _proto_ _) da Function.prototype. È così semplice :)
xorcus

Ci scusiamo per il commento sul vecchio thread. Ma sono creati da Inventor of Language?
Patel Parth,

6

Non so davvero perché la gente non ti abbia corretto su dove si trova il vero problema nella tua comprensione.

Ciò renderebbe molto più facile individuare il problema

Quindi vediamo cosa sta succedendo:

var newtoy = new Gadget("webcam", "black")

newtoy 
  .constructor //newtoy's constructor function is newtoy ( the function itself)
    .prototype // the function has a prototype property.( all functions has)
      .constructor // constructor here is a **property** (why ? becuase you just did `prototype.constructor`... see the dot ? )  ! it is not(!) the constructor function  !!! this is where your mess begins. it points back to the constructor function itself ( newtoy function)
         .prototype // so again we are at line 3 of this code snippet
            .constructor //same as line 4 ...
                .prototype 
                 rating = 3

Fantastico, quindi ora diamo un'occhiata a questo __proto__

Prima di ciò, ricorda 2 cose riguardanti __proto__ :

  1. Quando crei un oggetto con l' newoperatore, la sua proprietà [[Prototype]]/ interna proto__verrà impostata sulla prototypeproprietà (1) del suo constructor functiono "creatore", se lo desideri.

  2. Hard coded in JS -: Object.prototype.__proto__is null.

Facciamo riferimento a questi 2 punti come " bill"

newtoy
     .__proto__ // When `newtoy` was created , Js put __proto__'s value equal to the value of the cunstructor's prototype value. which is `Gadget.prototype`.
       .__proto__ // Ok so now our starting point is `Gadget.prototype`. so  regarding "bill" who is the constructor function now? watch out !! it's a simple object ! a regular object ! prototype is a regular object!! so who is the constructor function of that object ? Right , it's the `function Object(){...}`.  Ok .( continuing "bill" ) does it has a `prototype` property ? sure. all function has. it's `Object.prototype`. just remember that when Gadget.prototype was created , it's internal `__proto__` was refered to `Object.prototype` becuase as "bill" says :"..will be set to the `prototype` property of   its `constructor function`"
          .__proto__ // Ok so now our satrting point is `Object.prototype`. STOP. read bullet 2.Object.prototype.__proto__ is null by definition. when Object.prototype ( as an object) was created , they SET THE __PROTO__ AS NULL HARDCODED

Meglio?


2

Ogni funzione crea il suo prototipo. E quando creiamo un oggetto usando quel costruttore di funzioni, la proprietà __proto__ del mio oggetto inizierà a puntare al prototipo di quella funzione.


1
Penso che volevi dire la __proto__proprietà.
Demisx,

Sì. Intendevo proto proprietà di un oggetto. Spero che le informazioni siano state utili.
Apoorv Nag,

2

Se tutte quelle figure erano travolgenti, diamo un'occhiata al significato delle proprietà.

STH.prototype

Quando si crea una nuova funzione, viene creato un oggetto vuoto in parallelo e collegato alla funzione con [[Prototype]]catena. Per accedere a questo oggetto, utilizziamo la prototypeproprietà della funzione.

function Gadget() {}
// in background, new object has been created
// we can access it with Gadget.prototype
// it looks somewhat like {constructor: Gadget}

Tenere presente che la prototypeproprietà è disponibile solo per le funzioni.

STH.constructor

L'oggetto prototipo sopra menzionato non ha proprietà se non per uno - constructor. Questa proprietà rappresenta una funzione che ha creato l'oggetto prototipo.

var toy = new Gadget();

Quando Gadgetabbiamo creato una funzione, abbiamo creato anche un oggetto simile {constructor: Gadget}- che non è niente di simile Gadget.prototype. Come constructorriferito a una funzione che ha creato un prototipo di oggetto, toy.constructorrappresenta la Gadgetfunzione. Scriviamo toy.constructor.prototypee stiamo {constructor: Gadget}tornando.

Pertanto, c'è un circolo vizioso: puoi usare toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototypee lo sarà sempre Gadget.prototype.

toy
.constructor    // Gadget
.prototype    // {constructor: Gadget}
.constructor    // Gadget
.prototype    // {constructor: Gadget}
// ...

STH .__ proto__

Mentre prototypeè una proprietà specifica per le funzioni, __proto__è disponibile per tutti gli oggetti mentre si trova Object.prototype. Si riferisce al prototipo di una funzione che può creare un oggetto.

[].__proto__ === Array.prototype
// true

({}).__proto === Object.prototype
// true

Qui, toy.__proto__è Gadget.prototype. Come Gadget.prototypeè un oggetto ( {}) e gli oggetti sono creati con la Objectfunzione (vedi l'esempio sopra), otteniamo Object.prototype. Questo è l'oggetto superiore in JavaScript e __proto__può solo indicare null.

toy
.__proto__    // Gadget.prototype (object looking like {constructor: Gadget})
.__proto__    // Object.prototype (topmost object in JS)
.__proto__    // null - Object.prototype is the end of any chain

0

Risposta breve: __proto__è un riferimento alla prototypeproprietà del costruttore che ha creato l'oggetto.

Oggetti in JavaScript

Un oggetto JavaScript è un tipo incorporato per una raccolta di zero o più proprietà. Le proprietà sono contenitori che contengono altri oggetti, valori primitivi o funzioni.

Costruttori in JavaScript

Le funzioni sono oggetti regolari (che implementano [[Call]]in termini ECMA-262) con l'ulteriore capacità di essere richiamabili ma svolgono un altro ruolo in JavaScript: diventano costruttori ( fabbriche per oggetti) se invocati tramite l' newoperatore. I costruttori sono quindi un analogo approssimativo delle classi in altre lingue.

Ogni funzione JavaScript è in realtà un'istanza Functiondell'oggetto funzione incorporato che ha una proprietà speciale denominata prototypeutilizzata per implementare l'ereditarietà basata su prototipo e le proprietà condivise. Ogni oggetto creato da una funzione di costruzione ha un riferimento implicito (chiamato prototipo o __proto__) al valore del suo costruttore prototype.

Il costruttore prototypeè una sorta di progetto per la costruzione di oggetti poiché ogni oggetto creato dal costruttore eredita un riferimento al suo prototype.

La catena di prototipi

Un oggetto specifica il suo prototipo tramite la proprietà interna [[Prototype]]o __proto__. La relazione prototipo tra due oggetti riguarda l'eredità: ogni oggetto può avere un altro oggetto come prototipo. Il prototipo può essere il nullvalore.

La catena di oggetti collegati dalla __proto__proprietà è chiamata catena prototipo . Quando viene fatto riferimento a una proprietà in un oggetto, tale riferimento è alla proprietà rilevata nel primo oggetto nella catena di prototipi che contiene una proprietà con quel nome. La catena di prototipi si comporta come se fosse un singolo oggetto.

Vedi questa immagine (estratta da questo blog ):

proto.jpg

Ogni volta che si tenta di accedere a una proprietà in un oggetto, JavaScript avvia la ricerca in tale oggetto e continua con il suo prototipo, il prototipo del prototipo e così via fino a quando non viene rilevata la proprietà o se __proto__contiene il valore null.

Questo tipo di eredità che utilizza la catena di prototipi viene spesso chiamata delega per evitare confusione con altri linguaggi che utilizzano la catena di classi.

Quasi tutti gli oggetti sono esempi di Object, perché Object.prototypeè l'ultimo nella loro catena di prototipi. Ma Object.prototypenon è un'istanza di Objectperché Object.prototype.__proto__contiene il valore null.

Puoi anche creare un oggetto con un nullprototipo come questo:

var dict = Object.create(null);

Tale oggetto è una mappa migliore (dizionario) di un oggetto letterale, che è il motivo per cui questo modello è talvolta chiamato il dict modello ( dict per dizionario).

Nota: gli oggetti letterali creati usando {}sono istanze di Objectpoiché ({}).__proto__è un riferimento a Object.prototype.


Per favore, cita la fonte delle citazioni e degli artefatti che stai usando. L'immagine sembra provenire da giamir.com/pseudoclasses-and-prototypal-inheritance-in-JS , hai un copyright?
Bergi,

@Bergi Ho citato la fonte dell'immagine. La maggior parte delle citazioni che ho usato sono o estratte dallo standard JS o dal MDN
eigenslacker
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.