Domanda Come funziona JavaScript .prototype?


Non lo sono in 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(); // 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

Ricordo una lunga discussione che ho avuto con le persone un po 'indietro (non sono esattamente sicuro di quello che sto facendo) ma, a quanto 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 questo .prototype proprietà in JavaScript? Come si relaziona all'istanziazione di oggetti?


modificare

Queste scivoli mi ha davvero aiutato molto a capire questo argomento.


1855
2018-02-21 12:31


origine


risposte:


Ogni oggetto JavaScript ha una proprietà interna chiamata [[Prototipo]]. Se cerchi una proprietà via obj.propName o obj['propName'] e l'oggetto non ha una tale proprietà - che può essere controllata tramite obj.hasOwnProperty('propName') - il runtime cerca invece la proprietà nell'oggetto a cui fa riferimento [[Prototype]]. Se anche l'oggetto prototipo non ha tale proprietà, il suo prototipo viene controllato a turno, percorrendo così l'oggetto originale prototipo a catena fino a quando non viene trovata una partita o viene raggiunta la sua fine.

Alcune implementazioni di JavaScript consentono l'accesso diretto alla proprietà [[Prototype]], ad esempio tramite una proprietà non standard denominata __proto__. In generale, è possibile impostare il prototipo di un oggetto solo durante la creazione dell'oggetto: se si crea un nuovo oggetto tramite new Func(), la proprietà [[Prototype]] dell'oggetto sarà impostata sull'oggetto a cui fa riferimento Func.prototype.

Ciò consente di simulare classi in JavaScript, sebbene il sistema di ereditarietà di JavaScript sia - come abbiamo visto - prototipico e non basato su classi:

Basti pensare alle funzioni del costruttore come classi e alle proprietà del prototipo (cioè dell'oggetto a cui fa riferimento la funzione del costruttore prototype proprietà) come membri condivisi, ovvero membri uguali per ogni istanza. Nei sistemi basati su classi, i metodi vengono implementati allo stesso modo per ogni istanza, quindi i metodi vengono normalmente aggiunti al prototipo, mentre i campi di un oggetto sono specifici dell'istanza e quindi aggiunti all'oggetto stesso durante la costruzione.


925
2018-02-21 13:33



In una lingua che implementa l'ereditarietà classica come Java, C # o C ++ si inizia creando una classe - un modello per i propri oggetti - e quindi è possibile creare nuovi oggetti da quella classe o estendere la classe, definendo una nuova classe che aumenta la lezione originale

In JavaScript si crea per la prima volta un oggetto (non esiste un concetto di classe), quindi è possibile aumentare il proprio 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 poi 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());

var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
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);

1755
2018-01-24 03:42



Svolgo un ruolo di insegnante di JavaScript e il concetto di prototipo è sempre stato un argomento controverso da trattare quando insegno. Mi ci è voluto un po 'per trovare un buon metodo per chiarire il concetto, e ora in questo testo cercherò di spiegare come funziona JavaScript .prototype.


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

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 prototipo.

1- In che modo funzionano le funzioni JavaScript:

Per fare il primo passo dobbiamo capire come funzionano le funzioni JavaScript, come una funzione di classe this parola chiave in esso o semplicemente come una funzione regolare con i suoi argomenti, cosa fa e cosa restituisce.

Diciamo che vogliamo creare un Person modello a oggetti. ma in questo passo ci proverò fare la stessa cosa esatta senza usare prototype e new parola chiave.

Quindi in questo passaggio functions, objects e this parola chiave, sono tutto ciò che abbiamo.

La prima domanda sarebbe Come this la parola chiave potrebbe essere utile senza usare new parola 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 new parola chiave come potremmo usare queste funzioni Quindi JavaScript ha 3 diversi modi per farlo:

un. il primo modo è semplicemente chiamare la funzione come una funzione regolare:

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

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

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

