Come funziona JavaScript .prototype?


2041

Non mi piacciono i linguaggi di programmazione dinamici ma ho scritto la mia giusta dose di codice JavaScript. Non ho mai avuto la testa su questa programmazione basata su prototipi, qualcuno sa come funziona?

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Ricordo molte discussioni che ho avuto con le persone qualche tempo fa (non sono esattamente sicuro di quello che sto facendo) ma, come ho capito, non esiste un concetto di classe. È solo un oggetto e le istanze di quegli oggetti sono cloni dell'originale, giusto?

Ma qual è lo scopo esatto di questa proprietà ".prototype" in JavaScript? Come si collega all'istanza di oggetti?

Aggiornamento: modo corretto

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

Anche queste diapositive hanno davvero aiutato molto.


78
John Resig ha alcune diapositive sui prototipi di funzioni che mi sono state utili quando ho esaminato l'argomento (puoi anche apportare modifiche al codice e vedere cosa succede ...) http://ejohn.org/apps/learn/#64
John Foster,

5
Ottimo materiale di riferimento, al fine di mantenere questa domanda informativa, magari inserire alcuni dei commenti del sito di John sulla tua risposta nel caso in cui il suo sito sia modificato in modo tale che il tuo link non sia più disponibile. Ad ogni modo +1, mi ha aiutato.
Chris,

95
+1 per il tuo link alla diapositiva Ninja JavaScript # 64 di John Resig . Partire da lì è stato davvero utile e mi sembra di capire correttamente i prototipi.
un secchione pagato il

4
Abbiamo davvero bisogno di un oggetto funzionale per l'applicazione del prototipo? se sì, perché?
Anshul

6
Questo potrebbe esserti utile: webdeveasy.com/javascript-prototype
Naor

Risposte:


1007

Ogni oggetto JavaScript ha uno "slot" interno chiamato il [[Prototype]]cui valore è uno nullo uno object. Puoi pensare a uno slot come a una proprietà di un oggetto, interna al motore JavaScript, nascosta dal codice che scrivi. Le parentesi quadre intorno [[Prototype]]sono intenzionali e sono una convenzione di specifica ECMAScript per indicare gli slot interni.

Il valore indicato da [[Prototype]]un oggetto è colloquialmente noto come "il prototipo di quell'oggetto".

Se si accede a una proprietà tramite la notazione dot ( obj.propName) o parentesi ( obj['propName']) e l'oggetto non ha direttamente tale proprietà (cioè una propria proprietà , controllabile tramite obj.hasOwnProperty('propName')), il runtime cerca una proprietà con quel nome sull'oggetto a cui si fa riferimento dai [[Prototype]]invece. Se [[Prototype]] anche non dispone di tale proprietà, [[Prototype]]viene controllato a sua volta e così via. In questo modo, la catena prototipo dell'oggetto originale viene percorsa fino a quando non viene trovata una corrispondenza o viene raggiunta la fine. Nella parte superiore della catena prototipo è il nullvalore.

