Oo JavaScript constructor pattern: neo-classical vs prototypal

Oglądałem rozmowę Douglasa Crockforda o dobrych częściach w Javascript i Moje oczy zostały otwarte. W pewnym momencie powiedział coś w stylu: "Javascript jest jedynym językiem, w którym dobrzy Programiści wierzą, że mogą go efektywnie używać bez uczenia się."Wtedy zdałem sobie sprawę, że jestem tym facetem.

W tym przemówieniu, wygłosił kilka stwierdzeń, które dla mnie były dość zaskakujące i wnikliwe. Na przykład JavaScript jest najważniejszym językiem programowania na świecie. Lub jest to najpopularniejszy język na świecie. I, że jest złamany na wiele poważnych sposobów.

Najbardziej zaskakujące dla mnie było stwierdzenie "nowe jest niebezpieczne". Już go nie używa. On też nie używa this.

Przedstawił ciekawy wzorzec dla konstruktora w Javascript, który pozwala na prywatne i publiczne zmienne członkowskie i nie opiera się na new, ani this. Wygląda to tak:

// neo-classical constructor
var container =  function(initialParam) {
    var instance = {}; // empty object 

    // private members
    var privateField_Value = 0;
    var privateField_Name = "default";

    var privateMethod_M1 = function (a,b,c) {
        // arbitrary
    }; 

    // initialParam is optional
    if (typeof initialParam !== "undefined") {
        privateField_Name= initialParam;
    }

    // public members
    instance.publicMethod = function(a, b, c) {
        // because of closures,
        // can call private methods or
        // access private fields here. 
    };

    instance.setValue = function(v) {
        privateField_Value = v;
    };

    instance.toString = function(){
        return "container(v='" + privateField_Value + "', n='" + privateField_Name + "')";
    };

    return instance;
}


// usage
var a = container("Wallaby");
WScript.echo(a.toString()); 
a.setValue(42);
WScript.echo(a.toString()); 

var b = container();
WScript.echo(b.toString()); 

EDIT : kod zaktualizowany, aby przełączyć się na nazwa klasy małymi literami.

Ten wzór ewoluował z wcześniejszych modeli użytkowych Crockforda .

pytanie: czy używasz tego rodzaju wzoru konstruktora? Czy to zrozumiałe? Masz lepszy?

Author: Cheeso, 2009-11-27

7 answers

Wygląda to jak wersja bez Singletona wzorca modułu , w którym prywatne zmienne mogą być symulowane poprzez wykorzystanie "zamknięć" JavaScript.

Lubię to ( trochę...). Ale tak naprawdę nie widzę korzyści w prywatnych zmiennych zrobionych w ten sposób, zwłaszcza gdy oznacza to, że wszelkie nowe metody dodane (po inicjalizacji) nie mają dostępu do prywatnych zmiennych.

Plus, nie korzysta z prototypowego modelu JavaScript. Wszystkie Twoje metody i właściwości muszą być inicjowane za każdym razem, gdy konstruktor jest wywoływany - nie dzieje się tak, jeśli masz metody zapisane w prototypie konstruktora. Faktem jest, że użycie konwencjonalnego wzoru konstruktora / prototypu jest znacznie szybsze! Czy naprawdę uważasz, że prywatne zmienne sprawiają, że hit wydajności jest tego wart?

Ten rodzaj modelu ma sens ze wzorem modułu, ponieważ jest inicjowany tylko raz( aby utworzyć pseudo-singleton), ale nie jestem pewien, czy ma to sens proszę.

Czy używasz tego typu wzoru konstruktora?

Nie, chociaż używam jego wariantu Singletona, wzoru modułu...
Czy uważasz to za zrozumiałe?

Tak, jest czytelny i całkiem przejrzysty, ale nie podoba mi się pomysł wrzucenia wszystkiego w taki konstruktor.

Masz lepszy?

Jeśli naprawdę potrzebujesz zmiennych prywatnych, trzymaj się ich za wszelką cenę. W przeciwnym razie wystarczy użyć konwencjonalny konstruktor / wzór prototypu (, chyba że podzielasz strach Crockforda przed new/this combo):

function Constructor(foo) {
    this.foo = foo;
    // ...
}

Constructor.prototype.method = function() { };

Inne podobne pytania dotyczące poglądów Douga na ten temat:

 16
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:51

