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.