-La via più facile per fare questo è modificare il vuoto person oggetto, come:

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

in questo modo possiamo chiamarli come:

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

e ora il person l'oggetto è come:

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

-L'altro modo per allegare una proprietà a un oggetto sta usando il prototype di quell'oggetto che può essere trovato in qualsiasi oggetto JavaScript con il nome di __proto__e ho cercato di spiegarlo un po 'sulla parte sommaria. Quindi potremmo ottenere il risultato simile facendo:

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

Ma in questo modo ciò che stiamo facendo è modificare il Object.prototype, perché ogni volta che creiamo un oggetto JavaScript usando 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 frammento di codice precedente, tutti gli oggetti JavaScript sarebbero stati modificati, non una buona pratica. Quindi quale potrebbe essere la pratica migliore ora:

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

e ora altri oggetti sono in pace, ma non sembra ancora una buona pratica. Quindi abbiamo ancora una soluzione in più, ma per usare questa soluzione dovremmo tornare a quella linea di codice dove person oggetto è stato creato (var person = {};) quindi cambialo come:

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

ciò che fa è creare un nuovo JavaScript Object e allega il propertiesObject al __proto__ attributo. Quindi per essere sicuri di poter fare:

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

Ma il punto delicato qui è che hai accesso a tutte le proprietà definite in __proto__ al primo livello del person oggetto (leggi la parte di riepilogo per maggiori dettagli).


come vedi usando uno di questi due modi this punterebbe esattamente al person oggetto.

c. JavaScript ha un altro modo per fornire la funzione this, che sta usando chiamata o applicare per invocare la funzione.

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

e

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

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 passaggi iniziali importanti per capire la funzionalità .prototype.


2- Come fa il new parola chiave funziona?

questo è il secondo passo per capire il .prototype funzionalità.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 prendere tutti i passaggi che JavaScript prende, senza usare il new parola chiave e prototype, quando usi new parola chiave. così quando lo facciamo new Person("George"), Person la funzione funge da costruttore, ecco cosa fa JavaScript, uno per uno:

un. prima di tutto rende un oggetto vuoto, in pratica un hash vuoto come:

var newObject = {};

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

noi abbiamo my_person_prototype qui simile all'oggetto prototipo.

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

Non è il modo in cui JavaScript in realtà allega le proprietà definite nel prototipo. Il modo reale è legato al concetto di catena prototipo.


un. & b. Invece di questi due passaggi puoi ottenere lo stesso risultato 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 il getName funzione nel nostro my_person_prototype:

newObject.getName();

c. poi dà quell'oggetto al costruttore,

possiamo farlo con il nostro esempio come:

Person.call(newObject, "George");

o

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

quindi il costruttore può fare tutto ciò 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:     Oggetto {nome: "George"}


Sommario:

Fondamentalmente, quando usi il nuovo parola chiave su una funzione, la stai chiamando e quella funzione funge da costruttore, quindi quando dici:

new FunctionName()

JavaScript crea internamente un oggetto, un hash vuoto e quindi assegna quell'oggetto al costruttore, quindi il costruttore può fare tutto ciò 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 usato l'istruzione return nella tua funzione o se hai messo un return undefined; alla fine del tuo corpo di funzione.

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

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

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

The prototype chain

In altre parole, quando hai una proprietà prototipo su una funzione e ne chiami una nuova, dopo che JavaScript ha terminato di guardare l'oggetto appena creato per le proprietà, andrà a esaminare le funzioni della funzione .prototype ed inoltre è possibile che questo oggetto abbia il suo prototipo interno. e così via.


162
2018-02-13 19:32



prototype ti permette di fare lezioni. se non lo usi prototype poi diventa statico.

Ecco un breve esempio.

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

Nel caso precedente, hai un test di chiamata con funzione statica. Questa funzione è accessibile solo da obj.test dove puoi immaginare obj come 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 può ora essere istanziata. Possono esistere più istanze di obj e tutte hanno il test funzione.

