Para ampliar la respuesta de @ loganfsmyth:
Los únicos datos verdaderamente privados en JavaScript siguen siendo las variables de ámbito. No puede tener propiedades privadas en el sentido de propiedades a las que se accede internamente de la misma manera que las propiedades públicas, pero puede usar variables de ámbito para almacenar datos privados.
Variables de alcance
El enfoque aquí es utilizar el alcance de la función constructora, que es privada, para almacenar datos privados. Para que los métodos tengan acceso a estos datos privados, también deben crearse dentro del constructor, lo que significa que los está recreando con cada instancia. Esta es una penalización de rendimiento y memoria, pero algunos creen que la penalización es aceptable. La penalización se puede evitar para métodos que no necesitan acceso a datos privados agregándolos al prototipo como de costumbre.
Ejemplo:
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
Scoak WeakMap
Se puede usar un WeakMap para evitar el rendimiento del enfoque anterior y la penalización de la memoria. WeakMaps asocia datos con objetos (aquí, instancias) de tal manera que solo se puede acceder usando ese WeakMap. Por lo tanto, usamos el método de variables con ámbito para crear un WeakMap privado, luego usamos ese WeakMap para recuperar datos privados asociados conthis
. Esto es más rápido que el método de variables con ámbito porque todas sus instancias pueden compartir un solo WeakMap, por lo que no necesita volver a crear métodos solo para que accedan a sus propios WeakMaps.
Ejemplo:
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
Este ejemplo usa un objeto para usar un WeakMap para múltiples propiedades privadas; también puedes usar múltiples WeakMaps y usarlos comoage.set(this, 20)
, o escribir un pequeño contenedor y usarlo de otra manera, comoprivateProps.set(this, 'age', 0)
.
La privacidad de este enfoque podría ser violada teóricamente al alterar el WeakMap
objeto . Dicho esto, todo el JavaScript puede ser roto por los globals destrozados. Nuestro código ya está basado en el supuesto de que esto no está sucediendo.
(Este método también podría hacerse Map
, pero WeakMap
es mejor porqueMap
creará pérdidas de memoria a menos que sea muy cuidadoso, y para este propósito los dos no son diferentes).
Media respuesta: símbolos con ámbito
Un símbolo es un tipo de valor primitivo que puede servir como nombre de propiedad. Puede usar el método de variable con ámbito para crear un símbolo privado y luego almacenar datos privados enthis[mySymbol]
.
La privacidad de este método se puede violar usando Object.getOwnPropertySymbols
, pero es algo incómodo de hacer.
Ejemplo:
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)`.
Media respuesta: guiones bajos
El valor predeterminado anterior es usar una propiedad pública con un prefijo de subrayado. Aunque no es una propiedad privada de ninguna manera, esta convención es lo suficientemente frecuente como para hacer un buen trabajo al comunicar que los lectores deben tratar la propiedad como privada, lo que a menudo hace el trabajo. A cambio de este lapso, obtenemos un enfoque que es más fácil de leer, más fácil de escribir y más rápido.
Ejemplo:
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.
Conclusión
A partir de ES2017, todavía no hay una manera perfecta de hacer propiedades privadas. Varios enfoques tienen pros y contras. Las variables de ámbito son verdaderamente privadas; los WeakMaps con ámbito son muy privados y más prácticos que las variables con ámbito; Los símbolos de alcance son razonablemente privados y razonablemente prácticos; los guiones bajos suelen ser lo suficientemente privados y muy prácticos.