2012-01-02 10 views
8

Hullo! To jest moje pierwsze pytanie!Wzorzec modułu JavaScript - chronione elementy?

Eksperymentuję z modelem modułu promowanym przez Douga Crockforda i innych. Zasadniczo bardzo zadowolony z tego do tej pory, ale nie jestem pewien co do najlepszego sposobu radzenia sobie z pewnym modelem dziedziczenia.

Mam to wszystko sprowadza się do przypadku gołymi kościami przy użyciu kota i ssaka, chociaż moim prawdziwym zamiarem jest tworzenie obiektów do gry opartej na kafelkach na płótnie.

Ale tutaj jest przypadek moje nagie kości „zwierzęta” za pomocą wpisu Przeglądarka:

var ZOO = ZOO || {}; 
// 
ZOO.mammal = function() { 
    "use strict"; 
    var voice = "squeak.mp3", // default mammal sound 
     utter = function() { 
     window.alert(this.voice); 
     }; 
// 
    // public interface 
    return { 
     utter: utter, 
     voice: voice 
    }; 
}; 
// 
ZOO.cat = function() { 
    "use strict"; 
    // hook up ancestor 
    var thisCat = ZOO.mammal(); 
    thisCat.voice = "miaw.mp3"; 
    return thisCat; 
}; 
// 
var felix = ZOO.cat(); 
felix.utter(); 

Co przeszkadza mi o tego podejścia jest to, że musiałem zrobić voice własność publiczną tak, że kot może go modyfikować.

Czego naprawdę chcę jest coś takiego jak „chronione” widzialności (z Java, ActionScript etc.), tak że cat może modyfikować voice bez nikogo z dostępem do felix mogąc go zmodyfikować.

Czy istnieje rozwiązanie?

Odpowiedz

14

Można symulować chronioną widoczność (widoczną dla siebie i obiektów podrzędnych), przekazując pusty obiekt do bazy "klasy", aby służył jako repozytorium dla chronionych właściwości. Umożliwi to udostępnianie właściwości za pośrednictwem łańcucha dziedziczenia bez podawania ich publicznie.

var ZOO = ZOO || {}; 

ZOO.mammal = function (protectedInfo) { 
    "use strict"; 
    protectedInfo = protectedInfo || {}; 
    protectedInfo.voice = "squeak.mp3"; 

    // public interface 
    return { 
     utter: function() { 
     alert(protectedInfo.voice); 
     } 
    }; 
}; 

ZOO.cat = function() { 
    "use strict"; 

    var protectedInfo = {}; 
    // hook up ancestor 
    var thisCat = ZOO.mammal(protectedInfo); 

    protectedInfo.voice = "miaw.mp3"; 
    return thisCat; 
}; 

Oto live demo

1

Sidesteping non-odpowiedź:

Istnieje kilka sposobów, aby uzyskać rodzaj chronionych obiektów w JavaScript, ale nie zawsze są one bardzo idiomatyczne. Gdybym był tobą, najpierw mocno rozważyć albo

  1. Korzystanie konwencja własności publicznej poprzedza się znakiem podkreślenia (np .: _voice) dla oznaczenia prywatności. Jest bardzo prosty i jest standardem wśród dynamicznych języków.

  2. Szukaj alternatywnego rozwiązania bez dziedziczenia. Dziedziczenie często komplikuje i paruje wiele rzeczy, stąd stara mantra "preferuj kompozycję przez dziedziczenie". Javascript posiada wiele funkcji, takich jak kaczka wpisywanie i funkcja wyższego rzędu, które często pozwalają uniknąć używania dziedziczenia w sytuacjach, gdzie byś normalnie potrzebne w Javie

+0

Myślę, że Douglas Crockford nie zgadza się z tobą: "Jedną słabością schematów dziedziczenia, które do tej pory widzieliśmy, jest to, że nie mamy prywatności. Wszystkie właściwości obiektu są widoczne. Nie otrzymujemy prywatnych zmiennych ani prywatnych metod. Czasami to nie ma znaczenia, ale czasami ma to ogromne znaczenie. W frustracji niektórzy niedoinformowani programiści przyjęli schemat udawania prywatności. Jeśli mają własność, którą chcą uczynić prywatnym, nadają jej dziwną nazwę ... Na szczęście mamy znacznie lepszą alternatywę w zastosowaniu wzoru modułowego. "[Crockford 52] –

+0

Udawanie prywatności to nie tylko" zrodzony z frustracji "- jest bardzo prosty i prosty, a także dobrze współpracuje z dziedziczonym prototypem dziedziczeniem JS oraz takimi obiektami jak klonowanie i refleksja obiektów (więc nie odsuńmy go jeszcze). W każdym razie, prawdziwym powodem, dla którego napisałem odpowiedź, było wskazanie, że nie koniecznie * potrzebujesz * chronionych członków (a może nawet dziedziczenia w pierwszej kolejności). – hugomg