Le moderne implementazioni JavaScript consentono l'accesso in lettura e / o scrittura ai [[Prototype]]seguenti modi:

  1. L' newoperatore (configura la catena di prototipi sull'oggetto predefinito restituito da una funzione di costruzione),
  2. Il extends parola chiave (configura la catena di prototipi quando si utilizza la sintassi della classe),
  3. Object.create imposterà l'argomento fornito come [[Prototype]] dell'oggetto risultante,
  4. Object.getPrototypeOfe Object.setPrototypeOf(ottieni / imposta il [[Prototype]] dopo creazione dell'oggetto ), e
  5. La proprietà accessor standardizzata (es. Getter / setter) denominata __proto__(simile a 4.)

Object.getPrototypeOfe Object.setPrototypeOfsono preferiti rispetto __proto__, in parte perché il comportamento di o.__proto__ è insolito quando un oggetto ha un prototipo dinull .

Di un oggetto [[Prototype]] viene inizialmente impostato durante la creazione dell'oggetto.

Se si crea un nuovo oggetto tramite new Func(), [[Prototype]]per impostazione predefinita l'oggetto verrà impostato sull'oggetto a cui fa riferimento Func.prototype.

Pertanto, tutte le classi e tutte le funzioni che possono essere utilizzate con l' newoperatore hanno una proprietà denominata .prototypeoltre al proprio [[Prototype]]slot interno. Questo duplice uso della parola "prototipo" è fonte di infinita confusione tra i nuovi arrivati ​​nel linguaggio.

L'uso newcon le funzioni di costruzione ci consente di simulare l'eredità classica in JavaScript; sebbene il sistema di ereditarietà di JavaScript sia - come abbiamo visto - prototipico e non basato su classi.

Prima dell'introduzione della sintassi delle classi in JavaScript, le funzioni di costruzione erano l'unico modo per simulare le classi. Possiamo pensare alle proprietà dell'oggetto a cui fa riferimento la .prototypeproprietà della funzione di costruzione come membri condivisi; vale a dire. membri uguali per ogni istanza. Nei sistemi basati su classi, i metodi sono implementati allo stesso modo per ogni istanza, quindi i metodi vengono concettualmente aggiunti a.prototype proprietà; i campi di un oggetto, tuttavia, sono specifici dell'istanza e vengono quindi aggiunti all'oggetto stesso durante la costruzione.

Senza la sintassi della classe, gli sviluppatori dovevano configurare manualmente la catena di prototipi per ottenere funzionalità simili all'eredità classica. Ciò ha portato a una preponderanza di diversi modi per raggiungere questo obiettivo.

Ecco un modo:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... ed ecco un altro modo:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

La sintassi della classe introdotta in ES2015 semplifica le cose, fornendo extends come "l'unico vero modo" per configurare la catena di prototipi al fine di simulare l'eredità classica in JavaScript.

Quindi, simile al codice sopra, se usi la sintassi della classe per creare un nuovo oggetto in questo modo:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... l'oggetto risultante [[Prototype]]verrà impostato su un'istanza di Parent, il cui [[Prototype]], a sua volta, è Parent.prototype.

Infine, se si crea un nuovo oggetto tramite Object.create(foo), l'oggetto risultante [[Prototype]]verrà impostato su foo.


1
Quindi, sto facendo qualcosa di sbagliato definendo nuove proprietà sulla proprietà del prototipo nel mio breve frammento?
John Leidegren,

3
Penso che questo significhi avere oggetti funzionali come cittadini di prima classe.
John Leidegren,

8
Odio le cose non standard, specialmente nei linguaggi di programmazione, perché c'è persino un proto quando chiaramente non è necessario?
John Leidegren,

1
@John __proto__ è necessario per uso interno solo dall'interprete JS. Ogni oggetto deve sapere quali proprietà e metodi fanno parte del prototipo e quali fanno parte dell'oggetto stesso. L'interprete JS deve essere in grado di chiamare metodi prototipati su un oggetto.
BMiner

17
si noti che l'uso di [[prototipo]] è deliberato - ECMA-262 racchiude i nomi delle proprietà interne tra parentesi quadre doppie
Christoph,

1798

In un linguaggio che implementa l'eredità classica come Java, C # o C ++, inizi creando una classe - un modello per i tuoi oggetti - e quindi puoi creare nuovi oggetti da quella classe o puoi estendere la classe, definendo una nuova classe che aumenta la classe originale.

In JavaScript devi prima creare un oggetto (non esiste un concetto di classe), quindi puoi aumentare il tuo oggetto o creare nuovi oggetti da esso. Non è difficile, ma un po 'estraneo e difficile da metabolizzare per qualcuno abituato al modo classico.

Esempio:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

Fino ad ora ho esteso l'oggetto base, ora creo un altro oggetto e quindi eredito da Person.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Mentre, come detto, non posso chiamare setAmountDue (), getAmountDue () su una persona.

//The following statement generates an error.
john.setAmountDue(1000);

352
Penso che le risposte su StackOverflow non siano solo interessanti per il poster originale, ma anche per una grande comunità di altre persone in agguato o provenienti da ricerche. E sono stato uno di loro e ho tratto beneficio dai vecchi post. Penso di poter contribuire alle altre risposte aggiungendo alcuni esempi di codice. Informazioni sulla tua domanda: se lasci fuori il nuovo, non funziona. quando chiamo myCustomer.sayMyName () restituisce "myCustomer.sayMyName non è una funzione". Il modo più semplice è sperimentare firebug e vedere cosa succede.
Stavlo,

7
Per quanto ho capito var Person = function (name) {...}; sta definendo una funzione di costruzione in grado di costruire oggetti personali. Quindi non c'è ancora nessun oggetto, solo la funzione di costruzione anonima è assegnata a persona. Questa è un'ottima spiegazione: helephant.com/2008/08/how-javascript-objects-work
stivlo

17
ATTENZIONE: questa risposta trascura il fatto che il costruttore della classe genitore non viene chiamato in base all'istanza. L'unica ragione per cui funziona è perché ha fatto esattamente la stessa cosa (impostando il nome) sia nel costruttore figlio che in quello genitore. Per una spiegazione più approfondita degli errori comuni commessi durante il tentativo di ereditarietà in JavaScript (e una soluzione finale), vedere: questo post di overflow dello stack
Aaren Cordova

3
Noto che anche questa risposta non menziona il fatto che utilizzando "new Person ()" come prototipo, si sta effettivamente impostando la proprietà dell'istanza "name" di "Person" come proprietà statica di "Customer" (quindi tutti i clienti le istanze avranno la stessa proprietà). Sebbene sia un buon esempio di base, NON FARLO. :) Crea una nuova funzione anonima che funga da "ponte" impostando il prototipo su "Person.prototype", quindi crea un'istanza da essa e imposta "Customer.prototype" su quell'istanza anonima.
James Wilkins,

10
Riguardo alla Customer.prototype = new Person();linea, MDN mostra un esempio usando Customer.prototype = Object.create(Person.prototype), e afferma che 'Un errore comune qui è usare "new Person ()"' . fonte
Rafael Eyng,

186

Questo è un modello di oggetto basato su prototipo molto semplice che verrebbe considerato come un campione durante la spiegazione, senza ancora commenti:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

Ci sono alcuni punti cruciali che dobbiamo considerare prima di passare attraverso il concetto di prototipo.

1- Come funzionano effettivamente le funzioni JavaScript:

Per fare il primo passo dobbiamo capire come funzionano effettivamente le funzioni JavaScript, come una funzione di classe come usare la thisparola chiave in essa o semplicemente come una normale funzione con i suoi argomenti, cosa fa e cosa restituisce.

Diciamo che vogliamo creare un Personmodello a oggetti. ma in questo passaggio ci proverò fare la stessa cosa esatta senza l'utilizzo prototypee newparola chiave .

Quindi in questo passaggio functions,objects e thisparola chiave, siamo tutto ciò che abbiamo.

La prima domanda sarebbe comethis parola chiave potrebbe essere utile senza usare la newparola chiave .

Quindi per rispondere diciamo che abbiamo un oggetto vuoto e due funzioni come:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

e adesso senza usare la newparola chiave come potremmo usare queste funzioni. Quindi JavaScript ha 3 modi diversi per farlo:

un. il primo modo è solo quello di chiamare la funzione come una normale funzione:

Person("George");
getName();//would print the "George" in the console

in questo caso, questo sarebbe l'oggetto di contesto corrente, che di solito è il globale window oggetto nel browser o GLOBALinNode.js . Significa che avremmo, window.name nel browser o GLOBAL.name in Node.js, con "George" come valore.

b. Noi possiamo collegarli a un oggetto, come le sue proprietà

- Il modo più semplice per farlo è modificare l' personoggetto vuoto , come:

person.Person = Person;
person.getName = getName;

in questo modo possiamo chiamarli come:

person.Person("George");
person.getName();// -->"George"

e ora l' personoggetto è come:

Object {Person: function, getName: function, name: "George"}

- L'altro modo per associare una proprietà a un oggetto è usare quello prototypedi quell'oggetto che può essere trovato in qualsiasi oggetto JavaScript con il nome di __proto__, e ho provato a spiegarlo un po 'nella parte di riepilogo. Quindi potremmo ottenere il risultato simile facendo:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

Ma in questo modo ciò che stiamo effettivamente facendo è modificare il Object.prototype, perché ogni volta che creiamo un oggetto JavaScript usando i valori letterali ( { ... }), viene creato in base a Object.prototype, il che significa che viene collegato all'oggetto appena creato come un attributo chiamato__proto__ , quindi se lo cambiamo , come abbiamo fatto con il nostro precedente frammento di codice, tutti gli oggetti JavaScript verrebbero modificati, non è una buona pratica. Quindi quale potrebbe essere la migliore pratica ora:

person.__proto__ = {
    Person: Person,
    getName: getName
};

e ora altri oggetti sono in pace, ma non sembra ancora essere una buona pratica. Quindi abbiamo ancora un'altra soluzione, ma per usare questa soluzione dovremmo tornare a quella riga di codice in cui l' personoggetto è stato creato ( var person = {};) quindi cambiarlo come:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

quello che fa è creare un nuovo JavaScript Objecte allegare propertiesObjectal file__proto__ attributo. Quindi, per essere sicuro di poter fare:

console.log(person.__proto__===propertiesObject); //true

Ma il punto difficile qui è che hai accesso a tutte le proprietà definite nel __proto__primo livello diperson dell'oggetto (leggi la parte di riepilogo per maggiori dettagli).


come vedi usando uno di questi due modi this significherebbe esattamente l' personoggetto.

c. JavaScript ha un altro modo per fornire la funzione this, che utilizza call o apply per invocare la funzione.

Il metodo apply () chiama una funzione con un dato valore e argomenti forniti come una matrice (o un oggetto simile a una matrice).

e

Il metodo call () chiama una funzione con un dato valore e argomenti forniti singolarmente.

in questo modo, che è il mio preferito, possiamo facilmente chiamare le nostre funzioni come:

Person.call(person, "George");

o

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

questi 3 metodi sono i passi iniziali importanti per capire la funzionalità .prototype.


2- Come funziona il new parola chiave?

questo è il secondo passo per capire la .prototypefunzionalità. Questo è quello che uso per simulare il processo:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

in questa parte cercherò di fare tutti i passi che JavaScript prende, senza usare la newparola chiave e prototype, quando usi la newparola chiave. così quando lo facciamonew Person("George") ,Person funzione funge da costruttore, cosa fa JavaScript, uno per uno:

un. prima di tutto crea un oggetto vuoto, sostanzialmente un hash vuoto come:

var newObject = {};

b. il prossimo passo che JavaScript fa è collegarlo tutti gli oggetti prototipo all'oggetto appena creato

abbiamo my_person_prototypequi simile all'oggetto prototipo.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

JavaScript non associa effettivamente le proprietà definite nel prototipo. Il modo attuale è legato al concetto di catena prototipo.


un. & b. Invece di questi due passaggi puoi ottenere lo stesso risultato esatto facendo:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

ora possiamo chiamare la getNamefunzione nel nostro my_person_prototype:

newObject.getName();

c. quindi dà quell'oggetto al costruttore,

possiamo farlo con il nostro campione come:

Person.call(newObject, "George");

o

Person.apply(newObject, ["George"]);

quindi il costruttore può fare quello che vuole, perché questo all'interno di quel costruttore è l'oggetto che è stato appena creato.

ora il risultato finale prima di simulare gli altri passaggi: Object {name: "George"}


Sommario:

Fondamentalmente, quando usi la nuova parola chiave su una funzione, la stai invocando e quella funzione funge da costruttore, quindi quando dici:

new FunctionName()

JavaScript crea internamente un oggetto, un hash vuoto e quindi dà quell'oggetto al costruttore, quindi il costruttore può fare quello che vuole, perché questo all'interno di quel costruttore è l'oggetto che è stato appena creato e quindi ti dà quell'oggetto ovviamente se non hai utilizzato l'istruzione return nella tua funzione o se hai inserito un return undefined;alla fine del corpo della tua funzione.

Quindi, quando JavaScript cerca una proprietà su un oggetto, la prima cosa che fa è cercare quella su quell'oggetto. E poi c'è una proprietà segreta [[prototype]]che di solito ce l'abbiamo __proto__e quella proprietà è ciò che JavaScript guarda dopo. E quando guarda attraverso __proto__, per quanto sia di nuovo un altro oggetto JavaScript, ha il suo __proto__attributo, va su e su fino a raggiungere il punto in cui il prossimo __proto__è nullo. Il punto è l'unico oggetto in JavaScript che il suo __proto__attributo è null è Object.prototypeobject:

console.log(Object.prototype.__proto__===null);//true

ed è così che funziona l'ereditarietà in JavaScript.

La catena di prototipi

In altre parole, quando hai una proprietà prototipo su una funzione e ne chiami una nuova su quella, dopo che JavaScript ha finito per guardare l'oggetto appena creato per le proprietà, andrà a guardare la funzione .prototypeed è anche possibile che questo oggetto abbia il suo proprio prototipo interno. e così via.


6
a) Per favore non spiegare i prototipi copiando le proprietà b) L'impostazione del [[prototipo]] interno avviene prima dell'applicazione della funzione di costruzione sull'istanza, si prega di cambiare l'ordine c) jQuery è totalmente offtopico in questa domanda
Bergi,