Unikam tego wzoru, ponieważ większości ludzi trudniej go odczytać. Generalnie stosuję dwa podejścia:

  1. Jeśli mam tylko jeden z czegoś, to używam obiektów anonimowych:

    var MyObject = {
        myMethod: function() {
            // do something
        }
    };
    
  2. Dla więcej niż jednego czegoś używam standardowego dziedziczenia prototypowego javascript

    var MyClass = function() {
    };
    MyClass.prototype.myMethod = function() {
        // do something
    };
    
    var myObject = new MyClass();
    

(1) jest znacznie łatwiejszy do odczytania, zrozumienia i napisania. (2) jest bardziej wydajny, gdy istnieje wiele obiektów. Kod crockforda utworzy nową kopię funkcji wewnątrz konstruktora za każdym razem. Zamknięcie ma również minusy trudniejsze do debugowania.

Chociaż tracisz naprawdę prywatne zmienne, to prefixujesz członków rzekomo prywatnych z _ jako konwencją.

this jest to co prawda trudny problem w javascript, ale można go obejść używając .call i .apply, aby go poprawnie skonfigurować. Często używam również var self = this; do tworzenia zmiennej zamykającej, która będzie używana jako this wewnątrz funkcji zdefiniowanych w funkcji członka.

MyClass.prototype.myMethod = function() {
    var self = this;

    // Either
    function inner1() {
        this.member();
    }
    inner1.call(this);

    // Or
    function inner2() {
        self.member();
    }
    inner2();
};
 9
Author: Caleb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 18:23:59

Czy używasz tego typu wzoru konstruktora?

Nope

Czy uważasz to za zrozumiałe?
Tak, bardzo prosto.
Masz lepszy?

Nie oglądałem jeszcze rozmowy, ale wkrótce do niej dojdę. do tego czasu nie widzę niebezpieczeństwa używania new i this, A oto dlaczego:

Nie słysząc jego uwag, mogę tylko przypuszczać, że sugeruje trzymanie się z dala od takich rzeczy ze względu na naturę this, i to, jak jest podatna na zmiany w zależności od kontekstu, w którym dana metoda jest wykonywana (bezpośrednio na oryginalnym obiekcie lub jako wywołanie zwrotne, itp.). Jako pedagog może uczyć unikania tych słów kluczowych ze względu na demografię w dużej mierze nieświadomych i niedoświadczonych programistów, którzy zajmują się JavaScript bez uprzedniego grokking natury języka. Dla doświadczonych programistów, którzy doskonale znają jeśli chodzi o język, nie jestem przekonany, że należy unikać tej cechy języka, która daje mu niesamowitą elastyczność(która jest zupełnie inna niż unikanie rzeczy takich jak with). To wszystko, będę oglądał teraz.

W każdym razie, gdy nie używam jakiegoś frameworka, który obsługuje dziedziczenie automagic (jak dojo.declare), lub pisząc obiekt niezależny od frameworka, obecnie biorę następujące podejdźcie.

Definicja:

