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 WeakMapjest 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.