+0

To prawda - to uniemożliwia używanie prototypowego dziedziczenia. –

1

Istnieje obejście symulować członków chronione, gdzie można podać do wiadomości publicznej tych członków przez jakiś czas, a potem ponownie je sprywatyzujesz. Nie jestem wielkim fanem tego, ale jest to "rozwiązanie".

Ja tylko cytuje z tego SitePoint article:

Dodawanie Chronione Członkowie

Dzielenie skrypt do wielu modułów jest powszechną praktyką i wygodne .Znacznie ułatwia to zarządzanie dużymi kodami i pozwala na zaoszczędzenie przepustowości, gdy moduły nie zawsze są wymagane.

Ale co, jeśli chcemy udostępniać dane między różnymi modułami? Jeśli udostępnimy te dane publicznie, stracimy korzyści z prywatności, ale jeśli udostępnimy je jako prywatne, będą dostępne tylko dla jednego modułu. To, czego naprawdę potrzebujemy, to prywatni członkowie, którzy są chronieni.

JavaScript nie ma członków objętych ochroną jako takich, ale możemy skutecznie je tworzyć, nadając tym samym czasowo publiczne. Aby osiągnąć tego, pozwól mi najpierw wprowadzić do dwóch kluczowych funkcji - rozszerzenie i sprywatyzować - które będziemy definiują jako część funkcji użyteczności obiektu:

var utils = { 
    extend : function(root, props) { 
    for(var key in props) { 
     if(props.hasOwnProperty(key)) { 
     root[key] = props[key]; 
     } 
    } return root; 
    }, 
    privatise : function(root, prop) { 
    var data = root[prop]; 
    try { delete root[prop]; } catch(ex) { root[prop] = null; } 
    return data; 
    } 
}; 

Funkcja przedłużyć po prostu dodaje nowe właściwości do obiekt, a funkcja prywatyzacji kopiuje właściwość, a następnie usuwa oryginał. Możemy użyć rozszerzenia w jednym module, aby utworzyć publiczne odwołanie do prywatnej zmiennej , a następnie użyć prywatyzacji w innym module, aby skopiować ją z powrotem do zmienną prywatną i usunąć publiczną referencję.

Oto przykład pierwszego modułu, który ma dwa chronione elementy (w tym sam obiekt utils) i jeden element publiczny. Aby utrzymać przykład kodu skrócie funkcje użytkowe są tylko puste muszle, ale byłyby one identyczne z funkcjami Pokazałem ci chwilą:

var MyModule = (function() { 
    var myProtectedData = 909; 
    var utils = { 
    extend : function(root, props) { }, 
    privatise : function(root, prop) { } 
    }; 
    this.myPublicData = 42; 
    return utils.extend(this, { myProtectedData : myProtectedData, utils : utils }); 
})(); 

Można zobaczyć, jak używamy wariantu wzoru modułu ujawniającego, , aby zwracać nie tylko członków publicznych, ale także chronionych członków jako dobrze oznaczoną jako . W tym momencie mamy trzech publicznych członków: MyModule.myProtectedData, MyModule.utils i MyModule.myPublicData.

Teraz oto przykład z ostatniego modułu, który wykorzystuje sprywatyzować funkcji do kopiowania określonych członków publicznych do prywatnych zmiennych, a następnie usunąć ich referencje publicznych:

var MyModule = (function() { 
    var myProtectedData = this.utils.privatise(this, 'myProtectedData'); 
    var utils = this.utils.privatise(this, 'utils'); 
    return this; 
}).apply(MyModule); 

A kiedyś, że zrobił chroniony członkowie są zablokowani wewnątrz swoich obiektów , prywatnie dostępnych dla obu modułów, ale nie są już dostępne z zewnątrz.

Należy zauważyć, że funkcja prywatyzacji polega na posiadaniu oddzielnych argumentów dla obiektu i klucza własności, ponieważ obiekty w kodzie JavaScript są przekazywane jako odniesienie . Zatem root jest odwołaniem do MyModule, a gdy usuniemy z niego właściwość określoną przez klucz, usuwamy tę właściwość z obiektu, do którego się odwołujemy.

Ale gdyby to było tak:

privatise : function(root) { 
    var data = root; 
    try { delete root; } catch(ex) { root = null; } return data; 
} 

i nazwał tak:

var myProtectedData = this.utils.privatise(this.myProtectedData); 

Następnie członkowie publiczne nie zostałyby usunięte - funkcja będzie prostu usunąć odniesienie, a nie właściwość, do której się odnosi.

Konstrukcja catch ... catch jest również niezbędna w starszych wersjach IE, , w których usuwanie nie jest obsługiwane. W takim przypadku unieważniamy publiczną właściwość, zamiast ją usuwać, co oczywiście nie jest tym samym, ale ma równoważny wynik końcowy, który neguje publiczne odwołanie do elementu członkowskiego: .

Powiązane problemy