var SomeObject = function() {
    /* Private access */
    var privateMember = "I am a private member";
    var privateMethod = bindScope(this, function() {
        console.log(privateMember, this.publicMember);
    });

    /* Public access */
    this.publicMember = "I am a public member";

    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

Użycie :

var o = new SomeObject();
o.privateMethodWrapper();

Gdzie bindScope jest funkcją użytkową podobną do Dojo 's dojo.hitch lub Prototype' s Function.prototype.bind.

 7
Author: Justin Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 23:38:16

W jego książce nazywa się dziedziczenie funkcjonalne (strona 52). Jeszcze go nie używam. To zrozumiałe, jeśli nauczysz się javascript od Douglasa. Myślę, że nie ma lepszego podejścia. Ten jest dobry bo to:

  • Umożliwia programistom tworzenie prywatnych członków

  • Zabezpiecza prywatnych członków i eliminuje to W przeciwieństwie do udawania prywatności (prywatni członkowie zaczynają od _)

  • Sprawia, że dziedziczenie jest płynne i bez zbędnych kod

Ma jednak pewne wady. Możesz przeczytać więcej na ten temat tutaj: http://www.bolinfest.com/javascript/inheritance.php

 6
Author: Anton Kuzmin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 18:19:31

Czy używasz tego typu wzoru konstruktora?

Używałem tego wzoru wcześniej, gdy po raz pierwszy uczyłem się JavaScript i natknąłem się na literaturę Douglasa Crockforda.

Czy uważasz to za zrozumiałe?

Tak długo, jak rozumiesz zamknięcia, ta metoda jest jasna.

Masz lepszy?
To zależy od tego, co próbujesz osiągnąć. Jeśli próbujesz napisać bibliotekę, która będzie jak idiota dowód, jak to możliwe, wtedy mogę całkowicie zrozumieć użycie zmiennych prywatnych. Może to zapobiec przypadkowemu zakłóceniu integralności obiektu przez użytkowników. Tak, koszt w czasie może być nieco większy (około 3 razy większy), ale koszt czasu jest argumentem spornym, dopóki nie wpływa na Twoją aplikację. Zauważyłem, że test wspomniany w poprzednim poście (test ) nie uwzględnia czasu, jaki zajmuje dostęp do funkcji obiektu.

I ' ve found that metoda prototype / constructor zazwyczaj prowadzi do szybszego czasu budowy, tak, ale niekoniecznie oszczędza czas w odzyskiwaniu. Istnieje dodatkowy koszt korzystania z łańcucha prototypów w celu znalezienia funkcji w porównaniu do posiadania funkcji dołączonej bezpośrednio do używanego obiektu. Więc jeśli wywołujesz wiele funkcji prototypowych, a nie konstruujesz wielu obiektów, bardziej sensowne może być użycie wzoru Crockforda.

Zazwyczaj nie lubię używać zmiennych prywatnych, jeśli nie piszę mój kod dla dużej publiczności. Jeśli jestem z zespołem ludzi, którzy są zdolni do kodowania, mogę ogólnie zaufać, że będą wiedzieli, jak używać mojego kodu, jeśli sam koduję i komentuję. Następnie używam czegoś podobnego do wzoru Crockforda bez prywatnych członków / metod.

 1
Author: MillsJROSS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 20:44:44

Jeśli musimy tworzyć zawsze ten sam rodzaj obiektu, używam raczej prototypowy wzorzec konstruktora , ponieważ pozwala on na współdzielenie metod i właściwości poprzez automatyczną delegację Poprzez prototype chain .

Również możemy zachować Obiekty prywatne; spójrz na to podejście:

var ConstructorName = (function() { //IIFE
    'use strict';

    function privateMethod (args) {}

    function ConstructorName(args) {
        // enforces new (prevent 'this' be the global scope)
        if (!(this instanceof ConstructorName)) {
            return new ConstructorName(args);
        }
        // constructor body
    }

    // shared members (automatic delegation)
    ConstructorName.prototype.methodName = function(args) {
        // use private objects
    };

    return ConstructorName;
}());

Polecam przejrzeć tę odpowiedź, gdzie możemy znaleźć ciekawy sposób na stworzenie funkcji konstruktora: inny sposób tworzenia Javascript Obiekt

Jest to przykład, w którym można użyć podejścia konstruktora prototypowego: Jak utworzyć błąd niestandardowy w JavaScript?

 1
Author: jherax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:39

Myślę, że może to pomóc w dyskusji, aby zgłosić tutaj, co jest głównym punktem tego wzorca, jak wyjaśnił Douglas Crockford w swoim filmie prezentacyjnym" Advanced JavaScript".

Głównym celem jest uniknięcie użycia nowego operatora. Ponieważ gdy ktoś zapomni użyć nowego operatora podczas wywoływania funkcji konstruktora obiektów, wtedy elementy obiektów dodane do konstruktora wszystkie trafiają do globalnej przestrzeni nazw. Należy pamiętać, że w tym przypadku nie ma ostrzeżenia o czasie wykonywania lub błędu, który poinformuj o błędzie, globalna przestrzeń nazw po prostu zostaje zanieczyszczona. Okazało się, że ma to poważne konsekwencje dla bezpieczeństwa i jest źródłem wielu trudnych do zdiagnozowania błędów.

To jest główny punkt tego wzoru Powerconstructor.

Część prywatna / uprzywilejowana jest drugorzędna. Można to zrobić w inny sposób. Tak jest, że członkowie prototypu nie są używane.

HTH luKa

 0
Author: luKa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-07 20:13:43