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:
function Person(name) {
let age = 20; // this is private
this.name = name; // this is public
this.greet = function () {
// here we can access both name and age
console.log(`name: ${this.name}, age: ${age}`);
};
}
let joe = new Person('Joe');
joe.greet();
// here we can access name but not age
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 zthis
. 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:
let Person = (function () {
let privateProps = new WeakMap();
class Person {
constructor(name) {
this.name = name; // this is public
privateProps.set(this, {age: 20}); // this is private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
}
}
return Person;
})();
let joe = new Person('Joe');
joe.greet();
// here we can access joe's name but not age
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óbage.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
, ale WeakMap
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 adresemthis[mySymbol]
.
Za pomocą tej zasady można naruszać prywatność tej metody Object.getOwnPropertySymbols
, ale jest nieco niewygodna.
Przykład:
let Person = (function () {
let ageKey = Symbol();
class Person {
constructor(name) {
this.name = name; // this is public
this[ageKey] = 20; // this is intended to be private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${this[ageKey]}`);
}
}
return Person;
})();
let joe = new Person('Joe');
joe.greet();
// Here we can access joe's name and, with a little effort, age. ageKey is
// not in scope, but we can obtain it by listing all Symbol properties on
// joe with `Object.getOwnPropertySymbols(joe)`.
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:
class Person {
constructor(name) {
this.name = name; // this is public
this._age = 20; // this is intended to be private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${this._age}`);
}
}
let joe = new Person('Joe');
joe.greet();
// Here we can access both joe's name and age. But we know we aren't
// supposed to access his age, which just might stop us.
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.