Per espandere la risposta di @ loganfsmyth:
Gli unici dati veramente privati in JavaScript sono ancora variabili con ambito. Non è possibile avere proprietà private nel senso di proprietà accessibili internamente allo stesso modo delle proprietà pubbliche, ma è possibile utilizzare variabili con ambito per archiviare dati privati.
Variabili con ambito
L'approccio qui è quello di utilizzare l'ambito della funzione di costruzione, che è privata, per memorizzare i dati privati. Affinché i metodi abbiano accesso a questi dati privati, devono essere creati anche all'interno del costruttore, il che significa che li stai ricreando in ogni istanza. Questa è una penalità per prestazioni e memoria, ma alcuni credono che la penalità sia accettabile. La penalità può essere evitata per i metodi che non richiedono l'accesso ai dati privati aggiungendoli al prototipo come al solito.
Esempio:
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
WeakMap con ambito
Una WeakMap può essere utilizzata per evitare le prestazioni dell'approccio precedente e la penalità di memoria. WeakMap associa i dati agli oggetti (qui, istanze) in modo tale da poter accedere solo tramite quella WeakMap. Quindi, utilizziamo il metodo delle variabili con ambito per creare una WeakMap privata, quindi utilizziamo quella WeakMap per recuperare i dati privati associati this
. Questo è più veloce del metodo delle variabili con ambito perché tutte le tue istanze possono condividere una singola WeakMap, quindi non è necessario ricreare i metodi solo per far loro accedere alle proprie WeakMap.
Esempio:
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
In questo esempio viene utilizzato un oggetto per utilizzare una WeakMap per più proprietà private; potresti anche usare più WeakMap e usarle come age.set(this, 20)
, oppure scrivere un piccolo wrapper e usarlo in un altro modo, come privateProps.set(this, 'age', 0)
.
La privacy di questo approccio potrebbe teoricamente essere violata manomettendo l' WeakMap
oggetto globale . Detto questo, tutto JavaScript può essere rotto da globi alterati. Il nostro codice è già basato sul presupposto che ciò non accada.
(Questo metodo potrebbe anche essere fatto con Map
, ma WeakMap
è meglio perché Map
creerà perdite di memoria a meno che tu non sia molto attento, e per questo i due non sono altrimenti diversi.)
Mezza risposta: simboli con ambito
Un simbolo è un tipo di valore primitivo che può fungere da nome di proprietà. È possibile utilizzare il metodo della variabile con ambito per creare un simbolo privato, quindi archiviare i dati privati in this[mySymbol]
.
La privacy di questo metodo può essere violata utilizzando Object.getOwnPropertySymbols
, ma è piuttosto difficile da fare.
Esempio:
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)`.
Mezza risposta: sottolinea
Il vecchio predefinito, basta usare una proprietà pubblica con un prefisso di sottolineatura. Sebbene non sia in alcun modo una proprietà privata, questa convenzione è abbastanza diffusa da fare un buon lavoro comunicando che i lettori dovrebbero considerare la proprietà come privata, il che spesso porta a termine il lavoro. In cambio di questo intervallo, otteniamo un approccio più facile da leggere, più facile da scrivere e più veloce.
Esempio:
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.
Conclusione
A partire da ES2017, non esiste ancora un modo perfetto per fare proprietà private. Vari approcci hanno pro e contro. Le variabili con ambito sono veramente private; le WeakMap con ambito sono molto private e più pratiche delle variabili con ambito; I simboli con ambito sono ragionevolmente privati e ragionevolmente pratici; i caratteri di sottolineatura sono spesso abbastanza privati e molto pratici.