1
@Bergi: grazie per la segnalazione, sarei apprezzato se mi facessi sapere se va bene ora.
Mehran Hatami,

7
Puoi per favore renderlo semplice? Hai ragione su tutti i punti, ma gli studenti che leggono questa spiegazione possono essere davvero confusi per la prima volta. raccogli qualsiasi esempio più semplice e lascia che il codice si spieghi da solo o aggiungi un mucchio di commenti per chiarire cosa intendi.
PM

2
@PM: grazie per il tuo feedback. Ho provato a renderlo il più semplice possibile ma penso che tu abbia ragione ha ancora alcuni punti vaghi. Quindi proverò a modificarlo e anche a essere più descrittivo. :)
Mehran Hatami

1
@AndreaMattioli perché in questo modo stai creando un oggetto totalmente nuovo e sovrascrivendo quello vecchio che potrebbe essere condiviso anche con altri oggetti. Sostituendoti __proto__, eliminerai prima tutte le proprietà del prototipo di livello superiore e quindi avrai una nuova base di proto che non è più condivisa a meno che non la condividi.
Mehran Hatami,

77

I sette Koan del prototipo

Mentre Ciro San discendeva dal Monte Fox dopo una profonda meditazione, la sua mente era chiara e pacifica.

La sua mano, tuttavia, era irrequieta, e da sola afferrò un pennello e annotò le seguenti note.


0) Due cose diverse possono essere chiamate "prototipo":

  • la proprietà del prototipo, come in obj.prototype

  • la proprietà interna del prototipo, indicata come [[Prototype]] in ES5 .

    Può essere recuperato tramite ES5 Object.getPrototypeOf() .

    Firefox lo rende accessibile tramite la __proto__proprietà come estensione. ES6 ora menziona alcuni requisiti opzionali per __proto__.


1) Questi concetti esistono per rispondere alla domanda:

Quando lo faccio obj.property, dove cerca JS .property?

Intuitivamente, l'eredità classica dovrebbe influire sulla ricerca di proprietà.


2)

  • __proto__viene utilizzato per la .ricerca delle proprietà dei punti come inobj.property .
  • .prototypenon viene utilizzato per la ricerca direttamente, solo indirettamente, poiché determina __proto__al momento della creazione dell'oggetto new.

L'ordine di ricerca è:

  • obj proprietà aggiunte con obj.p = ... oObject.defineProperty(obj, ...)
  • proprietà di obj.__proto__
  • proprietà di obj.__proto__.__proto__ e così via
  • se qualcuno lo __proto__è null, ritorna undefined.

Questa è la cosiddetta catena di prototipi .

Puoi evitare la .ricerca con obj.hasOwnProperty('key')eObject.getOwnPropertyNames(f)


3) Esistono due modi principali per impostare obj.__proto__:

  • new:

    var F = function() {}
    var f = new F()

    quindi newha impostato:

    f.__proto__ === F.prototype

    Qui è dove .prototypesi abitua.

  • Object.create:

     f = Object.create(proto)

    imposta:

    f.__proto__ === proto

4) Il codice:

var F = function(i) { this.i = i }
var f = new F(1)

Corrisponde al seguente diagramma (alcune Numbercose sono state omesse):

(Function)       (  F  )                                      (f)----->(1)
 |  ^             | | ^                                        |   i    |
 |  |             | | |                                        |        |
 |  |             | | +-------------------------+              |        |
 |  |constructor  | |                           |              |        |
 |  |             | +--------------+            |              |        |
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |  |             |                | +----------+              |        |
 |  |             |                | |                         |        |
 |  |             |                | | +-----------------------+        |
 |  |             |                | | |                                |
 v  |             v                v | v                                |
(Function.prototype)              (F.prototype)                         |
 |                                 |                                    |
 |                                 |                                    |
 |[[Prototype]]                    |[[Prototype]]          [[Prototype]]|
 |                                 |                                    |
 |                                 |                                    |
 | +-------------------------------+                                    |
 | |                                                                    |
 v v                                                                    v
(Object.prototype)                                       (Number.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

Questo diagramma mostra molti nodi di oggetti predefiniti nella lingua:

  • null
  • Object
  • Object.prototype
  • Function
  • Function.prototype
  • 1
  • Number.prototype(può essere trovato con (1).__proto__, parentesi obbligatoria per soddisfare la sintassi)

Le nostre 2 righe di codice hanno creato solo i seguenti nuovi oggetti:

  • f
  • F
  • F.prototype

iè ora una proprietà di fperché quando lo fai:

var f = new F(1)

valuta Fcon thisil valore che newverrà restituito, a cui viene assegnato f.


5) .constructor normalmente viene dal F.prototypeattraverso la .ricerca:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

Quando scriviamo f.constructor, JavaScript esegue la .ricerca come:

  • f non ha .constructor
  • f.__proto__ === F.prototypeha .constructor === F, quindi prendilo

Il risultato f.constructor == Fè intuitivamente corretto, poiché Fviene utilizzato per costruire f, ad esempio, impostare campi, proprio come nei linguaggi OOP classici.


6) La sintassi ereditaria classica può essere ottenuta manipolando le catene di prototipi.

ES6 aggiunge le parole chiave classe extends, che sono principalmente zucchero di sintassi per la follia di manipolazione del prototipo precedentemente possibile.

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
c = new C(1)
c.inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

Diagramma semplificato senza tutti gli oggetti predefiniti:

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

Prendiamoci un momento per studiare come funziona:

c = new C(1)
c.inc() === 2

Il primo set di linea c.ia 1come spiegato in "4)".

Sulla seconda riga, quando lo facciamo:

c.inc()
  • .incsi trova attraverso la [[Prototype]]catena: c-> C-> C.prototype->inc
  • quando chiamiamo una funzione in Javascript come X.Y(), JavaScript si imposta automaticamente thissu uguale Xall'interno della Y()chiamata di funzione!

La stessa identica logica spiega anche d.ince d.inc2.

Questo articolo https://javascript.info/class#not-just-a-syntax-sugar menziona ulteriori effetti che classvale la pena conoscere. Alcuni di essi potrebbero non essere realizzabili senza la classparola chiave (verifica TODO quale):


1
@tomasb grazie! "Non so dove l'hai preso": dopo aver visto alcuni di quei linguaggi dinamici, ho notato che ciò che conta di più del loro sistema di classe è come funziona la .ricerca (e quante copie dei dati sono fatte) . Quindi ho deciso di capire questo punto. Il resto sono Google + post sul blog + un interprete Js a portata di mano. :)
Ciro Santilli 26 冠状 病 六四 事件 法轮功

1
Ancora non capisco perché g.constructor === Oggetto perché hai detto che "4) Quando fai f = new F, new imposta anche f.constructor = F". Potresti spiegarmi di più? Comunque questa è la migliore risposta che sto cercando. Grazie mille!
nguyenngoc101,

