2015-05-27 13 views
7

Wiem, że podobne pytania są podobne, ale chcę sprawdzić, czy odpowiedzi te są nadal aktualne, biorąc pod uwagę optymalizacje w nowych silnikach JavaScript.Funkcje wewnątrz konstruktora kontra prototypu

Moim zdaniem największą zaletą temat definiowania funkcji wewnątrz konstruktora jest to, że można łatwo uniknąć konieczności znać wartość „tego” słowa kluczowego:

var Person = function() { 
    var self = this; 
    self.firstName = null; 
    self.lastName = null; 
    self.fullName = function() { 
    return self.firstName + self.lastName; 
    }; 
}; 

Takie podejście jest zalecane przez nokaut Managing 'this'. Jest to ogromna zaleta, szczególnie gdy kod jest modyfikowany przez wielu programistów, ponieważ jest bardzo prosty w zrozumieniu i obsłudze.

Inne podejście byłoby użyć prototyp obiektu:

var Person = function() { 
    this.firstName = null; 
    this.lastName = null; 
}; 
Person.prototype.fullName = function() { 
    return this.firstName + this.lastName; 
}; 

W tym przypadku istnieją zalety wydajność, ponieważ funkcje obiekty będą tworzone raz. Jednak głównym problemem jest to, że obsługa tego słowa kluczowego może być skomplikowana. Powyższy przykład jest bardzo prosty, ale jeśli masz funkcje obsługi zdarzeń, dla wszystkich połączeń, wywołań jQuery each(), metod wywoływania z różnych kontekstów itp., Łatwo jest to zła wykorzystać.

Oczywiście, jeśli rozumiesz, jak działa "to" i wiemy, w jaki sposób wywoływane są metody, nie powinno być problemów. Jednak z mojego doświadczenia wynika, że ​​wymaga to czasu i jest podatny na błędy, zwłaszcza gdy kod jest tworzony przez wielu programistów.

Wiem, że nowe silniki JS, takie jak V8, stosują optymalizacje w przypadkach, w których deklarujesz funkcje wewnątrz konstruktora, tworząc ukryte klasy: How the V8 engine works?.

Moje pytanie brzmi, biorąc pod uwagę te optymalizacje wykonane przez nowe silniki JS i złożoność konieczności posługiwania się słowem kluczowym "to", czy nadal sensowne jest stosowanie podejścia opartego na prototypach? Co straciłbym, stosując podejście polegające na umieszczeniu wszystkiego w konstruktorze?

UPDATE 1:

ja po prostu nie mikro-odniesienia na Chrome (wersja 42). Tworzę obiekty 1M z funkcjami wewnątrz konstruktora i funkcjami w prototypie. Jest to bardzo prosty obiekt o dwóch zmiennych oraz trzech funkcji, a wyniki są tak:

Functions inside constructor: 1.91 seconds 
Functions in prototype: 1.10 seconds 

Brzmi nawet z tych optymalizacje w V8 to wciąż 73% szybciej. Jednak był to mikro-benchmark. Nie jestem pewien, czy to będzie duża różnica w aplikacjach świata rzeczywistego.

UPDATE 2:

Wziąłem też spojrzeć na zużycie pamięci i istnieją duże różnice, jak również. Dla funkcji wewnątrz konstruktorów:

Shallow size: 64,000,120 
Retained size: 336,001,128 

Dla funkcji prototype:

Shallow size: 40,000,000 
Retained size: 40,000,000 

Albo Optymalizacje ukryta klasy nie są tak dobre, czy brakuje mi coś o tym. Używam monomorficznego kodu (konstruktory bez argumentów) jak sugeruje V8, ale nie jestem pewien, czy robię coś nie tak.

UPDATE 3:

Oto link testu zrobiłem w przypadku ktoś może wskazać coś złego się tam: http://jsperf.com/dg-constructor-vs-prototype

+0

FWIW dodałem 3rd sprawy testowej „POJO”, która nie korzysta z prototypu lub „nowe” słowo kluczowe, po prostu tworzy obiekt z publicznych metod i zmiennych prywatnych. Jest porównywalny pod względem wydajności do przykładu konstruktora. Tak czy inaczej, prototypowa metoda jest konsekwentnie szybsza, aczkolwiek z dodatkową złożonością. http://jsperf.com/dg-constructor-vs-prototype/2 –

+0

Dzięki Scott. Tak, brzmi jak w tej chwili najbardziej efektywnym sposobem jest użycie prototypu. To wstyd, ponieważ uwielbiam prostotę przypisywania "tego" do "siebie" i upewniam się, że "ja" wskazuje na obiekt, zamiast myśleć o czym "to" wskazuje. – dgaviola

+0

Tak, całkowicie się zgadzam. W przypadku prostych obiektów (bez dziedziczenia) użycie "prototypu" jest niczym więcej niż mikrooptymalizacją, z tą wadą, że musi dokładnie zrozumieć i zarządzać użyciem "tego". –

Odpowiedz

3

ja wykonać szybki test. Jeśli zadeklarujesz funkcję w konstruktorze, dwie instancje obiektów mają różne instancje funkcji nawet po optymalizacji. Jednak z prototypem masz tylko jedną instancję funkcji, która wyjaśnia różnicę wydajności.

var Person = function() { 
 
     var self = this; 
 
     self.firstName = null; 
 
     self.lastName = null; 
 
     self.fullName = function() { 
 
      return self.firstName + self.lastName; 
 
     }; 
 
    }; 
 

 
    Person.prototype.fullName2 = function() { 
 
     return this.firstName + this.lastName; 
 
    }; 
 

 
    var a = new Person(); 
 
    var b = new Person(); 
 

 
    console.log(a.fullName == b.fullName); // returns false 
 
    console.log(a.fullName2 == b.fullName2); // returns true

+0

Tak, zaktualizowałem pytanie kilkoma testami, które zrobiłem. Czy nie powinno tworzyć jednej funkcji podczas korzystania z optymalizacji? Zastanawiam się, czy obiekty muszą być tworzone w jakiś szczególny sposób, aby skorzystać z tej optymalizacji. – dgaviola

+0

Wygląda na to, że nie ma sposobu na optymalizację. Mimo że v8 tworzy ukryte klasy, funkcje wewnątrz konstruktora są zmiennymi instancji. Jedynym sposobem obejścia tego problemu jest użycie prototypu i, jak "self", zdefiniowanie 'var pro = Person.prototype;'. Następnie zamiast 'self.fullName = function()' użyj 'pro.fullName = function()'. Przynajmniej kod będzie wyglądał podobnie :) W przypadku tego problemu można by zastosować coś takiego jak http://think-robot.com/2009/06/hitch-object-oriented-event-handlers-with-jquery/ dla jQuery . –

+0

Zaletą używania "siebie" jest to, że możesz go używać wszędzie, wiedząc, że wskaże obiekt. Używam obejść podobnych do tego, co mówisz, ale musisz pamiętać, aby to zrobić. Ale tak, prawdopodobnie nie ma innych wyborów do tej pory, aby być skutecznym. – dgaviola

Powiązane problemy