Quanto sopra è la mia comprensione. Sto facendo un wiki della comunità, quindi le persone possono correggermi se sbaglio.


66
2017-11-07 09:48



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

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance *[[protytype]]* and <code>prototype</code> property of function objects

è un grafico chiaro per mostrare JavaScript Inheritance di Prototype Chain

e

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

questo contiene un esempio con codice e diversi diagrammi.

la catena del prototipo ricade infine su Object.prototype.

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

Spero che sia anche utile per te capire la catena del prototipo JavaScript.


59
2018-05-26 20:40



I sette Koan del prototipo

Mentre Ciro San scendeva sul monte Fire Fox dopo una profonda meditazione, la sua mente era chiara e pacifica.

La sua mano tuttavia era inquieta 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 attraverso il __proto__ proprietà come estensione. ES6 ora cita alcuni requisiti opzionali per __proto__.


1) Questi concetti esistono per rispondere alla domanda:

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

Intuitivamente, l'ereditarietà classica dovrebbe influenzare la ricerca delle proprietà.


2)

  • __proto__ è usato per il punto . ricerca della proprietà come in obj.property.
  • .prototype è non utilizzato per la ricerca direttamente, solo indirettamente come determina __proto__ alla creazione di oggetti con new.

L'ordine di ricerca è:

  • obj proprietà aggiunte con obj.p = ... o Object.defineProperty(obj, ...)
  • proprietà di obj.__proto__
  • proprietà di obj.__proto__.__proto__, e così via
  • se alcuni __proto__ è null, ritorno undefined.

Questo è il cosiddetto catena prototipo.

Puoi evitare . ricerca con obj.hasOwnProperty('key') e Object.getOwnPropertyNames(f)


3) Ci sono due modi principali per impostare obj.__proto__:

  • new:

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

    poi new ha impostato:

    f.__proto__ === F.prototype
    

    Questo dov'è .prototype si abitua

  • Object.create:

     f = Object.create(proto)
    

    imposta:

    f.__proto__ === proto
    

4) Il codice:

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

Corrisponde allo schema seguente:

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

Questo diagramma mostra molti nodi di oggetti predefiniti per lingua: null, Object, Object.prototype, Function e Function.prototype. Le nostre 2 linee di codice sono state create solo f, F e F.prototype.


5)  .constructor viene normalmente da F.prototype tramite la . consultare:

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 fa il . ricerca come:

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

Il risultato f.constructor == F è intuitivamente corretto, dal momento che F è usato per costruire f, per esempio. imposta campi, proprio come nei classici linguaggi OOP.


6) La sintassi dell'ereditarietà classica può essere ottenuta manipolando catene di prototipi.

ES6 aggiunge il class e extends parole chiave, che sono semplicemente zucchero di sintassi per una possibile follia di manipolazione del prototipo.

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.
(new C(1)).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:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype

57
2018-06-18 19:48



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

object [[Prototype]] -> anotherObject

Nel javascript tradizionale, l'oggetto collegato è il prototype proprietà di una funzione:

object [[Prototype]] -> aFunction.prototype

Alcuni ambienti espongono [[Prototipo]] come __proto__:

anObject.__proto__ === anotherObject

Si crea il collegamento [[Prototipo]] 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;

UN new la dichiarazione non mostra il target di collegamento (Object.prototype) stesso; invece il target è implicito dal costruttore (Object).

Ricorda:

  • Ogni oggetto ha un collegamento, [[Prototipo]], a volte esposto come __proto__.
  • Ogni funzione ha un prototype proprietà.
  • Oggetti creati con new sono collegati al prototype proprietà del loro costruttore.
  • Se una funzione non viene mai utilizzata come costruttore, la sua prototype la proprietà andrà inutilizzata.
  • Se non hai bisogno di un costruttore, usa Object.create invece di new.

33
2018-02-21 12:41