Czy jest jakiś sposób na udostępnienie zmiennych „prywatnych” (zdefiniowanych w konstruktorze) metodom zdefiniowanym w prototypie?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
To działa:
t.nonProtoHello()
Ale to nie:
t.prototypeHello()
Jestem przyzwyczajony do definiowania moich metod w konstruktorze, ale odchodzę od tego z kilku powodów.
javascript
private-members
kody morganowe
źródło
źródło
Odpowiedzi:
Nie, nie ma na to sposobu. Zasadniczo byłoby to odwracanie zakresu.
Metody zdefiniowane w konstruktorze mają dostęp do zmiennych prywatnych, ponieważ wszystkie funkcje mają dostęp do zakresu, w którym zostały zdefiniowane.
Metody zdefiniowane w prototypie nie są zdefiniowane w zakresie konstruktora i nie będą miały dostępu do lokalnych zmiennych konstruktora.
Nadal możesz mieć prywatne zmienne, ale jeśli chcesz, aby metody zdefiniowane w prototypie miały do nich dostęp, powinieneś zdefiniować
this
metody pobierające i ustawiające na obiekcie, do którego będą miały dostęp metody prototypowe (wraz ze wszystkim innym) . Na przykład:źródło
secret
własnośćthis
. JavaScript po prostu nie obsługuje zmiennych prywatnych z prototypami, ponieważ prototypy są powiązane z kontekstem strony wywołującej, a nie kontekstem strony tworzenia.person.getSecret()
?Aktualizacja: w ES6 istnieje lepszy sposób:
Krótko mówiąc, możesz użyć nowego
Symbol
do tworzenia prywatnych pól.Oto świetny opis: https://curiosity-driven.org/private-properties-in-javascript
Przykład:
Dla wszystkich nowoczesnych przeglądarek z ES5:
Możesz użyć tylko Zamknięć
Najprostszym sposobem na konstruowanie obiektów jest całkowite uniknięcie dziedziczenia prototypowego. Wystarczy zdefiniować zmienne prywatne i funkcje publiczne w zamknięciu, a wszystkie metody publiczne będą miały prywatny dostęp do zmiennych.
Lub możesz użyć tylko prototypów
W JavaScript dziedziczenie prototypów jest przede wszystkim optymalizacją . Umożliwia wielu instancjom współużytkowanie metod prototypowych, a nie każda instancja ma własne metody.
Wadą jest to, że
this
jest tylko rzeczą, która różni się za każdym razem prototypal funkcja jest wywoływana.Dlatego wszelkie prywatne pola muszą być dostępne przez
this
, co oznacza, że będą publiczne. Trzymamy się więc konwencji nazewnictwa dla_private
pól.Nie zawracaj sobie głowy mieszaniem zamknięć z prototypami
Myślę, że nie powinieneś mieszać zmiennych zamknięcia z metodami prototypowymi. Powinieneś użyć jednego lub drugiego.
Gdy używasz zamknięcia w celu uzyskania dostępu do zmiennej prywatnej, metody prototypowe nie mogą uzyskać dostępu do zmiennej. Musisz więc ujawnić zamknięcie
this
, co oznacza, że ujawniasz je publicznie w taki czy inny sposób. Dzięki takiemu podejściu niewiele można zyskać.Który mam wybrać?
W przypadku naprawdę prostych obiektów wystarczy użyć zwykłego obiektu z zamknięciami.
Jeśli potrzebujesz dziedziczenia prototypowego - do dziedziczenia, wydajności itp. - trzymaj się konwencji nazewnictwa „_private” i nie zawracaj sobie głowy zamykaniem.
Nie rozumiem, dlaczego programiści JS tak bardzo starają się, aby pola były naprawdę prywatne.
źródło
_private
konwencja nazewnictwa jest nadal najlepszym rozwiązaniem, jeśli chcesz skorzystać z dziedziczenia prototypowego.Symbol
która jest doskonałym sposobem na tworzenie prywatnych pól. Oto świetne wyjaśnienie: ciekawośćSymbol
zamknięcie obejmujące całą klasę. W ten sposób wszystkie prototypowe metody mogą korzystać z Symbolu, ale nigdy nie są ujawniane poza klasą.Object.getOwnPropertySymbols
. Jest to więc prywatność tylko z powodu niejasności.toString
. Nie różni się to od Java lub C # ... członkowie prywatni są nadal dostępni przez odbicie, ale zwykle są mocno zasłonięci. Wszystko to wzmacnia mój końcowy punkt: „Nie rozumiem, dlaczego programiści JS tak bardzo starają się, aby pola były naprawdę prywatne”.Kiedy to przeczytałem, brzmiało to jak trudne wyzwanie, więc postanowiłem znaleźć sposób. Co wymyśliłem było CRAAAAZY ale całkowicie działa.
Najpierw próbowałem zdefiniować klasę w funkcji natychmiastowej, abyś miał dostęp do niektórych prywatnych właściwości tej funkcji. Działa to i pozwala uzyskać prywatne dane, jednak jeśli spróbujesz ustawić prywatne dane, wkrótce przekonasz się, że wszystkie obiekty będą miały tę samą wartość.
Jest wiele przypadków, w których byłoby to odpowiednie, na przykład gdybyś chciał mieć stałe wartości, takie jak nazwy zdarzeń, które są współużytkowane między instancjami. Ale zasadniczo działają one jak prywatne zmienne statyczne.
Jeśli absolutnie potrzebujesz dostępu do zmiennych w prywatnej przestrzeni nazw z metod zdefiniowanych w prototypie, możesz wypróbować ten wzorzec.
Chciałbym uzyskać informacje zwrotne od każdego, kto widzi błąd w tym sposobie.
źródło
i
został dodany do wszystkich instancji. Nie jest więc w pełni „przezroczysty” ii
może być nadal modyfikowany.patrz strona Douga Crockforda na ten temat . Musisz to zrobić pośrednio za pomocą czegoś, co może uzyskać dostęp do zakresu zmiennej prywatnej.
inny przykład:
przypadek użycia:
źródło
_set
poprzezset
? Dlaczego nie nazwać tegoset
na początek?Sugeruję, że prawdopodobnie dobrym pomysłem byłoby opisanie „posiadania przypisania prototypu w konstruktorze” jako anty-wzorca Javascript. Pomyśl o tym. To jest zbyt ryzykowne.
To, co tak naprawdę robisz przy tworzeniu drugiego obiektu (tj. B), zmienia definicję tej funkcji prototypu dla wszystkich obiektów, które używają tego prototypu. Spowoduje to skuteczne zresetowanie wartości obiektu a w twoim przykładzie. Będzie działać, jeśli chcesz udostępnić zmienną i jeśli zdarzy się, że utworzysz wszystkie instancje obiektów z góry, ale wydaje się to zbyt ryzykowne.
Znalazłem błąd w Javascript, nad którym ostatnio pracowałem, spowodowany dokładnie tym anty-wzorem. Próbował ustawić funkcję przeciągania i upuszczania dla konkretnego tworzonego obiektu, ale zamiast tego robił to dla wszystkich instancji. Niedobrze.
Rozwiązanie Douga Crockforda jest najlepsze.
źródło
@Kai
To nie zadziała. Jeśli zrobisz
t2.prototypeHello
będzie wtedy dostęp do prywatnej sekcji t.@AnglesCrimes
Przykładowy kod działa dobrze, ale w rzeczywistości tworzy „statyczny” prywatny element wspólny dla wszystkich instancji. Morgankody tego rozwiązania mogą nie być rozwiązaniem.
Do tej pory nie znalazłem łatwego i czystego sposobu na zrobienie tego bez wprowadzenia prywatnego skrótu i dodatkowych funkcji czyszczenia. Funkcję członka prywatnego można symulować do pewnego stopnia:
źródło
privateFoo
jest całkowicie prywatny, a zatem niewidoczny podczas uzyskiwanianew Foo()
.bar()
Jest tu tylko metoda publiczna, do której ma dostępprivateFoo
. Możesz użyć tego samego mechanizmu do prostych zmiennych i obiektów, jednak zawsze należy pamiętać, żeprivates
są one w rzeczywistości statyczne i będą wspólne dla wszystkich tworzonych obiektów.Tak, to możliwe. Wzorzec projektowy PPF właśnie to rozwiązuje.
PPF oznacza prywatne funkcje prototypowe. Podstawowe PPF rozwiązuje następujące problemy:
Po pierwsze:
To takie proste. Na przykład:
...
Przeczytaj całą historię tutaj:
Wzorzec projektowy PPF
źródło
Możesz to faktycznie osiągnąć, korzystając z weryfikacji Accessor :
Ten przykład pochodzi z mojego postu o funkcjach prototypowych i prywatnych danych i jest tam wyjaśniony bardziej szczegółowo.
źródło
W obecnym JavaScript jestem dość pewien, że istnieje jeden i jedyny sposób na uzyskanie stanu prywatnego , dostępnego z funkcji prototypowych , bez dodawania do niego niczego publicznego
this
. Odpowiedzią jest użycie wzorca „słabej mapy”.Podsumowując:
Person
Klasa ma jedną słabą mapę, w której kluczami są instancje Person, a wartości to zwykłe obiekty, które są używane do prywatnego przechowywania.Oto w pełni funkcjonalny przykład: (zagraj na http://jsfiddle.net/ScottRippey/BLNVr/ )
Tak jak powiedziałem, to naprawdę jedyny sposób na osiągnięcie wszystkich 3 części.
Istnieją jednak dwa zastrzeżenia. Po pierwsze, kosztuje to wydajność - za każdym razem, gdy uzyskujesz dostęp do prywatnych danych, jest to
O(n)
operacja, w którejn
jest liczba instancji. Nie będziesz chciał tego robić, jeśli masz dużą liczbę instancji. Po drugie, kiedy skończysz z instancją, musisz zadzwonićdestroy
; w przeciwnym razie instancja i dane nie zostaną usunięte, a skończy się wyciekiem pamięci.I dlatego chciałbym trzymać się mojej oryginalnej odpowiedzi „Nie powinieneś” .
źródło
Istnieje prostszy sposób, wykorzystując wykorzystanie metod
bind
icall
.Ustawiając prywatne zmienne na obiekt, możesz wykorzystać zasięg tego obiektu.
Przykład
Ta metoda nie jest pozbawiona wad. Ponieważ kontekst zasięgu jest skutecznie zastępowany, nie masz dostępu poza
_private
obiektem. Jednak nie jest niemożliwe zapewnienie dostępu do zakresu obiektu instancji. Możesz przekazać w kontekście obiektu (this
) jako drugi argument dobind
lubcall
nadal mieć dostęp do jego wartości publicznych w funkcji prototypu.Dostęp do wartości publicznych
źródło
Spróbuj!
źródło
caller
to od rozszerzenia zależnego od implementacji, które nie jest dozwolone w trybie ścisłym.Oto, co wymyśliłem.
głównym problemem związanym z tą implementacją jest to, że redefiniuje prototypy przy każdej instanacji.
źródło
Jest na to bardzo prosty sposób
Prototypy JavaScript są złote.
źródło
SharedPrivate.prototype
ponieważthis.constructor.prototype
wiele razy przedefiniowanie getP i setP jest wiele razy ...Spóźniam się na przyjęcie, ale myślę, że mogę się przyłączyć. Sprawdź to:
Nazywam tę metodę wzorcem akcesorium . Podstawową ideą jest to, że mamy zamknięcie , klucz wewnątrz zamknięcia i tworzymy prywatny obiekt (w konstruktorze), do którego można uzyskać dostęp tylko, jeśli masz klucz .
Jeśli jesteś zainteresowany, możesz przeczytać więcej na ten temat w moim artykule . Za pomocą tej metody można tworzyć właściwości dla każdego obiektu, do których nie można uzyskać dostępu poza zamknięciem. Dlatego możesz ich używać w konstruktorze lub prototypie, ale nigdzie indziej. Nigdzie nie widziałem tej metody, ale myślę, że jest naprawdę potężna.
źródło
Nie możesz umieścić zmiennych w wyższym zakresie?
źródło
Możesz także spróbować dodać metodę nie bezpośrednio na prototypie, ale na funkcji konstruktora w następujący sposób:
źródło
Oto coś, co wymyśliłem, próbując znaleźć najprostsze rozwiązanie tego problemu, być może może być przydatne dla kogoś. Jestem nowy w javascript, więc mogą być pewne problemy z kodem.
źródło
Dzisiaj stanąłem przed dokładnie tym samym pytaniem i po opracowaniu pierwszej klasy odpowiedzi Scotta Rippeya wymyśliłem bardzo proste rozwiązanie (IMHO), które jest zarówno kompatybilne z ES5, jak i wydajne, a także bezpieczne w przypadku kolizji nazw (używanie _private wydaje się niebezpieczne) .
Testowane z ringojs i nodejs. Z chęcią przeczytam twoją opinię.
źródło
Jak to jest Korzystanie z prywatnego akcesora. Pozwala tylko uzyskać zmienne, ale nie ustawiać ich, zależy od przypadku użycia.
źródło
Mam jedno rozwiązanie, ale nie jestem pewien, czy jest bez wad.
Aby to zadziałało, musisz użyć następującej struktury:
Oto kod:
Działa to tak, że udostępnia funkcję instancji „this.getPrivateFields” w celu uzyskania dostępu do obiektu zmiennych prywatnych „privateFields”, ale funkcja ta zwróci tylko obiekt „privateFields” w zdefiniowanym głównym zamknięciu (również funkcje prototypowe za pomocą „this.getPrivateFields” „należy zdefiniować wewnątrz tego zamknięcia).
Hash wytworzony podczas działania i trudny do odgadnięcia służy jako parametry, aby upewnić się, że nawet jeśli wywołanie „getPrivateFields” wykracza poza zakres zamknięcia, nie zwróci obiektu „privateFields”.
Wadą jest to, że nie możemy rozszerzyć TestClass o więcej funkcji prototypowych poza zamknięciem.
Oto kod testowy:
EDYCJA: Za pomocą tej metody można również „zdefiniować” funkcje prywatne.
źródło
Bawiłem się tym dzisiaj i było to jedyne rozwiązanie, jakie mogłem znaleźć bez użycia symboli. Najlepszą rzeczą jest to, że w rzeczywistości wszystko może być całkowicie prywatne.
Rozwiązanie opiera się na autorskim module ładującym, który w zasadzie staje się pośrednikiem prywatnej pamięci podręcznej (przy użyciu słabej mapy).
źródło
Wiem, że minęła ponad dekada, odkąd o to poproszono, ale po prostu zastanowiłem się nad tym po raz n-ty w życiu programisty i znalazłem możliwe rozwiązanie, którego nie wiem, czy całkowicie mi się podoba . Nie widziałem wcześniej tej metodologii, więc nadam jej nazwę „prywatny / publiczny wzór dolara” lub _ $ / $ $ .
Koncepcja wykorzystuje funkcję ClassDefinition, która zwraca funkcję konstruktora, która zwraca obiekt interfejsu . Jedyną metodą interfejsu jest
$
otrzymaniename
argumentu w celu wywołania odpowiedniej funkcji w obiekcie konstruktora, a wszelkie dodatkowe argumenty przekazane późniejname
są przekazywane w wywołaniu.Globalnie zdefiniowanej funkcji pomocnika
ClassValues
przechowuje wszystkie pola z obiektu w razie potrzeby. Określa_$
funkcję dostępu do nichname
. Jest to zgodne z krótkim wzorcem get / set, więc jeślivalue
zostanie przekazany, zostanie użyty jako nowa wartość zmiennej.Globalnie zdefiniowana funkcja
Interface
pobiera obiekt iValues
obiekt_interface
z jedną funkcją,$
która sprawdza, czyobj
znaleźć funkcję nazwaną na podstawie parametruname
i wywołuje jąvalues
jako obiekt o zasięgu . Dodatkowe przekazane argumenty$
zostaną przekazane przy wywołaniu funkcji.W poniższym przykładzie
ClassX
jest przypisany do wynikuClassDefinition
, który jestConstructor
funkcją.Constructor
może otrzymać dowolną liczbę argumentów.Interface
to, co otrzymuje kod zewnętrzny po wywołaniu konstruktora.Nie ma sensu mieć funkcji nie prototypowych
Constructor
, chociaż można je zdefiniować w treści funkcji konstruktora. Wszystkie funkcje są wywoływane z publicznym wzorem dolarathis.$("functionName"[, param1[, param2 ...]])
. Dostęp do prywatnych wartości uzyskuje się za pomocą prywatnego wzoru dolarathis._$("valueName"[, replacingValue]);
. PonieważInterface
nie ma definicji_$
, do obiektów zewnętrznych nie można uzyskać dostępu do wartości. Ponieważ każde prototypowane ciało funkcjithis
jest ustawione navalues
obiekt w funkcji$
, otrzymasz wyjątki, jeśli bezpośrednio wywołasz funkcje rodzeństwa Konstruktora; te _ $ / $ wzór musi być następnie w organach prototypy funkcji też. Poniżej przykładowe użycie.I wyjście konsoli.
_ $ / $ Wzór pozwala na pełną prywatność wartościami w pełni prototyp klas. Nie wiem, czy kiedykolwiek tego użyję, czy też ma wady, ale hej, to była dobra łamigłówka!
źródło
ES6 WeakMaps
Za pomocą prostego wzorca opartego na ES6 WeakMaps można uzyskać prywatne zmienne składowe , dostępne z funkcji prototypowych .
Bardziej szczegółowe wyjaśnienie tego wzoru można znaleźć tutaj
źródło
Musisz zmienić 3 rzeczy w kodzie:
var privateField = "hello"
sięthis.privateField = "hello"
.privateField
zthis.privateField
.privateField
zthis.privateField
.Ostateczny kod byłby następujący:
źródło
this.privateField
nie byłoby polem prywatnym. jest dostępny z zewnątrz:t.privateField
W definicji konstruktora można użyć przypisania prototypu.
Zmienna będzie widoczna dla dodanej metody prototypu, ale wszystkie instancje funkcji będą miały dostęp do tej samej zmiennej SHARED.
Mam nadzieję, że to może się przydać.
źródło