@nguyenngoc101 grazie! La sets f.constructor = Fparte era palesemente sbagliata e contraddetta da ulteriori sezioni: .constructorsi trova attraverso la .ricerca sulla catena di prototipi. Risolto il problema ora.
Ciro Santilli 5 冠状 病 六四 事件 法轮功

da tutta la discussione che cosa ottengo (derivante dall'eredità classica) se creo la funzione di costruzione e provo a crearne un'istanza usando un nuovo operatore otterrò solo metodi e proprietà che erano collegati all'oggetto proto, quindi è necessario collegare tutto il metodo e proprietà per proto-oggetto se vogliamo ereditare, mi giusto?
blackHawk,

1
@CiroSantilli 刘晓波 死 六四 事件 法轮功 Non credo sia un bug in Chromium. Penso che sia solo un sintomo che fil prototipo è ambientato Fsolo al momento della costruzione; fnon saprà o se ne preoccuperà F.prototypein nessun momento dopo la sua prima costruzione.
John Glassmyer,

76

prototypeti permette di fare lezioni. se non lo usiprototype , diventa statico.

Ecco un breve esempio.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

Nel caso precedente, si dispone di un test di chiamata con funzione statica. Questa funzione è accessibile solo da obj.test dove puoi immaginare che obj sia una classe.

dove come nel codice qui sotto

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

L'obj è diventato una classe che ora può essere istanziata. Possono esistere più istanze di obj e tutte hanno iltest funzione.

Quanto sopra è la mia comprensione. Lo sto trasformando in un wiki della comunità, quindi le persone possono correggermi se sbaglio.


13
-1: prototypeè una proprietà delle funzioni del costruttore, non delle istanze, ovvero il codice è sbagliato! Forse intendevi la proprietà non standard __proto__degli oggetti, ma questa è una bestia completamente diversa ...
Christoph,

@Christoph - Grazie per averlo segnalato. Ho aggiornato il codice di esempio.
Ramesh,

3
C'è molto di più ... Inoltre JavaScript non è un linguaggio di classe - si occupa dell'eredità tramite prototipi, è necessario coprire le differenze in modo più dettagliato!
James,

5
Penso che questa risposta sia un po 'fuorviante.
Armin Cifuentes,

Forse la risposta è "fuorviante", ma spiega a cosa serve il prototipo e ora mi è tutto chiaro, dopo tutte quelle "risposte" con centinaia di voti "positivi". Grazie.
Aleks,

66

Dopo aver letto questa discussione, mi sento confuso con JavaScript Prototype Chain, quindi ho trovato questi grafici

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance * [[protytype]] * e <code> prototype </code> proprietà degli oggetti funzione

è un grafico chiaro per mostrare l'ereditarietà JavaScript per catena di prototipi

e

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

questo contiene un esempio con codice e diversi schemi carini.

la catena di prototipi alla fine ricade su Object.prototype.

la catena di prototipi può essere estesa tecnicamente quanto vuoi, ogni volta impostando il prototipo della sottoclasse uguale a un oggetto della classe genitore.

Spero sia utile anche per te capire la catena di prototipi JavaScript.


È possibile avere ereditarietà multiple su Javascript?

Foo è un oggetto letterale qui o oggetto funzione? Se è un oggetto letterale, credo che Foo.prototype non farà riferimento a Foo tramite il costruttore.
Madhur Ahuja,

@ user3376708 JavaScript supporta solo l'ereditarietà singola ( fonte )
Rafael Eyng

@ Nuno_147 All'inizio non è chiaro, ma se guardi abbastanza a lungo potresti ricavarne qualcosa.
marcelocra,

3
Puoi spiegare cosa [[Prototype]]significa?
CodyBugstein,

40

Ogni oggetto ha una proprietà interna, [[Prototype]] , che lo collega a un altro oggetto:

object [[Prototype]]  anotherObject

Nel javascript tradizionale, l'oggetto collegato è la prototypeproprietà di una funzione:

object [[Prototype]]  aFunction.prototype

Alcuni ambienti espongono [[Prototype]] come __proto__:

anObject.__proto__ === anotherObject

Si crea il collegamento [[Prototype]] durante la creazione di un oggetto.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

Quindi queste affermazioni sono equivalenti:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

Non puoi effettivamente vedere il link target ( Object.prototype) in una nuova istruzione; invece l'obiettivo è implicito dal costruttore (Object ).

Ricorda:

  • Ogni oggetto ha un link, [[Prototype]] , a volte esposto come __proto__ .
  • Ogni funzione ha una prototypeproprietà, inizialmente contenente un oggetto vuoto.
  • Gli oggetti creati con nuovi sono collegati alla prototypeproprietà del loro costruttore.
  • Se una funzione non viene mai utilizzata come costruttore, è sua prototype proprietà rimarrà inutilizzata.
  • Se non hai bisogno di un costruttore, usa Object.create invece di new.

1
La revisione 5 ha rimosso alcune informazioni utili, comprese le informazioni su Object.create (). Vedi revisione 4 .
Palec,

@Palec cosa devo aggiungere di nuovo?
sam

2
IMO almeno il link ai Object.create()documenti , @sam. Collegamenti a __proto__e Object.prototypesarebbe un bel miglioramento. E mi sono piaciuti i tuoi esempi di come funzionano i prototipi con i costruttori e Object.create(), ma erano probabilmente la parte lunga e meno rilevante di cui volevi sbarazzarti.
Palec,

da tutta la discussione che cosa ottengo (derivante dall'eredità classica) se creo la funzione di costruzione e provo a crearne un'istanza usando un nuovo operatore otterrò solo metodi e proprietà che erano collegati all'oggetto proto, quindi è necessario collegare tutto il metodo e proprietà per proto-oggetto se vogliamo ereditare, mi giusto?
blackHawk,

29

Javascript non ha ereditarietà nel solito senso, ma ha la catena prototipo.

catena prototipo

Se un membro di un oggetto non può essere trovato nell'oggetto, lo cerca nella catena di prototipi. La catena è composta da altri oggetti. È possibile accedere al prototipo di una determinata istanza con__proto__ variabile. Ogni oggetto ne ha uno, poiché non esiste alcuna differenza tra classi e istanze in javascript.

Il vantaggio di aggiungere una funzione / variabile al prototipo è che deve essere nella memoria solo una volta, non per ogni istanza.

È anche utile per l'eredità, perché la catena di prototipi può essere costituita da molti altri oggetti.


1
FF e Chrome supportano proto , ma non IE né Opera.
circa il

Georg, per favore chiarisci un noob - "non c'è differenza tra classi ed istanze in javascript". - potresti elaborare? Come funziona?
Hamish Grubijan,

da tutta la discussione che cosa ottengo (derivante dall'eredità classica) se creo la funzione di costruzione e provo a crearne un'istanza usando un nuovo operatore otterrò solo metodi e proprietà che erano collegati all'oggetto proto, quindi è necessario collegare tutto il metodo e proprietà per proto-oggetto se vogliamo ereditare, mi giusto?
blackHawk,

28

Questo articolo è lungo Ma sono sicuro che cancellerà la maggior parte delle tue domande sulla natura "prototipica" dell'ereditarietà JavaScript. E anche di più. Si prega di leggere l'articolo completo.

JavaScript ha fondamentalmente due tipi di tipi di dati

  • Non oggetti
  • Oggetti

Non oggetti

Di seguito sono riportati i tipi di dati Non oggetto

  • corda
  • numero (compresi NaN e Infinity)
  • valori booleani (vero, falso)
  • non definito

Questi tipi di dati restituiscono quanto segue quando si utilizza l' operatore typeof

typeof "string letteral" (o una variabile contenente string letteral) === 'string'

typeof 5 (o qualsiasi letterale numerico o una variabile contenente letterale numerico o NaN o Infynity ) === 'numero'

tipo di vero (o falso o una variabile contenente vero o falso ) === "booleano"

tipo di non definito (o una variabile non definita o una variabile che contiene non definita ) === 'undefined'

La stringa , il numero e booleani tipi di dati possono essere rappresentati sia come oggetti e non oggetti .Quando sono rappresentati come oggetti la loro typeof è sempre === 'oggetto'. Torneremo su questo quando capiremo i tipi di dati oggetto.

Oggetti

I tipi di dati oggetto possono essere ulteriormente suddivisi in due tipi

  1. Oggetti del tipo di funzione
  2. Oggetti di tipo non funzionale

Gli oggetti del tipo di funzione sono quelli che restituiscono la stringa 'funzione' con l' operatore typeof . Tutte le funzioni definite dall'utente e tutti gli oggetti JavaScript incorporati che possono creare nuovi oggetti utilizzando un nuovo operatore rientrano in questa categoria. Per es.

  • Oggetto
  • Corda
  • Numero
  • booleano
  • Vettore
  • Matrici tipizzate
  • RegExp
  • Funzione
  • Tutti gli altri oggetti incorporati che possono creare nuovi oggetti utilizzando il nuovo operatore
  • funzione UserDefinedFunction () {/ * codice definito dall'utente * /}

Quindi, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Function) == = typeof (UserDefinedFunction) === 'funzione'

Tutti gli oggetti del tipo di funzione sono in realtà istanze dell'oggetto JavaScript incorporato Funzione (incluso l' oggetto Function , ovvero definito in modo ricorsivo). È come se questi oggetti fossero stati definiti nel modo seguente

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

Come accennato, gli oggetti di tipo Funzione possono creare ulteriormente nuovi oggetti utilizzando il nuovo operatore . Ad esempio , è possibile creare un oggetto di tipo Object , String , Number , Boolean , Array , RegExp o UserDefinedFunction utilizzando

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

Gli oggetti così creati sono tutti oggetti di tipo Non Function e restituiscono il loro typeof === 'object' . In tutti questi casi l'oggetto "a" non può creare ulteriormente oggetti utilizzando l'operatore new. Quindi il seguente è sbagliato

var b=new a() //error. a is not typeof==='function'

L'oggetto Math incorporato è typeof === 'oggetto' . Quindi un nuovo oggetto di tipo Math non può essere creato da un nuovo operatore.

var b=new Math() //error. Math is not typeof==='function'

Si noti inoltre che le funzioni Object , Array ed RegExp possono creare un nuovo oggetto senza nemmeno utilizzare l' operatore new . Tuttavia quelli che seguono non lo fanno.

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

Le funzioni definite dall'utente sono casi speciali.

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

Poiché gli oggetti di tipo Funzione possono creare nuovi oggetti, vengono anche chiamati Costruttori .

Ogni costruttore / funzione (se incorporato o definito dall'utente) quando definito automaticamente ha una proprietà chiamata "prototipo" il cui valore di default è impostato come oggetto. Questo oggetto stesso ha una proprietà chiamata "costruttore" che per impostazione predefinita fa riferimento al costruttore / funzione .

Ad esempio quando definiamo una funzione

function UserDefinedFunction()
{
}

il seguito avviene automaticamente

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

Questa proprietà "prototipo" è presente solo negli oggetti di tipo Funzione (e mai negli oggetti di tipo Non Funzione ).

Questo perché quando viene creato un nuovo oggetto (usando un nuovo operatore) eredita tutte le proprietà e tutti i metodi dall'oggetto prototipo corrente della funzione Costruttore, ovvero un riferimento interno viene creato nell'oggetto appena creato che fa riferimento all'oggetto a cui fa riferimento l'oggetto prototipo corrente della funzione Costruttore.

Questo "riferimento interno" che viene creato nell'oggetto per fare riferimento alle proprietà ereditate è noto come prototipo dell'oggetto (che fa riferimento all'oggetto a cui fa riferimento la proprietà "prototipo" del costruttore ma è diverso da esso). Per qualsiasi oggetto (Funzione o Non funzione) questo può essere recuperato usando il metodo Object.getPrototypeOf () . Utilizzando questo metodo si può tracciare la catena di prototipi di un oggetto.

Inoltre, ogni oggetto creato ( tipo di funzione o tipo di non funzione ) ha una proprietà "costruttore" che viene ereditata dall'oggetto a cui fa riferimento la proprietà prototipo della funzione costruttore. Per impostazione predefinita, questa proprietà "costruttore" fa riferimento alla funzione Costruttore che l'ha creata (se il "prototipo" predefinito della Funzione costruttore non viene modificato).

Per tutti gli oggetti di tipo Funzione la funzione di costruzione è sempre Funzione () {}

Per oggetti di tipo Non Function (es. Javascript Javascript in oggetti Math) la funzione di costruzione è la funzione che l'ha creata. Per l' oggetto Math è la funzione Object () {} .

Tutto il concetto spiegato sopra può essere un po 'scoraggiante da capire senza alcun codice di supporto. Si prega di passare attraverso il seguente codice riga per riga per comprendere il concetto. Prova a eseguirlo per avere una migliore comprensione.

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

La catena di prototipi di ogni oggetto alla fine risale a Object.prototype (che di per sé non ha alcun oggetto prototipo). Il seguente codice può essere utilizzato per tracciare la catena di prototipi di un oggetto

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

La catena di prototipi per vari oggetti funziona come segue.

  • Ogni oggetto Function (incluso l'oggetto Function incorporato) -> Function.prototype -> Object.prototype -> null
  • Oggetti semplici (creati da new Object () o {} incluso l'oggetto Math incorporato) -> Object.prototype -> null
  • Oggetto creato con new o Object.create -> Una o più catene prototipo -> Object.prototype -> null

Per creare un oggetto senza prototipo, utilizzare quanto segue:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

Si potrebbe pensare che l'impostazione della proprietà prototipo del costruttore su null crei un oggetto con un prototipo null. Tuttavia, in questi casi il prototipo dell'oggetto appena creato è impostato su Object.prototype e il suo costruttore è impostato sulla funzione Object. Ciò è dimostrato dal seguente codice

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

Di seguito nel riassunto di questo articolo

  • Esistono due tipi di oggetti Tipi di funzione e tipi di non funzione
  • Solo gli oggetti di tipo Funzione possono creare un nuovo oggetto usando l' operatore new . Gli oggetti così creati sono oggetti di tipo Non Function . Gli oggetti di tipo Non Function non possono ulteriormente creare un oggetto utilizzando l' operatore new .

  • Tutti gli oggetti del tipo di funzione per impostazione predefinita hanno una proprietà "prototype" . Questa proprietà "prototipo" fa riferimento a un oggetto che ha una proprietà "costruttore" che per impostazione predefinita fa riferimento all'oggetto tipo di funzione stesso.

  • Tutti gli oggetti ( tipo di funzione e tipo di non funzione ) hanno una proprietà "costruttore" che, per impostazione predefinita, fa riferimento all'oggetto del tipo di funzione / costruttore che lo ha creato.

  • Ogni oggetto che viene creato fa riferimento internamente all'oggetto a cui fa riferimento la proprietà "prototype" del costruttore che lo ha creato. Questo oggetto è noto come prototipo dell'oggetto creato (che è diverso dalla proprietà "prototipo" degli oggetti di tipo Funzione a cui fa riferimento). In questo modo l'oggetto creato può accedere direttamente ai metodi e alle proprietà definiti nell'oggetto a cui fa riferimento la proprietà "prototype" del costruttore (al momento della creazione dell'oggetto).

  • Il prototipo di un oggetto (e quindi i suoi nomi di proprietà ereditati) può essere recuperato usando il metodo Object.getPrototypeOf () . In realtà questo metodo può essere utilizzato per navigare nell'intera catena di prototipi dell'oggetto.

  • La catena di prototipi di ogni oggetto alla fine risale a Object.prototype (a meno che l'oggetto non venga creato utilizzando Object.create (null), nel qual caso l'oggetto non ha prototipo).

  • typeof (new Array ()) === 'oggetto' è in base alla progettazione del linguaggio e non è un errore come indicato da Douglas Crockford

  • L'impostazione della proprietà prototipo del costruttore su null (o indefinito, number, true, false, string) non deve creare un oggetto con un prototipo null. In tali casi, il prototipo dell'oggetto appena creato è impostato su Object.prototype e il suo costruttore è impostato sulla funzione Object.

Spero che sia di aiuto.


24

Il concetto di prototypaleredità è uno dei più complicati per molti sviluppatori. Proviamo a capire la radice del problema per capire prototypal inheritancemeglio. Cominciamo con una plainfunzione.

inserisci qui la descrizione dell'immagine

Se utilizziamo un newoperatore su Tree function, lo chiamiamo come una constructorfunzione.

inserisci qui la descrizione dell'immagine

Ogni JavaScriptfunzione ha un prototype. Quando si registra il Tree.prototype, si ottiene ...

inserisci qui la descrizione dell'immagine

Se guardi l' console.log()output di cui sopra , potresti vedere una proprietà del costruttore Tree.prototypee anche una __proto__proprietà. Il__proto__ rappresenta il prototypeche questo functionè basato, e dal momento che questo è solo una pianura JavaScript functionsenza inheritanceistituito ancora, si riferisce al Object prototypeche è qualcosa appena costruito a JavaScript ...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

Questo ha cose come .toString, .toValue, .hasOwnPropertyecc ...

__proto__che è stato portato il mio Mozilla è deprecato e viene sostituito dal Object.getPrototypeOfmetodo per ottenere il object's prototype.

inserisci qui la descrizione dell'immagine

Object.getPrototypeOf(Tree.prototype); // Object {} 

Aggiungiamo un metodo al nostro Tree prototype .

inserisci qui la descrizione dell'immagine

Abbiamo modificato il Roote aggiunto afunction ramo ad esso.

inserisci qui la descrizione dell'immagine

Ciò significa che quando si crea un instancedi Tree, è possibile chiamare il branchmetodo.

inserisci qui la descrizione dell'immagine

Possiamo anche aggiungere primitiveso objectsal nostro Prototype.

inserisci qui la descrizione dell'immagine

Aggiungiamo un child-treeal nostro Tree.

inserisci qui la descrizione dell'immagine

Qui Childeredita la sua prototypedall'albero, quello che stiamo facendo qui sta usando il Object.create()metodo per creare un nuovo oggetto basato su ciò che passi, eccolo qui Tree.prototype. In questo caso, ciò che stiamo facendo è impostare il prototipo di Child su un nuovo oggetto che sia identico al Treeprototipo. Successivamente stiamo impostando il Child's constructor to Child, se non lo facessimo farebbe riferimento Tree().

inserisci qui la descrizione dell'immagine

Childora ha il suo prototype, i suoi __proto__punti Treee Tree's prototypepunta alla base Object.

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

Ora crei un instancedi Childe chiama branchche è originariamente disponibile in Tree. In realtà non abbiamo definito il nostro branchsul Child prototype. MA, nelRoot prototype cui il bambino eredita.

inserisci qui la descrizione dell'immagine

In JS tutto non è un oggetto, tutto può agire come un oggetto.

Javascriptha primitivi come strings, number, booleans, undefined, null.non lo sono object(i.e reference types), ma certamente può agire come un object. Diamo un'occhiata a un esempio qui.

inserisci qui la descrizione dell'immagine

Nella prima riga di questo elenco, primitiveviene assegnato un valore stringa al nome. La seconda riga tratta il nome come un objecte chiama charAt(0)usando la notazione punto.

Questo è ciò che accade dietro le quinte: // cosa fa il JavaScriptmotore

inserisci qui la descrizione dell'immagine

L' String objectesiste solo per una dichiarazione prima che sia distrutto (un processo chiamato autoboxing). Torniamo di nuovo al nostro prototypal inheritance.

  • Javascriptsupporta l'ereditarietà delegationbasata su prototypes.
  • Ognuno Functionha una prototypeproprietà, che fa riferimento a un altro oggetto.
  • properties/functionssono guardati da objectse stessi o tramite prototypecatena se non esiste

A prototypein JS è un oggetto a cui yieldssei il genitore di un altro object. [es. delegazione] Delegation significa che se non sei in grado di fare qualcosa, dirai a qualcun altro di farlo per te.

inserisci qui la descrizione dell'immagine

https://jsfiddle.net/say0tzpL/1/

Se cerchi il violino sopra, il cane ha accesso al toStringmetodo, ma non è disponibile in esso, ma è disponibile tramite la catena di prototipi che delega aObject.prototype

inserisci qui la descrizione dell'immagine

Se guardi quello sotto, stiamo provando ad accedere al callmetodo che è disponibile in ogni function.

inserisci qui la descrizione dell'immagine

https://jsfiddle.net/rknffckc/

Se cerchi il violino sopra, ProfileFunction ha accesso al callmetodo, ma non è disponibile in esso, ma è disponibile tramite la catena di prototipi che delega aFunction.prototype

inserisci qui la descrizione dell'immagine

Nota: prototype è una proprietà del costruttore della funzione, mentre __proto__è una proprietà degli oggetti costruiti dal costruttore della funzione. Ogni funzione ha una prototypeproprietà il cui valore è vuoto object. Quando creiamo un'istanza della funzione, otteniamo una proprietà interna [[Prototype]]o il __proto__cui riferimento è il prototipo della funzione constructor.

inserisci qui la descrizione dell'immagine

Il diagramma sopra sembra un po 'complicato, ma mette in evidenza l'intera immagine di come prototype chaining funziona. Camminiamo attraverso questo lentamente:

Ci sono due istanze b1e b2, il cui costruttore è Bare padre è Foo e ha due metodi dalla catena prototipo identifye speakvia BareFoo

inserisci qui la descrizione dell'immagine

https://jsfiddle.net/kbp7jr7n/

Se cerchi il codice sopra, abbiamo Foocostruttore che ha il metodo identify()e Barcostruttore che ha speakmetodo. Creiamo due Baristanze b1e il b2cui tipo principale è Foo. Ora mentre chiamiamo il speakmetodo di Bar, siamo in grado di identificare chi sta chiamando il parlato tramite prototypecatena.

inserisci qui la descrizione dell'immagine

Barora ha tutti i metodi di Foocui sono definiti nella sua prototype. Analizziamo ulteriormente la comprensione di Object.prototypee Function.prototypee come sono correlati. Se cerchi il costruttore di Foo, Bare lo Objectsei Function constructor.

inserisci qui la descrizione dell'immagine

Il prototypedi Barè Foo, prototypedi Fooè Objecte se si osserva da vicino il prototypedi Fooè correlato Object.prototype.

inserisci qui la descrizione dell'immagine

Prima di chiudere, chiudiamo un piccolo codice qui per riassumere tutto quanto sopra . Stiamo usando l' instanceofoperatore qui per verificare se un objectha nella sua prototypecatena la prototypeproprietà di un constructorche di seguito riassume l'intero diagramma grande.

inserisci qui la descrizione dell'immagine

Spero che questa aggiunta sia qualche informazione, so che questo tipo di cose potrebbe essere grande da capire ... in parole semplici è solo oggetti collegati ad oggetti !!!!


22

qual è lo scopo esatto di questa proprietà ".prototype"?

L'interfaccia con le classi standard diventa estensibile. Ad esempio, stai usando la Arrayclasse e devi anche aggiungere un serializzatore personalizzato per tutti gli oggetti dell'array. Passeresti il ​​tempo a codificare una sottoclasse, o usi la composizione o ... La proprietà prototype risolve questo consentendo agli utenti di controllare il set esatto di membri / metodi disponibili per una classe.

Pensa ai prototipi come a un puntatore vtable aggiuntivo. Quando mancano alcuni membri della classe originale, il prototipo viene cercato in fase di esecuzione.


21

Potrebbe essere utile classificare le catene di prototipi in due categorie.

Considera il costruttore:

 function Person() {}

Il valore di Object.getPrototypeOf(Person)è una funzione. In effetti lo è Function.prototype. Poiché è Personstato creato come funzione, condivide lo stesso oggetto funzione prototipo che hanno tutte le funzioni. È lo stesso di Person.__proto__, ma quella proprietà non deve essere utilizzata. Ad ogni modo, con Object.getPrototypeOf(Person)te salite efficacemente la scala di quella che viene chiamata la catena del prototipo.

La catena verso l'alto si presenta così:

    PersonFunction.prototypeObject.prototype(punto finale)

È importante che questa catena di prototipi abbia poco a che fare con gli oggetti che Personpossono costruire . Quegli oggetti costruiti hanno la loro catena di prototipi e questa catena non può potenzialmente avere antenati simili a quelli sopra menzionati.

Prendi ad esempio questo oggetto:

var p = new Person();

p non ha una relazione prototipo-catena diretta con Person . La loro relazione è diversa. L'oggetto p ha una propria catena di prototipi. Usando Object.getPrototypeOf, troverai che la catena è la seguente:

    pPerson.prototypeObject.prototype(punto finale)

Non esiste alcun oggetto funzione in questa catena (anche se potrebbe essere).

Quindi Personsembra legato a due tipi di catene, che vivono la propria vita. Per "saltare" da una catena all'altra, usi:

  1. .prototype: passa dalla catena del costruttore alla catena dell'oggetto creato. Questa proprietà è quindi definita solo per gli oggetti funzione (poiché newpuò essere utilizzata solo per le funzioni).

  2. .constructor: passa dalla catena dell'oggetto creato alla catena del costruttore.

Ecco una presentazione visiva delle due catene di prototipi coinvolte, rappresentate come colonne:

inserisci qui la descrizione dell'immagine

Riassumere:

La prototypeproprietà non fornisce informazioni sulla catena prototipo del soggetto , ma sugli oggetti creati dal soggetto.

Non sorprende che il nome della proprietà prototypepossa creare confusione. Sarebbe stato forse più chiaro se questa proprietà fosse stata nominata prototypeOfConstructedInstanceso qualcosa del genere.

Puoi saltare avanti e indietro tra le due catene di prototipi:

Person.prototype.constructor === Person

Questa simmetria può essere spezzata assegnando esplicitamente un oggetto diverso a prototype proprietà (ne parleremo più avanti).

Crea una funzione, ottieni due oggetti

Person.prototypeè un oggetto che è stato creato contemporaneamente alla funzione Person. Ha Personcome costruttore, anche se quel costruttore non è stato ancora eseguito. Quindi vengono creati due oggetti contemporaneamente:

  1. La Personstessa funzione
  2. L'oggetto che fungerà da prototipo quando la funzione viene chiamata come costruttore

Entrambi sono oggetti, ma hanno ruoli diversi: l'oggetto funzione costruisce , mentre l'altro oggetto rappresenta il prototipo di qualsiasi oggetto che la funzione costruirà. L'oggetto prototipo diventerà il genitore dell'oggetto costruito nella sua catena prototipo.

Poiché una funzione è anche un oggetto, ha anche un suo genitore nella sua catena di prototipi, ma ricorda che queste due catene riguardano cose diverse.

Ecco alcune uguaglianze che potrebbero aiutare a cogliere il problema - tutte queste stampe true:

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);

Aggiunta di livelli alla catena di prototipi

Sebbene un oggetto prototipo venga creato quando si crea una funzione di costruzione, è possibile ignorare quell'oggetto e assegnare un altro oggetto che dovrebbe essere usato come prototipo per tutte le istanze successive create da quel costruttore.

Per esempio:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

Ora la catena di prototipi di t è un passo più lunga di quella di p :

    tpPerson.prototypeObject.prototype(punto finale)

L'altra catena di prototipi non è più lunga: Thiefe Personsono fratelli che condividono lo stesso genitore nella loro catena di prototipi:

    Person}
    Thief  } → Function.prototypeObject.prototype(punto finale)

Il grafico presentato in precedenza può quindi essere esteso a questo (l'originale Thief.prototypeviene lasciato fuori):

inserisci qui la descrizione dell'immagine

Le linee blu rappresentano catene prototipo, le altre linee colorate rappresentano altre relazioni:

  • tra un oggetto e il suo costruttore
  • tra un costruttore e l'oggetto prototipo che verrà utilizzato per costruire oggetti


16

Ho trovato utile spiegare la "catena di prototipi" come convenzione ricorsiva quando obj_n.prop_Xsi fa riferimento:

se obj_n.prop_Xnon esiste, controllareobj_n+1.prop_X doveobj_n+1 = obj_n.[[prototype]]

Se prop_Xfinalmente si trova l'oggetto k-esimo prototipo, allora

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

Puoi trovare un grafico della relazione degli oggetti Javascript in base alle loro proprietà qui:

grafico degli oggetti js

http://jsobjects.org


14

Quando un costruttore crea un oggetto, quell'oggetto fa implicitamente riferimento alla proprietà "prototipo" del costruttore allo scopo di risolvere i riferimenti di proprietà. La proprietà "prototype" del costruttore può essere referenziata dall'espressione di programma constructor.prototype e le proprietà aggiunte al prototipo di un oggetto sono condivise, tramite ereditarietà, da tutti gli oggetti che condividono il prototipo.


11

Ci sono due entità distinte ma correlate qui che devono essere spiegate:

  • La .prototypeproprietà delle funzioni.
  • La proprietà [[Prototype]][1] di tutti gli oggetti [2] .

Queste sono due cose diverse.

La [[Prototype]]proprietà:

Questa è una proprietà esistente su tutti [2] oggetti.

Ciò che è memorizzato qui è un altro oggetto, che, come oggetto stesso, ha un [[Prototype]]proprio che punta a un altro oggetto. L'altro oggetto ha un [[Prototype]]suo proprio. Questa storia continua fino a raggiungere l'oggetto prototipico che fornisce metodi accessibili su tutti gli oggetti (come .toString).

La [[Prototype]]proprietà fa parte di ciò che costituisce la [[Prototype]]catena. Questa catena di [[Prototype]]oggetti è ciò che viene esaminato quando, ad esempio, [[Get]]o [[Set]]vengono eseguite operazioni su un oggetto:

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain

La .prototypeproprietà:

Questa è una proprietà che si trova solo sulle funzioni. Utilizzando una funzione molto semplice:

function Bar(){};

La .prototypeproprietà contiene un oggetto a cui verrà assegnato b.[[Prototype]]quando lo fai var b = new Bar. Puoi facilmente esaminare questo:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true

Uno dei più importanti .prototypeè quello della Objectfunzione . Questo prototipo contiene l'oggetto prototipico contenuto in tutte le [[Prototype]]catene. Su di esso, sono definiti tutti i metodi disponibili per i nuovi oggetti:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))

Ora, poiché .prototypeè un oggetto, ha una [[Prototype]]proprietà. Quando non si esegue alcuna assegnazione a Function.prototype, .prototypei [[Prototype]]punti puntano all'oggetto prototipo ( Object.prototype). Questo viene eseguito automaticamente ogni volta che crei una nuova funzione.

In questo modo, ogni volta che fai new Bar;la catena di prototipi è impostata per te, ottieni tutto definito Bar.prototypee tutto definito Object.prototype:

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)

Quando si eseguono assegnazioni a Function.prototypetutto ciò che si sta facendo è estendere la catena di prototipi per includere un altro oggetto. È come un inserimento in un elenco collegato singolarmente.

Ciò altera sostanzialmente la [[Prototype]]catena consentendo alle proprietà definite sull'oggetto assegnato Function.prototypedi essere viste da qualsiasi oggetto creato dalla funzione.


[1: Questo non confonderà nessuno; reso disponibile tramite la __proto__proprietà in molte implementazioni.
[2]: Tutti tranne null.


10

Lascia che ti dica la mia comprensione dei prototipi. Non ho intenzione di confrontare l'eredità qui con altre lingue. Vorrei che le persone smettessero di confrontare le lingue e capissero semplicemente la lingua stessa. Comprendere i prototipi e l'eredità dei prototipi è così semplice, come ti mostrerò di seguito.

Il prototipo è come un modello, in base al quale si crea un prodotto. Il punto cruciale da capire è che quando si crea un oggetto usando un altro oggetto come prototipo, il collegamento tra il prototipo e il prodotto è duraturo. Per esempio:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Ogni oggetto contiene una proprietà interna chiamata [[prototype]], a cui può accedere la Object.getPrototypeOf()funzione. Object.create(model)crea un nuovo oggetto e imposta la sua proprietà [[prototype]] sul modello a oggetti . Quindi, quando lo fai Object.getPrototypeOf(product), otterrai il modello a oggetti .

Le proprietà del prodotto vengono gestite nel modo seguente:

  • Quando si accede a una proprietà per leggerne solo il valore, viene cercata nella catena dell'ambito. La ricerca della variabile inizia dal prodotto verso l' alto fino al suo prototipo. Se una tale variabile viene trovata nella ricerca, la ricerca viene interrotta proprio lì e il valore viene restituito. Se tale variabile non può essere trovata nella catena dell'ambito, viene restituito undefined.
  • Quando una proprietà viene scritta (modificata), la proprietà viene sempre scritta sull'oggetto prodotto . Se il prodotto non dispone già di tale proprietà, viene implicitamente creato e scritto.

Tale collegamento di oggetti mediante la proprietà prototype è chiamato ereditarietà prototipo. Ecco, è così semplice, d'accordo?


Non sempre scritto sul prodotto su incarico. Non stai chiarendo che i membri specifici dell'istanza devono essere inizializzati e i membri condivisi possono andare sul prototipo. Soprattutto quando si dispone di membri modificabili specifici dell'istanza: stackoverflow.com/questions/16063394/…
HMR

HMR: Nel tuo esempio nella tua risposta, ben.food.push ("Hamburger"); line altera la proprietà dell'oggetto prototipo a causa di quanto segue: 1.) Innanzitutto viene consultato ben.food e qualsiasi azione di ricerca cercherà semplicemente la catena dell'ambito. 2.) Viene eseguita la funzione push di quell'oggetto ben.food. Scrivendo mode nella mia risposta, intendo quando si imposta esplicitamente un valore su di esso, come in: ben.food = ['Idly']; Ciò creerà sempre una nuova proprietà (se non già presente) sull'oggetto prodotto e quindi gli assegnerà il valore.
Aravind,

HMR: Grazie per il tuo commento, mi ha fatto riflettere e testare la mia comprensione.
Aravind,

Quando si riassegna ben.food ombreggerà il membro alimentare a meno che il cibo non venga creato utilizzando Object.defineProperty, Object.defineProperties o Object.create con il secondo argomento (quindi non sempre). È anche possibile modificare il prototipo con (come appare) una nuova assegnazione quando si crea un setter getter. Quando si tratta di modelli di eredità, capisco che la funzione di costruzione è difficile da capire e ha alcuni problemi importanti, ma è bene se la capisci. L'ereditarietà in JavaScript non inizia e termina con l'impostazione di un prototipo, anche le inizializzazioni (costruttori) devono essere (ri) utilizzate.
HMR,

La tua risposta è buona nello spiegare il prototipo, ma potrebbe essere interpretata erroneamente semplificando l'ereditarietà in JavaScript e membri specifici dell'istanza. Sono state poste molte domande sul perché la mutazione di un membro prototipo su un'istanza influisca su altre istanze.
HMR,


10

Considera il seguente keyValueStoreoggetto:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Posso creare una nuova istanza di questo oggetto facendo questo:

kvs = keyValueStore.create();

Ogni istanza di questo oggetto avrebbe le seguenti proprietà pubbliche:

  • data
  • get
  • set
  • delete
  • getLength

Supponiamo ora di creare 100 istanze di questo keyValueStoreoggetto. Anche se get, set, delete, getLengthfarà la stessa cosa esatta per ciascuno di questi 100 casi, ogni istanza ha la propria copia di questa funzione.

Ora, immaginate se si potrebbe avere solo un singolo get, set, deletee getLengthla copia, e ogni istanza avrebbe riferimento che la stessa funzione. Questo sarebbe meglio per le prestazioni e richiedere meno memoria.

È qui che arrivano i prototipi. Un prototipo è un "progetto" di proprietà che viene ereditato ma non copiato dalle istanze. Quindi questo significa che esiste solo una volta in memoria per tutte le istanze di un oggetto ed è condiviso da tutte quelle istanze.

Ora, considera di keyValueStorenuovo l' oggetto. Potrei riscriverlo in questo modo:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Ciò corrisponde esattamente alla versione precedente keyValueStoredell'oggetto, tranne per il fatto che tutti i suoi metodi sono ora inseriti in un prototipo. Ciò significa che tutte e 100 le istanze condividono ora questi quattro metodi invece che ognuno di essi ha la propria copia.


9

Sommario:

  • Le funzioni sono oggetti in JavaScript e quindi possono avere proprietà
  • Le funzioni (costruttore) hanno sempre una proprietà prototipo
  • Quando una funzione viene utilizzata come costruttore con la newparola chiave, l'oggetto diventa prototipo. Un riferimento a questo prototipo può essere trovato sulla __proto__proprietà dell'oggetto appena creato.
  • Questa __proto__proprietà si riferisce alla prototypeproprietà della funzione di costruzione.

Esempio:

function Person (name) {
  this.name = name;
}

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.

Perché è utile:

Javascript ha un meccanismo quando si cercano proprietà su Oggetti che si chiama 'ereditarietà prototipale' , ecco cosa fa sostanzialmente:

  • Innanzitutto viene verificato se la proprietà si trova sull'oggetto stesso. In tal caso viene restituita questa proprietà.
  • Se la proprietà non si trova sull'oggetto stesso, "scalerà la protochain". Praticamente esamina l'oggetto a cui fa riferimento la proprietà proto . Qui verifica se la proprietà è disponibile sull'oggetto a cui fa riferimento proto
  • Se la proprietà non si trova sull'oggetto proto , salirà la catena proto fino all'oggetto Object.
  • Se non riesce a trovare la proprietà da nessuna parte sull'oggetto e sulla sua catena di prototipi, verrà restituito indefinito.

Per esempio:

function Person(name) {
  this.name = name;
}

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);

Aggiornare:

La __proto__proprietà è stata deprecata, sebbene sia implementata nella maggior parte dei browser moderni, un modo migliore per ottenere il riferimento dell'oggetto prototipo sarebbe:

Object.getPrototypeOf()


7

Mi piacciono sempre le analogie quando si tratta di capire questo tipo di cose. "L'eredità prototipica" è piuttosto confusa rispetto all'eredità dei bassi di classe secondo me, anche se i prototipi sono un paradigma molto più semplice. In effetti con i prototipi, non esiste davvero alcuna eredità, quindi il nome in sé e per sé fuorviante, è più un tipo di "delegazione".

Immagina questo ....

Sei al liceo e sei in classe e hai un quiz che è dovuto oggi, ma non hai una penna per compilare le tue risposte. Doh!

Sei seduto accanto al tuo amico Finnius, che potrebbe avere una penna. Chiedete, e si guarda intorno alla scrivania senza successo, ma invece di dire "Non ho una penna", è un buon amico che controlla con l'altro amico Derp se ha una penna. Derp ha davvero una penna di riserva e la restituisce a Finnius, che la passa a te per completare il tuo quiz. Derp ha affidato la penna a Finnius, che le ha delegato per l'uso.

Ciò che è importante qui è che Derp non ti dia la penna, poiché non hai una relazione diretta con lui.

Questo è un esempio semplificato di come funzionano i prototipi, in cui un albero di dati viene cercato per ciò che stai cercando.


3

un altro schema che mostra __proto__ , prototipo e relazioni del costruttore : inserisci qui la descrizione dell'immagine


1

È solo che hai già un oggetto con Object.newma non hai ancora un oggetto quando usi la sintassi del costruttore.


1

È importante capire che esiste una distinzione tra il prototipo di un oggetto (che è disponibile tramite Object.getPrototypeOf(obj)o tramite la __proto__proprietà deprecata ) e la prototypeproprietà sulle funzioni del costruttore. Il primo è la proprietà su ogni istanza e il secondo è la proprietà sul costruttore. Cioè, si Object.getPrototypeOf(new Foobar())riferisce allo stesso oggetto di Foobar.prototype.

Riferimento: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes


0

Il prototipo crea un nuovo oggetto clonando l' oggetto esistente . Quindi davvero quando pensiamo al prototipo possiamo davvero pensare alla clonazione o alla creazione di una copia di qualcosa invece di inventarla.

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.