JavaScript: Class.method vs. Class.prototype.method


499

Qual è la differenza tra le seguenti due dichiarazioni?

Class.method = function () { /* code */ }
Class.prototype.method = function () { /* code using this.values */ }

Va bene pensare alla prima istruzione come a una dichiarazione di un metodo statico e alla seconda istruzione come a una dichiarazione di un metodo di istanza?

Risposte:


696

Sì, la prima funzione non ha alcuna relazione con un'istanza di oggetto di quella funzione di costruzione , puoi considerarla come un "metodo statico" .

Nelle funzioni JavaScript sono oggetti di prima classe , il che significa che puoi trattarli come qualsiasi altro oggetto, in questo caso, stai solo aggiungendo una proprietà all'oggetto funzione .

La seconda funzione, mentre si estende il prototipo della funzione di costruzione, sarà disponibile per tutte le istanze di oggetto create con la newparola chiave e il contesto all'interno di quella funzione (la thisparola chiave) farà riferimento all'istanza dell'oggetto reale in cui la si chiama.

Considera questo esempio:

// constructor function
function MyClass () {
  var privateVariable; // private member only available within the constructor fn

  this.privilegedMethod = function () { // it can access private members
    //..
  };
}

// A 'static method', it's just like a normal function 
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};

MyClass.prototype.publicMethod = function () {
  // the 'this' keyword refers to the object instance
  // you can access only 'privileged' and 'public' members
};

var myObj = new MyClass(); // new object instance

myObj.publicMethod();
MyClass.staticMethod();

1
Ma perché Function.prototype.method == Function.method?
Raghavendra,

1
@Raghavendra non lo è
Zorgatone,

1
@Menda il tuo link è morto
Eugen Sunic il

19

Quando crei più di un'istanza di MyClass, avrai ancora solo un'istanza di publicMethod in memoria, ma in caso di privileggedMethod finirai per creare molte istanze e staticMethod non ha alcuna relazione con un'istanza di oggetto.

Ecco perché i prototipi risparmiano memoria.

Inoltre, se si modificano le proprietà dell'oggetto padre, se la proprietà corrispondente del figlio non è stata modificata, verrà aggiornata.


15

Per gli studenti visivi, quando si definisce la funzione senza .prototype

ExampleClass = function(){};
ExampleClass.method = function(customString){
             console.log((customString !== undefined)? 
                          customString : 
                          "called from func def.");}
ExampleClass.method(); // >> output: `called from func def.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
    // >> error! `someInstance.method is not a function`  

Con lo stesso codice, se .prototypeaggiunto,

ExampleClass.prototype.method = function(customString){
             console.log((customString !== undefined)? 
                          customString : 
                          "called from func def.");}
ExampleClass.method();  
      // > error! `ExampleClass.method is not a function.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
                 // > output: `Called from instance`

Per renderlo più chiaro,

ExampleClass = function(){};
ExampleClass.directM = function(){}  //M for method
ExampleClass.prototype.protoM = function(){}

var instanceOfExample = new ExampleClass();

ExampleClass.directM();      works
instanceOfExample.directM();   x Error!

ExampleClass.protoM();     x Error!
instanceOfExample.protoM();   works

**** Nota per l'esempio sopra, someInstance.method () non verrà eseguito in quanto,
ExampleClass.method () causa errori e l'esecuzione non può continuare.
Ma per motivi di illustrazione e facile comprensione, ho mantenuto questa sequenza. ****

Risultati generati da chrome developer console& Fare clic sul collegamento jsbin sopra per scorrere il codice. Attiva / disattiva la sezione commentata con +JS Bin

ctrl/


15

Sì, il primo è static methodanche chiamato class method, mentre il secondo è un instance method.

Considera i seguenti esempi, per capirlo in modo più dettagliato.

In ES5

function Person(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
}

Person.isPerson = function(obj) {
   return obj.constructor === Person;
}

Person.prototype.sayHi = function() {
   return "Hi " + this.firstName;
}

Nel codice sopra, isPersonè un metodo statico, mentre sayHiè un metodo di istanza di Person.

Di seguito, è come creare un oggetto dal Personcostruttore.

var aminu = new Person("Aminu", "Abubakar");

Utilizzando il metodo statico isPerson.

Person.isPerson(aminu); // will return true

Utilizzando il metodo dell'istanza sayHi.

aminu.sayHi(); // will return "Hi Aminu"

In ES6

class Person {
   constructor(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
   }

   static isPerson(obj) {
      return obj.constructor === Person;
   }

   sayHi() {
      return `Hi ${this.firstName}`;
   }
}

Guarda come è staticstata utilizzata la parola chiave per dichiarare il metodo statico isPerson.

Per creare un oggetto di Personclasse.

const aminu = new Person("Aminu", "Abubakar");

Utilizzando il metodo statico isPerson.

Person.isPerson(aminu); // will return true

Utilizzando il metodo dell'istanza sayHi.

aminu.sayHi(); // will return "Hi Aminu"

NOTA: entrambi gli esempi sono essenzialmente gli stessi, JavaScript rimane un linguaggio senza classi. L' classintroduzione in ES6 è principalmente uno zucchero sintattico rispetto al modello di ereditarietà basato sul prototipo esistente.


"In ES6" descrivi solo una sintassi di zucchero. Questo non è il modo "ES2015" (per favore tutti smettono di usare ES6 usano il termine corretto ES2015) per farlo. È semplicemente un altro modo di farlo e secondo me il modo sbagliato.
K - La tossicità in SO sta crescendo.

2
@KarlMorrison Aminu non ha scritto "un modo per farlo", l'hai appena scritto tu e ne hai fatto un'eccezione. Il tuo punto potrebbe essere equo su ES6 vs ES2015 ma nelle conversazioni le persone ricorrono spesso a una convenzione più breve per l'efficienza, quindi penso che rimuoverlo dalla scrittura non sia possibile o sicuramente consigliabile.
Wuliwong,

Grazie per la parte ES6 della tua risposta; questo chiarisce molto, specialmente se combinato con le 2 risposte "pubbliche + privilegiate" sopra. Sono comunque profondamente confuso dal tuo obj.constructor === Personessere trueesempio però ... Whaaaat? Come può un'istanza di classe costruire ===la classe stessa ...? (È come dire che un sottoinsieme di un set è il set stesso, ecc ...)
Andrew

Ohhh ... è tutto da dire poi che letteralmente, il costruttore è tutto ciò che una classe JS è davvero alla fine della giornata? Tutto il resto è o ammucchiato nel costruttore o totalmente un costrutto statico isolato dalla classe se non per nome / concetto (e come un "questo" implicito reso ovviamente disponibile)? (Quindi, quello che pensavo fosse un sottoinsieme del set non era in realtà un sottoinsieme.)
Andrew
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.