Czy można tworzyć prywatne właściwości w klasach ES6?
Oto przykład. Jak mogę uniemożliwić dostęp instance.property
?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
Odpowiedzi:
Prywatne pola (i metody) są wdrażane w standardzie ECMA . Możesz zacząć korzystać z nich już dziś z ustawieniem Babel 7 i etapu 3.
źródło
this
w konstruktorze przed wywołaniemsuper()
. Jednak babel stawia je przed super.#privateCrap
składnię?#beep() {}
:; i toasync #bzzzt() {}
:?Krótka odpowiedź, nie, nie ma natywnego wsparcia dla własności prywatnych z klasami ES6.
Ale możesz naśladować to zachowanie, nie dołączając nowych właściwości do obiektu, ale trzymając je w konstruktorze klas, i używaj metod pobierających i ustawiających, aby uzyskać dostęp do ukrytych właściwości. Zauważ, że metody pobierające i ustawiające są redefiniowane przy każdej nowej instancji klasy.
ES6
ES5
źródło
class
składni w pierwszej kolejności.getName
isetName
właściwości prywatnymi?Aby rozwinąć odpowiedź @ loganfsmyth:
Jedynymi naprawdę prywatnymi danymi w JavaScript są wciąż zmienne o zasięgu. Nie można mieć właściwości prywatnych w sensie właściwości, do których dostęp jest uzyskiwany wewnętrznie w taki sam sposób, jak właściwości publicznych, ale można używać zmiennych o zasięgu do przechowywania danych prywatnych.
Zmienne o zasięgu
Podejście polega na wykorzystaniu zakresu funkcji konstruktora, która jest prywatna, do przechowywania prywatnych danych. Aby metody miały dostęp do tych prywatnych danych, muszą być również utworzone w konstruktorze, co oznacza, że odtwarzasz je przy każdej instancji. Jest to kara za wydajność i pamięć, ale niektórzy uważają, że kara jest do zaakceptowania. Można uniknąć kary za metody, które nie potrzebują dostępu do prywatnych danych, dodając je jak zwykle do prototypu.
Przykład:
Scoped WeakMap
Można użyć WeakMap, aby uniknąć wydajności i kary pamięci z poprzedniego podejścia. WeakMaps kojarzy dane z Obiektami (tutaj instancje) w taki sposób, że można uzyskać do nich dostęp tylko przy użyciu tej WeakMap. Tak więc używamy metody zmiennych o zasięgu do utworzenia prywatnej WeakMap, a następnie używamy tej WeakMap do pobierania prywatnych danych związanych z
this
. Jest to szybsze niż metoda zmiennych o zasięgu, ponieważ wszystkie instancje mogą współużytkować jedną WeakMap, więc nie trzeba ponownie tworzyć metod, aby umożliwić im dostęp do własnych WeakMap.Przykład:
W tym przykładzie użyto obiektu do użycia jednej WeakMap dla wielu prywatnych właściwości; możesz także użyć wielu WeakMap i używać ich w podobny sposób
age.set(this, 20)
, lub napisać małe opakowanie i użyć go w inny sposób, npprivateProps.set(this, 'age', 0)
.Prywatność tego podejścia mogłaby teoretycznie zostać naruszona przez manipulowanie globalnym
WeakMap
obiektem . To powiedziawszy, cały JavaScript może zostać uszkodzony przez zniekształcone globale. Nasz kod jest już oparty na założeniu, że tak się nie dzieje.(Tę metodę można również wykonać
Map
, aleWeakMap
jest lepsza, ponieważMap
spowoduje przecieki pamięci, chyba że będziesz bardzo ostrożny, iw tym celu nie różnią się one inaczej.)Połowa odpowiedzi: Symbole o zasięgu
Symbol jest rodzajem pierwotnej wartości, która może służyć jako nazwa właściwości. Możesz użyć metody zmiennej o zasięgu, aby utworzyć prywatny symbol, a następnie przechowywać prywatne dane pod adresem
this[mySymbol]
.Za pomocą tej zasady można naruszać prywatność tej metody
Object.getOwnPropertySymbols
, ale jest nieco niewygodna.Przykład:
Pół odpowiedzi: podkreślenia
Stare domyślne, wystarczy użyć właściwości publicznej z prefiksem podkreślenia. Chociaż konwencja nie jest w żaden sposób własnością prywatną, ta konwencja jest na tyle rozpowszechniona, że wykonuje dobrą robotę, komunikując, że czytelnicy powinni traktować tę własność jako prywatną, co często kończy pracę. W zamian za ten upływ otrzymujemy podejście łatwiejsze do odczytania, łatwiejsze do pisania i szybsze.
Przykład:
Wniosek
Począwszy od ES2017, nadal nie ma idealnego sposobu na robienie prywatnych nieruchomości. Różne podejścia mają zalety i wady. Zmienne o zasięgu są naprawdę prywatne; scakowane WeakMapy są bardzo prywatne i bardziej praktyczne niż zmienne zakresowe; Symbole o zasięgu są w miarę prywatne i praktyczne; podkreślenia są często dość prywatne i bardzo praktyczne.
źródło
instanceof
. Przyznaję, że myślałem o tym podejściu uwzględnionym wyłącznie dla kompletności i powinienem był więcej zastanowić się nad tym, na ile faktycznie jest w stanie.Aktualizacja: przygotowywana jest propozycja z ładniejszą składnią . Wkłady są mile widziane.
Tak, istnieje - dla dostępu do obiektów w zasięgu
Symbol
- ES6 wprowadza s .Symbole są unikalne, nie możesz uzyskać dostępu do jednego z zewnątrz, z wyjątkiem odbicia (jak prywatne w Javie / C #), ale każdy, kto ma dostęp do symbolu wewnątrz, może go użyć do uzyskania klucza:
źródło
Object.getOwnPropertySymbols
? ;)const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();
. To nie jest tak naprawdę prywatność, to niejasność w sensie tradycyjnego JavaScript. Uważam, że „prywatny” JavaScript oznacza używanie zamknięć do enkapsulacji zmiennych. Te zmienne nie są zatem dostępne poprzez odbicie.private
iprotected
byłoby o wiele czystsze niżSymbol
lubName
. Wolę notację kropkową niż notację nawiasową. Chciałbym nadal używać kropki do prywatnych rzeczy.this.privateVar
Odpowiedź brzmi nie". Ale możesz utworzyć prywatny dostęp do takich nieruchomości:
export
słowa kluczowego.(Sugestia, że Symbole mogą być używane do zapewnienia prywatności, była prawdziwa we wcześniejszej wersji specyfikacji ES6, ale nie jest już tak: https://mail.mozilla.org/pipermail/es-discuss/2014- stycznia / 035604. HTML i https://stackoverflow.com/a/22280202/1282216 . Aby uzyskać dłuższą dyskusję na temat symboli i prywatności, zobacz: https://curiosity-driven.org/private-properties-in-javascript )
źródło
Jedynym sposobem na uzyskanie prawdziwej prywatności w JS jest określenie zakresu, więc nie ma możliwości, aby właściwość, która jest jej członkiem
this
, była dostępna tylko wewnątrz komponentu. Najlepszym sposobem przechowywania prawdziwie prywatnych danych w ES6 jest WeakMap.Oczywiście jest to prawdopodobnie powolne i zdecydowanie brzydkie, ale zapewnia prywatność.
Pamiętaj, że NAWET TO NIE jest idealne, ponieważ JavaScript jest tak dynamiczny. Ktoś może jeszcze to zrobić
aby przechwytywać wartości podczas ich przechowywania, więc jeśli chcesz zachować szczególną ostrożność, musisz przechwycić lokalne odwołanie do
.set
i.get
użyć jawnie zamiast polegać na nadpisywalnym prototypie.źródło
get
do jednej na metodę (npconst _ = privates.get(this); console.log(_.privateProp1);
.).const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"
oznacza, że twoja własność jest prywatna czy nie?W celu odniesienia się do innych osób poszukujących w przyszłości, słyszę teraz, że zaleceniem jest użycie WeakMaps do przechowywania prywatnych danych.
Oto wyraźniejszy, działający przykład:
źródło
Zależy od kogo pytasz :-)
Żaden
private
modyfikator właściwości nie jest zawarty w propozycji maksymalnie minimalnych klas, która wydaje się, że znalazła się w bieżącej wersji roboczej .Może jednak istnieć wsparcie dla prywatnych nazw , które dopuszczają prywatne własności - i prawdopodobnie mogłyby być również użyte w definicjach klas.
źródło
Korzystanie z modułów ES6 (początkowo proponowanych przez @ d13) działa dobrze dla mnie. Nie naśladuje on doskonale własności prywatnych, ale przynajmniej możesz mieć pewność, że właściwości, które powinny być prywatne, nie wyciekną poza twoją klasę. Oto przykład:
coś.js
W takim przypadku konsumujący kod może wyglądać następująco:
Aktualizacja (ważne):
Jak wspomniano w komentarzach @DanyalAytekin, te własności prywatne są statyczne, dlatego mają zasięg globalny. Będą dobrze działać podczas pracy z Singletonami, ale należy zachować ostrożność w przypadku obiektów przejściowych. Rozszerzając powyższy przykład:
źródło
private static
.a.say(); // a
powinno byćb.say(); // b
let _message = null
sposób, nie tak fajny, gdy wywołuje konstruktora wiele razy, to psuje.Wypełnianie @ d13 i komentarze @ johnny-oshika i @DanyalAytekin:
Myślę, że w przykładzie podanym przez @ johnny-oshika moglibyśmy użyć normalnych funkcji zamiast funkcji strzałek, a następnie
.bind
z bieżącym obiektem plus_privates
obiektem jako parametrem curry:coś.js
main.js
Korzyści, które mogę wymyślić:
_greet
i_updateMessage
zachowywać się jak metody prywatne, dopóki tego nie robimy)export
referencji)_privates
obiektuNiektóre wady, które mogę wymyślić:
Działający fragment kodu można znaleźć tutaj: http://www.webpackbin.com/NJgI5J8lZ
źródło
Tak - możesz utworzyć właściwość kapsułkowaną , ale nie zrobiono tego za pomocą modyfikatorów dostępu (public | private), przynajmniej nie w ES6.
Oto prosty przykład, jak można to zrobić za pomocą ES6:
1 Utwórz klasę za pomocą klasy słowa
2 wewnątrz jest konstruktor stwierdzenie bloku scoped zmienna użyciu let LUB const zasięgu blokowym, słów „ -> ponieważ mają one zasięg blokowy, nie można uzyskać do nich dostępu z zewnątrz (enkapsulowane)
3 Aby umożliwić kontrolę dostępu (setters | getters) do tych zmiennych, możesz zadeklarować metodę instancji wewnątrz jej konstruktora, używając:
this.methodName=function(){}
składniTeraz sprawdźmy to:
źródło
new Something();
ponieważ twoje metody są deklarowane w konstruktorze, aby mieć do nich dostęp zmienne prywatne. Może to powodować duże zużycie pamięci, jeśli utworzysz wiele wystąpień swojej klasy, więc problemy z wydajnością. Metody powinny zostać zadeklarowane poza zakresem konstruktora. Mój komentarz był raczej wyjaśnieniem wad twojego rozwiązania niż krytyką.Inne podejście do „prywatnego”
Zamiast walczyć z tym, że prywatna widoczność jest obecnie niedostępna w ES6, postanowiłem zastosować bardziej praktyczne podejście, które jest w porządku, jeśli twoje IDE obsługuje JSDoc (np. Webstorm). Chodzi o to, aby użyć
@private
tagu . Jeśli chodzi o rozwój, IDE uniemożliwi ci dostęp do dowolnego członka prywatnego spoza jego klasy. Działa całkiem dobrze dla mnie i było bardzo przydatne do ukrywania wewnętrznych metod, więc funkcja autouzupełniania pokazuje mi, co klasa naprawdę chciała ujawnić. Oto przykład:źródło
@private
komentarz nie może temu zapobiec, jest to tylko funkcja do generowania dokumentacji i masz IDE.WeakMap
Object.getOwnPropertySymbols
)Najpierw zdefiniuj funkcję do zawijania WeakMap:
Następnie zbuduj referencję poza klasą:
Uwaga: klasa nie jest obsługiwana przez IE11, ale wygląda lepiej w tym przykładzie.
źródło
Och, tyle egzotycznych rozwiązań! Zwykle nie dbam o prywatność, więc używam „pseudo-prywatności”, jak tu powiedziano . Ale jeśli to ważne (jeśli istnieją specjalne wymagania) używam czegoś takiego jak w tym przykładzie:
Inna możliwa implementacja funkcji (konstruktora)
Job
:źródło
Osobiście podoba mi się propozycja operatora bind,
::
a następnie połączę ją ze wspomnianym rozwiązaniem @ d13, ale na razie trzymaj się odpowiedzi @ d13, gdzie używaszexport
słowa kluczowego dla swojej klasy i umieszczasz prywatne funkcje w module.jest jeszcze jedno trudne rozwiązanie, o którym nie wspomniano tutaj, które jest bardziej funkcjonalne i pozwoliłoby na posiadanie wszystkich prywatnych rekwizytów / metod w klasie.
Private.js
Test.js
komentarze na ten temat będą mile widziane.
źródło
Natknąłem się na ten post, gdy szukałem najlepszych praktyk w zakresie „prywatnych danych dla klas”. Wspomniano, że niektóre wzorce będą miały problemy z wydajnością.
Złożyłem kilka testów jsperf opartych na 4 głównych wzorach z książki online „Exploring ES6”:
http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes
Testy można znaleźć tutaj:
https://jsperf.com/private-data-for-classes
W Chrome 63.0.3239 / Mac OS X 10.11.6 najskuteczniejszymi wzorcami były „Prywatne dane za pośrednictwem środowisk konstruktorów” i „Prywatne dane za pośrednictwem konwencji nazewnictwa”. Dla mnie Safari wypadło dobrze dla WeakMap, ale Chrome nie tak dobrze.
Nie znam wpływu pamięci, ale wzorzec „środowisk konstruktorów”, który, jak ostrzegali niektórzy, byłby problem z wydajnością, był bardzo wydajny.
4 podstawowe wzory to:
Prywatne dane za pośrednictwem środowisk konstruktorów
Prywatne dane za pośrednictwem środowisk konstruktorów 2
Prywatne dane za pomocą konwencji nazewnictwa
Prywatne dane za pośrednictwem WeakMaps
Prywatne dane za pomocą symboli
źródło
Wierzę, że możliwe jest uzyskanie „najlepszego z obu światów” za pomocą zamknięć wewnątrz konstruktorów. Istnieją dwie odmiany:
Wszyscy członkowie danych są prywatni
Niektórzy członkowie są prywatni
UWAGA: Jest to wprawdzie brzydkie. Jeśli znasz lepsze rozwiązanie, edytuj tę odpowiedź.
źródło
W rzeczywistości jest to możliwe przy użyciu symboli i serwerów proxy. Używasz symboli w zakresie klasy i ustawiasz dwie pułapki w proxy: jeden dla prototypu klasy, tak aby Reflect.ownKeys (instancja) lub Object.getOwnPropertySymbols nie oddawał twoich symboli, drugi jest dla samego konstruktora więc kiedy
new ClassName(attrs)
zostanie wywołane, zwrócona instancja zostanie przechwycona i będzie miała zablokowane symbole własnych właściwości. Oto kod:Reflect.ownKeys()
działa tak:Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))
dlatego potrzebujemy pułapki na te obiekty.źródło
Nawet maszynopis nie może tego zrobić. Z ich dokumentacji :
Ale przeniesione na ich plac zabaw daje to:
Dlatego ich „prywatne” słowo kluczowe jest nieskuteczne.
źródło
Spóźniam się na tę imprezę, ale trafiłem na pytanie OP w poszukiwaniu, więc ... Tak, możesz mieć prywatne nieruchomości, zawijając deklarację klasy w zamknięciu
Jest przykład tego, jak mam prywatne metody w tym codepen . W poniższym fragmencie klasa Subscribable ma dwie funkcje „prywatne”
process
iprocessCallbacks
. Wszelkie właściwości można dodawać w ten sposób i są one poufne dzięki zastosowaniu zamknięcia. Prywatność IMO jest rzadką potrzebą, jeśli obawy są dobrze rozdzielone, a JavaScript nie musi być rozdęty przez dodanie większej składni, gdy zamknięcie starannie wykonuje zadanie.Podoba mi się to podejście, ponieważ ładnie rozdziela obawy i zapewnia prywatność. Jedynym minusem jest konieczność użycia „ja” (lub czegoś podobnego) w celu odniesienia do „tego” w treści prywatnej.
źródło
Myślę, że odpowiedź Benjamina jest prawdopodobnie najlepsza w większości przypadków, dopóki język natywnie nie obsługuje jawnie prywatnych zmiennych.
Jeśli jednak z jakiegoś powodu musisz uniemożliwić dostęp
Object.getOwnPropertySymbols()
, metodą, którą rozważałem, jest dołączenie unikalnej, nieskonfigurowalnej, niepoliczalnej, niepisywalnej właściwości, której można użyć jako identyfikatora właściwości do każdego obiektu w budowie (np. unikatowySymbol
, jeśli nie masz jeszcze innej unikalnej właściwości, takiej jak anid
). Następnie po prostu przechowuj mapę „prywatnych” zmiennych każdego obiektu za pomocą tego identyfikatora.Potencjalną zaletą tego podejścia w porównaniu z użyciem
WeakMap
jest szybszy czas dostępu, jeśli wydajność staje się problemem.źródło
destroy()
metodę, która powinna być wywoływana za pomocą kodu za każdym razem, gdy trzeba usunąć obiekt.Tak, całkowicie można i całkiem łatwo. Odbywa się to poprzez ujawnienie prywatnych zmiennych i funkcji poprzez zwrócenie prototypowego wykresu obiektów w konstruktorze. To nic nowego, ale weź trochę js foo, aby zrozumieć jego elegancję. W ten sposób nie używa się globalnego zasięgu ani słabych map. Jest to forma refleksji wbudowana w język. W zależności od tego, jak wykorzystasz to; można albo wymusić wyjątek, który przerywa stos wywołań, albo zakopać wyjątek jako
undefined
. Zostało to przedstawione poniżej i można przeczytać więcej o tych funkcjach tutajźródło
źródło
console.log(instance.property)
powinien rzucić lub dać ci niezdefiniowany, a nie dać ci „testu”.Inny sposób podobny do dwóch ostatnich opublikowanych
źródło
Większość odpowiedzi albo mówi, że jest to niemożliwe, albo wymaga użycia WeakMap lub Symbol, które są funkcjami ES6, które prawdopodobnie wymagałyby wielokrotnych wypełnień. Jest jednak inny sposób! 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
Zobacz tę odpowiedź, aby uzyskać czyste i proste rozwiązanie „klasowe” z prywatnym i publicznym interfejsem oraz obsługą kompozycji
źródło
Znalazłem bardzo proste rozwiązanie, po prostu użyj
Object.freeze()
. Oczywiście problem polega na tym, że nie możesz później dodać niczego do obiektu.źródło
setName(name) { this.name = name; }
Używam tego wzoru i zawsze działa dla mnie
źródło
Właściwie jest to możliwe.
1. Najpierw utwórz klasę i w konstruktorze zwróć wywoływaną
_public
funkcję.2. W wywoływanej
_public
funkcji przekażthis
odwołanie (aby uzyskać dostęp do wszystkich prywatnych metod i rekwizytów) i wszystkich argumentów zconstructor
(które zostaną przekazanenew Names()
)3. W zakresie
_public
funkcji znajduje się równieżNames
klasa z dostępem dothis
(_this ) odniesienie doNames
klasy prywatnejźródło
Możesz spróbować https://www.npmjs.com/package/private-members
Ten pakiet zapisze członków według instancji.
źródło