Ostatnio przy projekcie nad którym aktualnie pracuje pojawiła się dyskusja dotycząca ujednolicenia sposobu twozenia klas i obiektów. Ponieważ pracujemy nad pubicznie dostepna biblioteka zastanawialismy sie czy dażyć w strone minimalizacji kodu  czy w strone szybkości i mniejszego zużycia pamięci. Mianowicie czy tworzyć obiekty funkcjonalnie:

//konstruktor
myObject = function(){
   //prywatna zmienna
   var privateVar;
   //prywatna funkcja
   var privateFunction = function(){
      return ++privateVarr;
   }
   //publiczna funkcja
   this.publicFunction = function(){
      privateVar = 10;
      return privateFunction();
   }
}

czy prototypowo:

myObject = function(){
    //konstruktor
    myObjectClass = function(){
        //prywatna zmienna
        var privateVar;
        //metoda uprzywilejowana
        this.setVar = function(value){
             privateVar = value;
        };

        this.getVar = function(){
             return privateVar;
        };
    };

    //publiczne zmienne oraz funkcje
    myObjectClass.prototype = {
         publicFunction1:function(){
             return this.getVar() + 1;
         },
         publicFunction2:function(){
             //do something
         }
    };
    return myObjectClass;
}();

Do kompresji oraz obfuskacji kodu można wykorzystac compiler Yahoo lub Google closure compiler. Na czym polega wybór? Przy pierwszym funkcjonalnym podejściu wszystkie prywatne zmienne w obiekcie (oprócz tych oznaczonych poprzez this) przy obfuskacji zostaną zamienione odpowiednio na krótsze formy np. var A,B. w porównaniu do drugiego podejscią kod jest mniejszy o około 30 do 40% procent (jeśli korzystamy ze miennych prywatnych które są widoczne w obrębie wszystkich funkcji obiektu). Również mamy możliwość tworzenia zmiennych oraz funkcji prywatnych tworząc np biblioteke można udostępnić tylko te funkcje z których użytkownik powinien korzystać.

Natomiast przy podejściu prototypowym kod niestety jest dużo większy ponieważ obfuskacja działa tylko w obrębie poszczególnych funkcji oraz konstruktora. ‘Compiler’ nie możę zamienić żadnej zmiennej która jest oznaczona przez prototype lub this na krótszą formę ponieważ są one dostępne publicznie. Utrudnione jest również tworzenie zmiennych prywatnych ponieważ można je tworzyć tylko w obrębie konstruktora i dodatkowo aby były dostępne w rozszerzeniu obiektu przez prototype trzeba tworzyć funkcje uprzywilejowane aby uzyskać do nich dostęp. Dlatego też przy tym podejsciu zazwyczaj nie korzysta się z prywatnych zmiennych i wszystkie tworzy się jako publiczne (które niestety nie podlegaja minifikacji). Istotna zaletą tego podejscia jednak jest zmniejszenie zużycia pamięci oraz większa szybkosć działania szczegolnie przy tworzeniu większej ilości obiektów.

Tak naprawde istotna w tych podejściach jest różnica pomiędzy this a prototype. Zmienne oraz funkcje tworzone przy pomocy this w obiekcie są tworzone w pamięci na nowo przy każdej inicializaji obiektu (new myObject()), co za tym idzie np. jeśli tworzymy obiekt który posiada 3 funkcje utoworzone przez this prowadzi to tego że to gdy zainicjalizujemy ten obiekt np. 20 razy to w pamięci zostanie utworzoncyh 60 funkcji (20 * 3). W przeciwieńswtie do this przy użyciu prototype rozszerzamy prototyp obiektu więc przy tworzeniu 20 obiektów tym sposobem w pamięci dalej pozostaną tylko 3 funkcje i każdy obiekt będzie się do nich odwoływał. Oszczędzamy na zużyciu pamięci oraz na czasie tworzenia funkcji.

Które podejscie jest lepsze? tak naprawde to zależy od naszej aplikacji osobiście uważam że zasada powinna być taka: mało incjalizacji obiektów podejscie pierwsze (kod świetnie się kompresuje), dużo inicjalizacji obiektów np. w pętlach to lepiej skorzystać z podjeścia prototypowego, wszystko zależy od architektury naszej aplikacji.

Komentarzy: 3


  1. guest on 11 sty 2011

    Pytanie:) gdzie mogę jeszcze znaleźć informacje nt wydajności js podczas stosowania różnych konstrukcji językowych ( np wspomniane różnice ‘this a prototype’ )
    , jak lepiej incjalizować funkcje,obiekty, co robić a czego nie?

  2. Patryk yarpo Jar on 07 maj 2011

    @quest: Może Ci się przyda: http://www.yarpo.pl/2011/01/11/tworzenie-obiektow-w-js/

    A jeśli chodzi o treść wpisu, to czy takie rozwiązanie pośrednie nie dałoby porządanego rezultatu?
    myObject = function(){

    //konstruktor
    myObjectClass = function(){
    //prywatna zmienna
    var privateVar;
    //metoda uprzywilejowana
    this.setVar = function(value){
    privateVar = value;
    };

    this.getVar = function(){
    return privateVar;
    };
    };

    //publiczne zmienne oraz funkcje
    myObjectClass.prototype = function() {
    // zmienne prywatne
    return {
    publicFunction1:function(){
    return this.getVar() + 1;
    },
    publicFunction2:function(){
    //do something
    }
    };
    };
    return myObjectClass;
    }();

    Nie wiem, jak z wydajnością. Mamy jednak zmienne prywatne i używamy prototypów :)

  3. Radek on 20 lip 2011

    Tak zmienne te beda prywatne, ale niestety statyczne (z tego tez czesto sie kozysta), kazda instancja nowego obiektu bedzie miala dostep do tej samej zmiennej poniewaz funckcja ktora przypisuje do prototypu bedzie wywolana tylko raz (nie dla kazdego nowego obiektu). Niestety przy prototypach prywatne zmienne musza byc w konstruktorze i musza miec metody uprzywilejowane.

    Jesli chodzi o performance roznice miedzy this a prototype to zapostuje stronke jak odnajde, ale generalnie to jest tak ze przy uzyciu this funkcja tworzona jest przy kazdej inicjalizacji obiektu a jesli chodzo prototyp to jest umieszczona raz w pamieci i jest wywolywana w kontekscie kazdego nowego obieku. Mozesz sprobowac duza petle zrobic i poinicjalizowac obiekty moze to nie jest idealne mierzenie performance-u ale powinno wyjsc ze this jest troche wolniejszy i ma duzo wieksze zuzycie pamieci.


Skomentuj