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

498

Jaka jest różnica między następującymi dwiema deklaracjami?

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

Czy można uznać pierwszą instrukcję za deklarację metody statycznej, a drugą instrukcję za deklarację metody instancji?

postrational
źródło

Odpowiedzi:

696

Tak, pierwsza funkcja nie ma związku z instancją obiektu tej funkcji konstruktora , można ją traktować jak „metodę statyczną” .

W JavaScript funkcje są pierwszorzędnymi obiektami, co oznacza, że ​​możesz traktować je jak każdy obiekt, w tym przypadku dodajesz tylko właściwość do obiektu funkcji .

Druga funkcja, w miarę rozszerzania prototypu funkcji konstruktora, będzie dostępna dla wszystkich instancji obiektów utworzonych za pomocą newsłowa kluczowego, a kontekst tej funkcji ( thissłowo kluczowe) będzie odnosił się do rzeczywistej instancji obiektu, w której ją wywołujesz.

Rozważ ten przykład:

// 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();
CMS
źródło
1
Ale dlaczego jest Function.prototype.method == Function.method?
Raghavendra
1
@Raghavendra to nie jest
Zorgatone
1
@Menda Twój link nie żyje
Eugen Sunic
19

Kiedy utworzysz więcej niż jedną instancję MyClass, nadal będziesz mieć tylko jedną instancję publicMethod w pamięci, ale w przypadku privilegedMethod skończysz tworzenie wielu instancji, a staticMethod nie będzie mieć związku z instancją obiektu.

Właśnie dlatego prototypy oszczędzają pamięć.

Ponadto, jeśli zmienisz właściwości obiektu nadrzędnego, to czy odpowiednia właściwość dziecka nie została zmieniona, zostanie zaktualizowana.

użytkownik2440156
źródło
15

Dla uczniów wizualnych, którzy definiują funkcję bez .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`  

Z tym samym kodem, jeśli .prototypejest dodany,

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`

Aby było to jaśniejsze,

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

**** Uwaga dla powyższego przykładu, niektóreInstance.method () nie zostanie wykonane, ponieważ
ExampleClass.method () powoduje błąd i wykonanie nie może być kontynuowane.
Ale dla ilustracji i łatwego zrozumienia zachowałem tę sekwencję. ****

Wyniki wygenerowane z chrome developer console& Kliknij powyższy link jsbin, aby przejść przez kod. Przełącz komentowaną sekcję za pomocą +JS Bin

ctrl/

SAm
źródło
15

Tak, pierwszy jest static methodrównież nazywany class method, podczas gdy drugi to instance method.

Rozważ następujące przykłady, aby zrozumieć to bardziej szczegółowo.

W 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;
}

W powyższym kodzie isPersonjest metoda statyczna, a sayHimetoda instancji Person.

Poniżej znajduje się sposób utworzenia obiektu z Personkonstruktora.

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

Za pomocą metody statycznej isPerson.

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

Za pomocą metody instancji sayHi.

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

W ES6

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

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

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

Zobacz, jak staticużyto słowa kluczowego do zadeklarowania metody statycznej isPerson.

Aby utworzyć obiekt Personklasy.

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

Za pomocą metody statycznej isPerson.

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

Za pomocą metody instancji sayHi.

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

UWAGA: oba przykłady są zasadniczo takie same, JavaScript pozostaje językiem bezklasowym. classWprowadzony w ES6 to przede wszystkim cukier syntaktyczny w stosunku do istniejącego prototypu na bazie modelu dziedziczenia.

Aminu Kano
źródło
„W ES6” opisujesz tylko cukier składniowy. To nie jest „ES2015” (proszę, żeby wszyscy przestali używać ES6, używali właściwego terminu ES2015). To po prostu inny sposób na zrobienie tego i moim zdaniem niewłaściwy sposób.
Karl Morrison,
2
@KarlMorrison Aminu nie napisał „sposobu robienia tego”, po prostu sam to napisałeś i zrobiłeś wyjątek. Twój pogląd może być sprawiedliwy w stosunku do ES6 vs. ES2015, ale w rozmowach ludzie często uciekają się do krótszej konwencji dotyczącej wydajności, więc myślę, że usunięcie go z pisania nie jest możliwe lub na pewno wskazane.
wuliwong
Dziękuję za część odpowiedzi dotyczącą ES6; to wiele wyjaśnia, szczególnie w połączeniu z powyższymi 2 odpowiedziami „publicznymi + uprzywilejowanymi”. Jestem jednak całkowicie zdezorientowany tym, że obj.constructor === Personjesteś trueprzykładem ... Coaaaat? W jaki sposób konstruktor instancji ===klasy może sam klasę ...? (To tak, jakby powiedzieć, że podzbiorem zbioru jest sam zbiór itp.)
Andrew
Ohhh ... czy to wszystko znaczy, że dosłownie, konstruktor jest wszystkim, czym naprawdę jest klasa JS pod koniec dnia? Cała reszta jest albo ułożona w konstruktorze, albo całkowicie w konstrukcji statycznej, odizolowanej od klasy, z wyjątkiem nazwy / pojęcia (i jak implikowane „to” jest oczywiście dostępne)? (Tak więc to, co uważałem za podzbiór zestawu, nie było tak naprawdę podzbiorem).
Andrew