Para expandir a resposta de @ loganfsmyth:
Os únicos dados verdadeiramente privados em JavaScript ainda são variáveis de escopo. Você não pode ter propriedades privadas no sentido de propriedades acessadas internamente da mesma maneira que propriedades públicas, mas pode usar variáveis de escopo para armazenar dados privados.
Variáveis de escopo
A abordagem aqui é usar o escopo da função construtora, que é privada, para armazenar dados privados. Para que os métodos tenham acesso a esses dados privados, eles também devem ser criados no construtor, o que significa que você os está recriando a cada instância. Essa é uma penalidade de desempenho e memória, mas alguns acreditam que a penalidade é aceitável. A penalidade pode ser evitada para métodos que não precisam de acesso a dados privados, adicionando-os ao protótipo como de costume.
Exemplo:
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
Escopo FracoMapa
Um WeakMap pode ser usado para evitar o desempenho da abordagem anterior e a penalidade de memória. O WeakMaps associa os dados aos Objetos (aqui, instâncias) de forma que eles só possam ser acessados usando esse WeakMap. Portanto, usamos o método de variáveis com escopo definido para criar um WeakMap privado e, em seguida, usamos esse WeakMap para recuperar dados privados associados athis
. Isso é mais rápido que o método das variáveis com escopo definido, porque todas as suas instâncias podem compartilhar um único WeakMap; portanto, você não precisa recriar métodos apenas para fazê-los acessar seus próprios WeakMaps.
Exemplo:
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 exemplo usa um Objeto para usar um WeakMap para várias propriedades particulares; você também pode usar vários WeakMaps e usá-los como age.set(this, 20)
, ou escrever um pequeno invólucro e usá-lo de outra maneira, comoprivateProps.set(this, 'age', 0)
.
Teoricamente, a privacidade dessa abordagem pode ser violada ao se adulterar o WeakMap
objeto . Dito isto, todo o JavaScript pode ser quebrado por globals mutilados. Nosso código já foi desenvolvido com base no pressuposto de que isso não está acontecendo.
(Esse método também pode ser feito Map
, mas WeakMap
é melhor, porque Map
criará vazamentos de memória, a menos que você tenha muito cuidado e, para esse fim, os dois não são diferentes.)
Meia resposta: símbolos com escopo
Um símbolo é um tipo de valor primitivo que pode servir como um nome de propriedade. Você pode usar o método de variável com escopo definido para criar um símbolo particular e armazenar dados particulares em this[mySymbol]
.
A privacidade desse método pode ser violada usando Object.getOwnPropertySymbols
, mas é um pouco difícil de fazer.
Exemplo:
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)`.
Meia resposta: sublinhados
O padrão antigo, basta usar uma propriedade pública com um prefixo de sublinhado. Embora não seja uma propriedade privada de forma alguma, essa convenção é predominante o suficiente para que ele faça um bom trabalho comunicando que os leitores devem tratar a propriedade como privada, o que geralmente faz o trabalho. Em troca desse lapso, obtemos uma abordagem mais fácil de ler, mais fácil de digitar e mais rápida.
Exemplo:
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.
Conclusão
No ES2017, ainda não havia uma maneira perfeita de fazer propriedades particulares. Várias abordagens têm prós e contras. Variáveis com escopo definido são verdadeiramente privadas; WeakMaps com escopo definido é muito particular e mais prático do que variáveis com escopo definido; Símbolos com escopo definido são razoavelmente particulares e razoavelmente práticos; sublinhados costumam ser privados o suficiente e muito práticos.