Propriedades privadas nas classes JavaScript ES6


444

É possível criar propriedades particulares nas classes ES6?

Aqui está um exemplo. Como posso impedir o acesso instance.property?

class Something {
  constructor(){
    this.property = "test";
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"

5
Na verdade, existe uma proposta de estágio 3 para esse recurso - tc39.github.io/proposal-class-fields github.com/tc39/proposal-class-fields #
arty

@arty Forneci uma resposta para isso com exemplos: stackoverflow.com/a/52237988/1432509
Alister

Respostas:


165

Campos privados (e métodos) estão sendo implementados no padrão ECMA . Você pode começar a usá-los hoje com as predefinições babel 7 e estágio 3.

class Something {
  #property;

  constructor(){
    this.#property = "test";
  }

  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
      return this.#privateMethod();
  }
}

const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> hello world

Estou me perguntando como esses campos de classe podem funcionar. No momento, você não pode usar thisno construtor antes de ligar super(). No entanto, babel os coloca antes de super.
Seeker_of_bacon

Como configurar o ESLint para permitir a #privateCrapsintaxe?
Marecky

6
E o eslint? Eu tenho erro de analisador no sinal de igual. Babel está funcionando, mas o slint não pode analisar esta nova sintaxe js.
martonx

6
Uau, isso é muito feio. Hashtag é um caractere válido. A propriedade não é realmente privada, ou? .. Eu verifiquei no TypeScript. Membros privados não são compilados em privado ou somente leitura (de fora). Apenas declarado como outra propriedade (pública). (ES5)
Dominik

2
Como você escreve métodos particulares com isso? Posso fazer isso #beep() {}:; e isso async #bzzzt() {}:?
Константин Ван

277

Resposta curta, não, não há suporte nativo para propriedades particulares com classes ES6.

Mas você pode imitar esse comportamento não anexando as novas propriedades ao objeto, mas mantendo-as dentro de um construtor de classe e usar getters e setters para alcançar as propriedades ocultas. Observe que os getters e setters são redefinidos a cada nova instância da classe.

ES6

class Person {
    constructor(name) {
        var _name = name
        this.setName = function(name) { _name = name; }
        this.getName = function() { return _name; }
    }
}

ES5

function Person(name) {
    var _name = name
    this.setName = function(name) { _name = name; }
    this.getName = function() { return _name; }
}

1
Eu gosto desta solução da melhor maneira. Concordo que não deve ser usado para dimensionamento, mas é perfeito para classes que geralmente serão instanciadas apenas uma vez por inclusão.
Blake Regalia

2
Além disso, você está redefinindo cada componente desta classe cada vez que um novo é criado.
Quentin Roy

10
Isto é tão estranho! No ES6, você está criando mais "pirâmides de fechamento" do que antes do ES6! Definir funções DENTRO de um construtor parece mais feio do que no exemplo acima do ES5.
Kokodoko #

1
Como o OP pergunta especificamente sobre as classes ES6, eu pessoalmente acho que essa é uma solução ruim, mesmo que tecnicamente funcione. A principal limitação é que agora todo método de classe que usa variáveis ​​privadas deve ser declarado dentro do construtor, minando severamente as vantagens de ter classsintaxe em primeiro lugar.
NanoWizard

10
Tudo isso faz é introduzir indireção. Agora, como você torna as propriedades getNamee setNameprivadas?
12/04/19

195

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 Mapcriará 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.


7
O primeiro exemplo de snippet ("variáveis ​​com escopo definido") é um antipadrão total - cada objeto retornado terá uma classe diferente. Não faça isso. Se você deseja métodos privilegiados, crie-os no construtor.
Bergi 5/11

1
O agrupamento de uma classe dentro de uma função parece derrotar todo o propósito de usar classes em primeiro lugar. Se você já usa a função para criar uma instância, também pode colocar todos os seus membros públicos / privados nessa função e esquecer toda a palavra-chave da classe.
Kokodoko

2
@Bergi @Kokodoko Editei a abordagem de variáveis ​​com escopo para ser um pouco mais rápida e não quebrar instanceof. Admito que estava pensando nessa abordagem como incluída apenas por uma questão de exaustividade e deveria ter pensado mais em quanto ela é realmente capaz.
Tristan

1
Excelente explicação! Ainda estou surpreso que o ES6 tenha realmente dificultado a simulação de uma variável privada, onde no ES5 você poderia usar var e isso dentro de uma função para simular o privado e o público.
Kokodoko #

2
@Kokodoko Se você dispensar a classe e apenas colocar tudo na função, também precisará reverter para implementar a herança usando o método prototype. Usar a extensão nas classes é de longe uma abordagem mais limpa, portanto, usar uma classe dentro de uma função é totalmente aceitável.
AndroidDev 24/05

117

Atualização: uma proposta com sintaxe mais agradável está a caminho. Contribuições são bem-vindas.


Sim, existe - para acesso com escopo definido em objetos - o ES6 apresenta Symbols .

Os símbolos são únicos, você não pode obter acesso a um externo, exceto com reflexão (como privates em Java / C #), mas qualquer pessoa que tenha acesso a um símbolo interno pode usá-lo para acesso à chave:

var property = Symbol();
class Something {
    constructor(){
        this[property] = "test";
    }
}

var instance = new Something();

console.log(instance.property); //=> undefined, can only access with access to the Symbol

6
Você não pode usar Object.getOwnPropertySymbols? ;)
Qantas 94 Heavy

41
@BenjaminGruenbaum: Aparentemente, os símbolos não garantem mais a privacidade real: stackoverflow.com/a/22280202/1282216
d13 19/14

28
@trusktr através de três chaves? Não. Através dos símbolos? Sim. Muito parecido com como você pode usar a reflexão em linguagens como C # e Java para acessar campos particulares. Modificadores de acesso não são sobre segurança - eles são sobre clareza de intenção.
Benjamin Gruenbaum 26/10

9
Parece que usar Símbolos é semelhante a fazer const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();. Isso não é realmente privacidade, é obscuridade, no sentido do JavaScript tradicional. Eu consideraria JavaScript "privado" como o uso de fechamentos para encapsular variáveis. Portanto, essas variáveis ​​não são acessíveis através da reflexão.
trusktr

13
Além disso, acho que usar as palavras private- protectedchave e seria muito mais limpo que Symbolou Name. Eu prefiro a notação de ponto em vez da colchete. Eu gostaria de continuar usando um ponto para coisas particulares. this.privateVar
trusktr

33

A resposta é não". Mas você pode criar acesso privado a propriedades como esta:

(A sugestão de que o Symbols pudesse ser usado para garantir a privacidade era verdadeira em uma versão anterior da especificação ES6, mas não é mais o caso: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604. html e https://stackoverflow.com/a/22280202/1282216 . Para uma discussão mais longa sobre símbolos e privacidade, consulte: https://curiosity-driven.org/private-properties-in-javascript )


6
-1, isso realmente não responde à sua pergunta. (Você também pode usar fechamentos com IIFEs no ES5). Propriedades privadas são enumeráveis ​​através da reflexão na maioria dos idiomas (Java, C #, etc). O objetivo das propriedades privadas é transmitir a intenção a outros programadores e não reforçar a segurança.
Benjamin Gruenbaum

1
@BenjaminGruenbaum, eu sei, eu gostaria de ter uma resposta melhor, também não estou feliz com isso.
d13

Eu acho que os símbolos ainda são uma maneira válida de obter membros inacessíveis enquanto estiver no ambiente de programação. Sim, eles ainda podem ser encontrados se você realmente quiser, mas esse não é o ponto, não é? Você não deve armazenar informações confidenciais, mas não deve fazê-lo de qualquer maneira no código do cliente. Mas funciona com o objetivo de ocultar uma propriedade ou método de uma classe externa.
Kokodoko

O uso de variáveis ​​com escopo no nível de um módulo como substituto de propriedades privadas em uma classe levará a um comportamento singleton.behavior ou similart a propriedades statitc. As instâncias de vars serão compartilhadas.
Adrian Moisa

30

A única maneira de obter verdadeira privacidade no JS é através do escopo, portanto, não há como ter uma propriedade que seja membro, thisque estará acessível apenas dentro do componente. A melhor maneira de armazenar dados verdadeiramente privados no ES6 é com um WeakMap.

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    privateProp1.set(this, "I am Private1");
    privateProp2.set(this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(privateProp1.get(this), privateProp2.get(this))
    };        
  }

  printPrivate() {
    console.log(privateProp1.get(this));
  }
}

Obviamente, isso é provavelmente lento, e definitivamente feio, mas fornece privacidade.

Lembre-se de que MESMO ISSO não é perfeito, porque o Javascript é muito dinâmico. Alguém ainda poderia fazer

var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};

para capturar valores à medida que são armazenados, por isso, se você quiser ter cuidado extra, precisará capturar uma referência local .sete .getusar explicitamente em vez de confiar no protótipo substituível.

const {set: WMSet, get: WMGet} = WeakMap.prototype;

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    WMSet.call(privateProp1, this, "I am Private1");
    WMSet.call(privateProp2, this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
    };        
  }

  printPrivate() {
    console.log(WMGet.call(privateProp1, this));
  }
}

3
Como sugestão, você pode evitar o uso de um mapa fraco por propriedade usando um objeto como valor. Dessa forma, você também pode reduzir o número de mapas getpara um por método (por exemplo const _ = privates.get(this); console.log(_.privateProp1);).
Quentin Roy

Sim, isso também é totalmente uma opção. Eu principalmente fui com isso, pois ele mapeia mais diretamente o que um usuário teria escrito ao usar propriedades reais.
loganfsmyth

@loganfsmyth const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"isso significa que sua propriedade é privada ou não?
Barbu Barbu

2
Para que isso funcione, o código de acesso a propriedade seria necessário acesso ao objeto WeakMap, que normalmente seria escopo dentro de um módulo e inacessível
loganfsmyth

22

Para referência futura de outros usuários, estou ouvindo agora que a recomendação é usar o WeakMaps para armazenar dados particulares.

Aqui está um exemplo de trabalho mais claro:

function storePrivateProperties(a, b, c, d) {
  let privateData = new WeakMap;
  // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value 
  let keyA = {}, keyB = {}, keyC = {}, keyD = {};

  privateData.set(keyA, a);
  privateData.set(keyB, b);
  privateData.set(keyC, c);
  privateData.set(keyD, d);

  return {
    logPrivateKey(key) {
      switch(key) {
      case "a":
        console.log(privateData.get(keyA));
        break;
      case "b":
        console.log(privateData.get(keyB));
        break;
      case "c":
        console.log(privateData.get(keyC));
        break;
      case "d":
        console.log(privateData.set(keyD));
        break;
      default:
        console.log(`There is no value for ${key}`)
      }
    }
  }
}

20
Esteja ciente de que essas propriedades são estáticas.
Michael Theriot

8
Eu não diminuí a votação, mas seu exemplo de mapa fraco está completamente errado.
Benjamin Gruenbaum

4
Ou seja - você está compartilhando os dados entre todas as instâncias de classe e não por instância - posso pelo menos corrigi-lo?
Benjamin Gruenbaum 04/04

1
De fato, o mapa fraco precisa ser anexado a uma determinada instância. Veja fitzgeraldnick.com/weblog/53 para um exemplo.
widged

2
De acordo com o MDN, tipos de dados primitivos, como símbolos, não são permitidos como chave WeakMap. Documentação do MDN WeakMap
leepowell

12

Depende de quem você pergunta :-)

Nenhum privatemodificador de propriedade está incluído na proposta Maximally minimal classes , que parece ter entrado no rascunho atual .

No entanto, pode haver suporte para nomes particulares , o que permite propriedades particulares - e elas provavelmente também podem ser usadas nas definições de classe.


3
É altamente improvável que nomes particulares cheguem ao ES6, embora estejam pensando em alguma forma de coisa privada para o ES7.
Qantas 94 Heavy

@ Qantas94Heavy nomes particulares e valores únicos de sequência foram substituídos pelos símbolos pelo que entendi.
Benjamin Gruenbaum

Sim, provavelmente se tornará símbolos. Entretanto, os "símbolos" atualmente contidos na especificação são usados ​​apenas para descrever propriedades internas como [[prototype]], e não há como criá-las e usá-las no código do usuário. Você conhece alguns documentos?
Bergi

Acabei de perceber que os módulos podem ser usados ​​para definir a privacidade. Combinado com símbolos que podem ser tudo o que você precisa ...?
d13

1
@ Cody: Todo o código do seu módulo tem seu próprio escopo no ES6, de qualquer maneira, sem a necessidade de um IEFE. E sim, os símbolos são destinados à singularidade (prevenção de colisões), não à privacidade.
Bergi 04/09/2015

10

O uso de módulos ES6 (inicialmente proposto por @ d13) funciona bem para mim. Ele não imita propriedades privadas perfeitamente, mas pelo menos você pode ter certeza de que propriedades que deveriam ser privadas não vazarão para fora da sua classe. Aqui está um exemplo:

something.js

let _message = null;
const _greet = name => {
  console.log('Hello ' + name);
};

export default class Something {
  constructor(message) {
    _message = message;
  }

  say() {
    console.log(_message);
    _greet('Bob');
  }
};

Em seguida, o código de consumo pode ficar assim:

import Something from './something.js';

const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception

Atualização (importante):

Como @DanyalAytekin descrito nos comentários, essas propriedades privadas são estáticas, portanto, de escopo global. Eles funcionarão bem ao trabalhar com Singletons, mas é preciso ter cuidado com objetos transitórios. Estendendo o exemplo acima:

import Something from './something.js';
import Something2 from './something.js';

const a = new Something('a');
a.say(); // a

const b = new Something('b');
b.say(); // b

const c = new Something2('c');
c.say(); // c

a.say(); // c
b.say(); // c
c.say(); // c

4
Bom para private static.
Danyal Aytekin 23/03

@DanyalAytekin: esse é um ponto muito bom. Essas propriedades privadas são estáticas e têm escopo global. Atualizei minha resposta para refletir isso.
Johnny Oshika

Quanto mais eu aprendo sobre programação funcional (especialmente Elm e Haskell), mais acredito que os programadores JS se beneficiariam de uma abordagem baseada em módulo à "modularidade" em vez de uma abordagem baseada em classe OOP. Se pensarmos nos módulos ES6 como os fundamentos para a criação de aplicativos e esquecermos completamente as classes, acredito que podemos acabar com aplicativos muito melhores em geral. Algum usuário experiente de Elm ou Haskell pode comentar sobre essa abordagem?
d13

1
Na atualização, o segundo a.say(); // adeve serb.say(); // b
grokky

tentado let _message = null, não tão legal, quando o construtor de chamadas várias vezes, ele estraga tudo.
Littlee 30/03

9

Concluindo @ d13 e os comentários de @ johnny-oshika e @DanyalAytekin:

Eu acho que no exemplo fornecido por @ johnny-oshika, poderíamos usar funções normais em vez de funções de seta e depois .bindcom o objeto atual mais um_privates objeto como um parâmetro ao curry:

something.js

function _greet(_privates) {
  return 'Hello ' + _privates.message;
}

function _updateMessage(_privates, newMessage) {
  _privates.message = newMessage;
}

export default class Something {
  constructor(message) {
    const _privates = {
      message
    };

    this.say = _greet.bind(this, _privates);
    this.updateMessage = _updateMessage.bind(this, _privates);
  }
}

main.js

import Something from './something.js';

const something = new Something('Sunny day!');

const message1 = something.say();
something.updateMessage('Cloudy day!');
const message2 = something.say();

console.log(message1 === 'Hello Sunny day!');  // true
console.log(message2 === 'Hello Cloudy day!');  // true

// the followings are not public
console.log(something._greet === undefined);  // true
console.log(something._privates === undefined);  // true
console.log(something._updateMessage === undefined);  // true

// another instance which doesn't share the _privates
const something2 = new Something('another Sunny day!');

const message3 = something2.say();

console.log(message3 === 'Hello another Sunny day!'); // true

Benefícios em que posso pensar:

  • podemos ter métodos privados ( _greete _updateMessageagir como métodos privados, desde que nãoexport as referências)
  • embora não estejam no protótipo, os métodos mencionados acima economizarão memória porque as instâncias são criadas uma vez, fora da classe (em vez de defini-las no construtor)
  • não vazamos globais, pois estamos dentro de um módulo
  • também podemos ter propriedades particulares usando o _privatesobjeto vinculado

Algumas desvantagens que posso pensar:

Um snippet em execução pode ser encontrado aqui: http://www.webpackbin.com/NJgI5J8lZ


8

Sim - você pode criar uma propriedade encapsulada , mas isso não foi feito com modificadores de acesso (público | privado) pelo menos não com o ES6.

Aqui está um exemplo simples de como isso pode ser feito com o ES6:

1 Crie uma classe usando a palavra da classe

2 Dentro do construtor declarar bloco escopo variável usando let OU const palavras reservadas -> uma vez que são bloco-scope eles não podem ser acessados de fora (encapsulados)

3 Para permitir algum controle de acesso (setters | getters) a essas variáveis, você pode declarar o método da instância dentro do construtor usando: this.methodName=function(){}sintaxe

"use strict";
    class Something{
        constructor(){
            //private property
            let property="test";
            //private final (immutable) property
            const property2="test2";
            //public getter
            this.getProperty2=function(){
                return property2;
            }
            //public getter
            this.getProperty=function(){
                return property;
            }
            //public setter
            this.setProperty=function(prop){
                property=prop;
            }
        }
    }

Agora vamos verificar:

var s=new Something();
    console.log(typeof s.property);//undefined 
    s.setProperty("another");//set to encapsulated `property`
    console.log(s.getProperty());//get encapsulated `property` value
    console.log(s.getProperty2());//get encapsulated immutable `property2` value

1
Essa é (por enquanto) a única solução para esse problema, apesar de todos os métodos declarados no construtor serem redeclarados para cada instância da classe. Essa é uma péssima idéia sobre desempenho e uso de memória. Os métodos de classe devem ser declarados fora do escopo do construtor.
Freezystem

@ Freezystem First: Primeiro, esses são métodos de instância (não métodos de classe). A segunda pergunta do OP foi: _ Como posso impedir o acesso ao instance.property?_ e minha resposta é: um exemplo de como ... Terceiro, se você tiver alguma idéia melhor - vamos ouvi-lo
Nikita Kurtin

1
Eu não estava dizendo que você estava errado, eu disse que sua solução era o melhor compromisso para obter variável privada, apesar do fato de que uma cópia de cada método de instância é criada toda vez que você chama, new Something();porque seus métodos são declarados no construtor para ter acesso a estes. variáveis ​​privadas. Isso pode causar muito consumo de memória se você criar muitas instâncias da sua classe, portanto, problemas de desempenho. Os métodos deveriam ter sido declarados fora do escopo do construtor. Meu comentário foi mais uma explicação para as desvantagens de sua solução do que uma crítica.
Freezystem

1
Mas não é uma prática ruim definir sua classe inteira dentro do construtor? Não estamos apenas "invadindo" o javascript agora? Basta olhar para qualquer outra linguagem de programação OOP e você verá que o construtor não deve definir uma classe.
Kokodoko #

1
Sim, foi isso que eu quis dizer e sua solução funciona! Estou apenas dizendo que, em geral, estou surpreso por o ES6 ter adicionado uma palavra-chave 'class', mas removido a solução elegante de trabalhar com var e isso, para obter o encapsulamento.
Kokodoko

8

Uma abordagem diferente para "privado"

Em vez de lutar contra o fato de que atualmente a visibilidade privada não está disponível no ES6, decidi adotar uma abordagem mais prática que funcionaria bem se o seu IDE oferecer suporte ao JSDoc (por exemplo, Webstorm). A ideia é usar a @privatetag . No que diz respeito ao desenvolvimento, o IDE impedirá que você acesse qualquer membro privado de fora de sua classe. Funciona muito bem para mim e tem sido realmente útil para ocultar métodos internos, de modo que o recurso de preenchimento automático me mostra exatamente o que a classe realmente pretendia expor. Aqui está um exemplo:

preenchimento automático mostrando apenas coisas públicas


1
O problema é que não queremos acessar as variáveis ​​privadas sobre o Editor, não queremos proteger as variáveis ​​privadas de fora - E é isso que o público / privado faz. Se seu código estiver concluído, você poderá acessar (e o importante pensa: substituir ) essas variáveis ​​de fora da classe. Seu @privatecomentário não pode evitá-los, é apenas um Recurso para geração de documentação e você é IDE.
Adrian Preuss

Sim, eu estou ciente disso. É que isso é suficiente para mim e pode ser suficiente para outras pessoas por aí. Eu sei que não está realmente tornando minhas variáveis ​​privadas; está apenas me avisando para não tentar acessá-lo de fora (apenas, é claro, se minha equipe e eu estiver usando um IDE que suporte esse recurso). Javascript (e outros idiomas, como Python) não foi projetado com os níveis de acesso em mente. As pessoas fazem todo tipo de coisa para implementar de alguma forma essa funcionalidade, mas no final acabamos invadindo a linguagem para conseguir isso. Decidi seguir uma abordagem mais "natural", se você preferir.
Lucio Paiva

6

WeakMap

  • suportado no IE11 (os símbolos não são)
  • hard-private (adereços usando símbolos são soft-private devido a Object.getOwnPropertySymbols)
  • pode parecer realmente limpo (ao contrário de fechamentos que exigem todos os adereços e métodos no construtor)

Primeiro, defina uma função para agrupar o WeakMap:

function Private() {
  const map = new WeakMap();
  return obj => {
    let props = map.get(obj);
    if (!props) {
      props = {};
      map.set(obj, props);
    }
    return props;
  };
}

Em seguida, construa uma referência fora da sua classe:

const p = new Private();

class Person {
  constructor(name, age) {
    this.name = name;
    p(this).age = age; // it's easy to set a private variable
  }

  getAge() {
    return p(this).age; // and get a private variable
  }
}

Nota: a classe não é suportada pelo IE11, mas parece mais limpa no exemplo.


6

Oh, tantas soluções exóticas! Normalmente não me importo com privacidade, então uso "pseudo privacidade", como é dito aqui . Mas se me importo (se houver alguns requisitos especiais para isso) eu uso algo como neste exemplo:

class jobImpl{
  // public
  constructor(name){
    this.name = name;
  }
  // public
  do(time){
    console.log(`${this.name} started at ${time}`);
    this.prepare();
    this.execute();
  }
  //public
  stop(time){
    this.finish();
    console.log(`${this.name} finished at ${time}`);
  }
  // private
  prepare(){ console.log('prepare..'); }
  // private
  execute(){ console.log('execute..'); }
  // private
  finish(){ console.log('finish..'); }
}

function Job(name){
  var impl = new jobImpl(name);
  return {
    do: time => impl.do(time),
    stop: time => impl.stop(time)
  };
}

// Test:
// create class "Job"
var j = new Job("Digging a ditch");
// call public members..
j.do("08:00am");
j.stop("06:00pm");

// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error

Outra implementação possível da função (construtor) Job:

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}

5

Pessoalmente, gosto da proposta do operador bind :: e a combinaria com a solução @ d13 mencionada, mas, por enquanto, atenha-se à resposta da @ d13, onde você usa a exportpalavra - chave para sua classe e coloca as funções privadas no módulo.

há mais uma solução difícil que não foi mencionada aqui a seguir, é uma abordagem mais funcional e permitiria que ele tivesse todos os métodos / props privados da classe.

Private.js

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }

Test.js

import { get, set } from './utils/Private'
export default class Test {
  constructor(initialState = {}) {
    const _set = this.set = set(initialState);
    const _get = this.get = get(initialState);

    this.set('privateMethod', () => _get('propValue'));
  }

  showProp() {
    return this.get('privateMethod')();
  }
}

let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5

comentários sobre isso seriam apreciados.


Geralmente eu gosto da abordagem. Feedback: 1. você precisará de um módulo private.js diferente para cada classe para evitar conflitos. 2. Não gosto do potencial de tornar o construtor muito longo ao definir em linha cada um dos seus métodos particulares. 3. Seria bom se todos os métodos de classe estivessem em um arquivo.
Doug Coburn

5

Me deparei com este post ao procurar a melhor prática para "dados privados para aulas". Foi mencionado que alguns dos padrões teriam problemas de desempenho.

Eu montei alguns testes jsperf com base nos quatro padrões principais do livro on-line "Explorando o ES6":

http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes

Os testes podem ser encontrados aqui:

https://jsperf.com/private-data-for-classes

No Chrome 63.0.3239 / Mac OS X 10.11.6, os padrões com melhor desempenho eram "Dados privados por meio de ambientes de construtores" e "Dados privados por meio de uma convenção de nomenclatura". Para mim, o Safari teve um bom desempenho no WeakMap, mas o Chrome não tão bem.

Não conheço o impacto da memória, mas o padrão para "ambientes construtores", que alguns haviam avisado que seria um problema de desempenho, foi muito eficiente.

Os 4 padrões básicos são:

Dados privados via ambientes construtores

class Countdown {
    constructor(counter, action) {
        Object.assign(this, {
            dec() {
                if (counter < 1) return;
                counter--;
                if (counter === 0) {
                    action();
                }
            }
        });
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Dados privados via ambientes construtores 2

class Countdown {
    constructor(counter, action) {
        this.dec = function dec() {
            if (counter < 1) return;
            counter--;
            if (counter === 0) {
                action();
            }
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Dados privados por meio de uma convenção de nomenclatura

class Countdown {
    constructor(counter, action) {
        this._counter = counter;
        this._action = action;
    }
    dec() {
        if (this._counter < 1) return;
        this._counter--;
        if (this._counter === 0) {
            this._action();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Dados privados via WeakMaps

const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
    constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
    }
    dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
            _action.get(this)();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Dados privados via símbolos

const _counter = Symbol('counter');
const _action = Symbol('action');

class Countdown {
    constructor(counter, action) {
        this[_counter] = counter;
        this[_action] = action;
    }
    dec() {
        if (this[_counter] < 1) return;
        this[_counter]--;
        if (this[_counter] === 0) {
            this[_action]();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

4

Eu acredito que é possível obter o 'melhor dos dois mundos' usando fechamentos dentro de construtores. Existem duas variações:

Todos os membros dos dados são privados

function myFunc() {
   console.log('Value of x: ' + this.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   console.log('Enhanced value of x: ' + (this.x + 1));
}

class Test {
   constructor() {

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(internal);
      
      this.myFunc = myFunc.bind(internal);
   }
};

Alguns membros são privados

NOTA: Isso é reconhecidamente feio. Se você conhece uma solução melhor, edite esta resposta.

function myFunc(priv, pub) {
   pub.y = 3; // The Test object now gets a member 'y' with value 3.
   console.log('Value of x: ' + priv.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   pub.z = 5; // The Test object now gets a member 'z' with value 3.
   console.log('Enhanced value of x: ' + (priv.x + 1));
}

class Test {
   constructor() {
      
      let self = this;

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self);
      
      this.myFunc = myFunc.bind(null, internal, self);
   }
};


4

De fato, é possível usar Símbolos e Proxies. Você usa os símbolos no escopo da classe e define dois desvios em um proxy: um para o protótipo de classe, para que o Reflect.ownKeys (instance) ou Object.getOwnPropertySymbols não divulgue seus símbolos, o outro é para o próprio construtor portanto, quando new ClassName(attrs)for chamada, a instância retornada será interceptada e terá os próprios símbolos de propriedades bloqueados. Aqui está o código:

const Human = (function() {
  const pet = Symbol();
  const greet = Symbol();

  const Human = privatizeSymbolsInFn(function(name) {
    this.name = name; // public
    this[pet] = 'dog'; // private 
  });

  Human.prototype = privatizeSymbolsInObj({
    [greet]() { // private
      return 'Hi there!';
    },
    revealSecrets() {
      console.log(this[greet]() + ` The pet is a ${this[pet]}`);
    }
  });

  return Human;
})();

const bob = new Human('Bob');

console.assert(bob instanceof Human);
console.assert(Reflect.ownKeys(bob).length === 1) // only ['name']
console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets']


// Setting up the traps inside proxies:
function privatizeSymbolsInObj(target) { 
  return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
}

function privatizeSymbolsInFn(Class) {
  function construct(TargetClass, argsList) {
    const instance = new TargetClass(...argsList);
    return privatizeSymbolsInObj(instance);
  }
  return new Proxy(Class, { construct });
}

Reflect.ownKeys()funciona assim: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))é por isso que precisamos de uma armadilha para esses objetos.


Obrigado, vou tentar símbolos :) De todas as respostas acima, parece que a maneira mais simples de criar um membro da classe inacessível :)
Kokodoko

4

Mesmo o Typecript não pode fazer isso. Da documentação deles :

Quando um membro é marcado como privado, ele não pode ser acessado de fora da classe que o contém. Por exemplo:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

Mas transpiled em seu playground isso dá:

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    return Animal;
}());
console.log(new Animal("Cat").name);

Portanto, a palavra-chave "particular" é ineficaz.


2
Bem, ainda é eficaz porque impede a programação "ruim" enquanto estiver no IDE. Ele mostra quais membros você deve ou não usar. Eu acho que essa é a principal razão para o uso privado e público. (Por exemplo, quando você compila C # para código de máquina, o privado ainda será privado? Quem sabe?). Ao ler as outras respostas, parece que o uso do @Symbol também pode tornar um membro inacessível. Mas mesmo os símbolos ainda podem ser encontrados no console.
Kokodoko

O erro do TypeScript ocorre durante a transpilação do TypeScript para JavaScript? (Como a verificação de tipo acontece em tempo de transpite Ao invés de algum mecanismo privado de tempo de execução..)
Eljay

4

Chegando muito tarde para esta festa, mas eu acertei a pergunta do OP em uma pesquisa, então ... Sim, você pode ter propriedades particulares envolvendo a declaração de classe em um fechamento

Há um exemplo de como eu tenho métodos particulares neste codepen . No snippet abaixo, a classe Subscribable possui duas funções 'private' processe processCallbacks. Quaisquer propriedades podem ser adicionadas dessa maneira e são mantidas em sigilo pelo uso do fechamento. A privacidade da IMO é uma necessidade rara, se as preocupações estiverem bem separadas e o Javascript não precisar ficar inchado, adicionando mais sintaxe quando um fechamento faz o trabalho corretamente.

const Subscribable = (function(){

  const process = (self, eventName, args) => {
    self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};

  const processCallbacks = (self, eventName, args) => {
    if (self.callingBack.get(eventName).length > 0){
      const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
      self.callingBack.set(eventName, callingBack);
      process(self, eventName, args);
      nextCallback(...args)}
    else {
      delete self.processing.delete(eventName)}};

  return class {
    constructor(){
      this.callingBack = new Map();
      this.processing = new Map();
      this.toCallbacks = new Map()}

    subscribe(eventName, callback){
      const callbacks = this.unsubscribe(eventName, callback);
      this.toCallbacks.set(eventName,  [...callbacks, callback]);
      return () => this.unsubscribe(eventName, callback)}  // callable to unsubscribe for convenience

    unsubscribe(eventName, callback){
      let callbacks = this.toCallbacks.get(eventName) || [];
      callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
      if (callbacks.length > 0) {
        this.toCallbacks.set(eventName, callbacks)}
      else {
        this.toCallbacks.delete(eventName)}
      return callbacks}

    emit(eventName, ...args){
      this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
      if (!this.processing.has(eventName)){
        process(this, eventName, args)}}}})();

Eu gosto dessa abordagem porque ela separa bem as preocupações e mantém as coisas realmente privadas. A única desvantagem é a necessidade de usar 'self' (ou algo semelhante) para se referir a 'this' no conteúdo privado.


4

Acho que a resposta de Benjamin é provavelmente a melhor para a maioria dos casos, até que a linguagem suporte nativamente variáveis ​​explicitamente privadas.

No entanto, se por algum motivo você precisar impedir o acesso Object.getOwnPropertySymbols(), um método que considerei usar é anexar uma propriedade exclusiva, não configurável, não enumerável e não gravável, que pode ser usada como um identificador de propriedade para cada objeto em construção (como um exclusivo Symbol, se você ainda não tiver outra propriedade exclusiva, como um id). Em seguida, basta manter um mapa das variáveis ​​'privadas' de cada objeto usando esse identificador.

const privateVars = {};

class Something {
    constructor(){
        Object.defineProperty(this, '_sym', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: Symbol()
        });

        var myPrivateVars = {
            privateProperty: "I'm hidden"
        };

        privateVars[this._sym] = myPrivateVars;

        this.property = "I'm public";
    }

    getPrivateProperty() {
        return privateVars[this._sym].privateProperty;
    }

    // A clean up method of some kind is necessary since the
    // variables won't be cleaned up from memory automatically
    // when the object is garbage collected
    destroy() {
        delete privateVars[this._sym];
    }
}

var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"

A vantagem potencial dessa abordagem sobre o uso de a WeakMapé um tempo de acesso mais rápido se o desempenho se tornar uma preocupação.


1
Corrija-me se eu estiver errado, mas esse código não conteria vazamentos de memória, já que privateVars ainda armazenará as variáveis ​​privadas de um objeto, mesmo que o objeto já esteja destruído?
Russell Santos

@RussellSantos, você está certo, assumindo que os objetos precisarão ser coletados em algum momento. Obrigado por apontar isso. No meu exemplo, adicionei um destroy()método que deve ser chamado pelo código using sempre que um objeto precisar ser removido.
NanoWizard

4

Sim, totalmente pode, e muito facilmente também. Isso é feito expondo suas variáveis ​​e funções privadas retornando o gráfico de objeto de protótipo no construtor. Isso não é novidade, mas tome um pouco de tempo para entender a elegância disso. Dessa forma, não utiliza escopos globais ou mapas fracos. É uma forma de reflexão embutida na linguagem. Dependendo de como você aproveita isso; pode-se forçar uma exceção que interrompe a pilha de chamadas ou enterrar a exceção como um undefined. Isso é iniciado abaixo e pode ler mais sobre esses recursos aqui

class Clazz {
  constructor() {
    var _level = 1

    function _private(x) {
      return _level * x;
    }
    return {
      level: _level,
      public: this.private,
      public2: function(x) {
        return _private(x);
      },
      public3: function(x) {
        return _private(x) * this.public(x);
      },
    };
  }

  private(x) {
    return x * x;
  }
}

var clazz = new Clazz();

console.log(clazz._level); //undefined
console.log(clazz._private); // undefined
console.log(clazz.level); // 1
console.log(clazz.public(1)); //1
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error


3
class Something {
  constructor(){
    var _property = "test";
    Object.defineProperty(this, "property", {
        get: function(){ return _property}
    });
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"
instance.property = "can read from outside, but can't write";
console.log(instance.property); //=> "test"

2
É melhor evitar respostas apenas de código. Seria melhor se você pudesse explicar como seu código responde a pergunta do OP
Stewart_R

Isto é realmente como tornar uma variável somente leitura mais do que uma variável privada. Uma variável privada não deve ser acessível para o exterior. console.log(instance.property)deve jogar ou lhe dar indefinido, não devolver "teste".
oooyaya

3

Outra maneira semelhante às duas últimas postadas

class Example {
  constructor(foo) {

    // privates
    const self = this;
    this.foo = foo;

    // public interface
    return self.public;
  }

  public = {
    // empty data
    nodata: { data: [] },
    // noop
    noop: () => {},
  }

  // everything else private
  bar = 10
}

const test = new Example('FOO');
console.log(test.foo); // undefined
console.log(test.noop); // { data: [] }
console.log(test.bar); // undefined

2

A maioria das respostas diz que é impossível ou exige que você use um WeakMap ou Symbol, que são recursos do ES6 que provavelmente exigiriam polyfills. No entanto, há outra maneira! Confira isso:

// 1. Create closure
var SomeClass = function() {
  // 2. Create `key` inside a closure
  var key = {};
  // Function to create private storage
  var private = function() {
    var obj = {};
    // return Function to access private storage using `key`
    return function(testkey) {
      if(key === testkey) return obj;
      // If `key` is wrong, then storage cannot be accessed
      console.error('Cannot access private properties');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Create private storage
    this._ = private();
    // 4. Access private storage using the `key`
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Using property from prototype
  };
  return SomeClass;
}();

// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged

// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged

Eu chamo esse padrão de acessador de método . A idéia essencial é que tenhamos um fechamento , uma chave dentro do fechamento, e criamos um objeto privado (no construtor) que só pode ser acessado se você tiver a chave .

Se você estiver interessado, pode ler mais sobre isso no meu artigo . Usando esse método, você pode criar propriedades por objeto que não podem ser acessadas fora do fechamento. Portanto, você pode usá-los no construtor ou protótipo, mas não em nenhum outro lugar. Não vi esse método usado em nenhum lugar, mas acho que é realmente poderoso.


A questão era sobre como conseguir isso nas aulas de ES6.
precisa saber é o seguinte

Você pode usar exatamente o mesmo método nas classes ES6. As aulas ES6 são principalmente apenas açúcar, além de funções como apresentei no meu exemplo. É bem possível que o pôster original esteja usando um transpiler; nesse caso, WeakMaps ou Symbols ainda exigirão polyfills. Minha resposta é válida independentemente.
Violino #

2

Consulte esta resposta para obter uma solução 'classe' limpa e simples com uma interface pública e privada e suporte para composição


2

Encontrei uma solução muito simples, basta usar Object.freeze(). Obviamente, o problema é que você não pode adicionar nada ao objeto posteriormente.

class Cat {
    constructor(name ,age) {
        this.name = name
        this.age = age
        Object.freeze(this)
    }
}

let cat = new Cat('Garfield', 5)
cat.age = 6 // doesn't work, even throws an error in strict mode

Isso também irá desativar o método setter comosetName(name) { this.name = name; }
ngakak

2

Eu uso esse padrão e sempre funcionou para mim

class Test {
    constructor(data) {
        class Public {
            constructor(prv) {

                // public function (must be in constructor on order to access "prv" variable)
                connectToDb(ip) {
                    prv._db(ip, prv._err);
                } 
            }

            // public function w/o access to "prv" variable
            log() {
                console.log("I'm logging");
            }
        }

        // private variables
        this._data = data;
        this._err = function(ip) {
            console.log("could not connect to "+ip);
        }
    }

    // private function
    _db(ip, err) {
        if(!!ip) {
		    console.log("connected to "+ip+", sending data '"+this.data+"'");
			return true;
		}
        else err(ip);
    }
}



var test = new Test(10),
		ip = "185.167.210.49";
test.connectToDb(ip); // true
test.log(); // I'm logging
test._err(ip); // undefined
test._db(ip, function() { console.log("You have got hacked!"); }); // undefined


2

Na verdade é possível.
1. Primeiro, crie a classe e no construtor retorne a _publicfunção chamada .
2. Na _publicfunção chamada , passe a thisreferência (para obter acesso a todos os métodos e props privados) e todos os argumentos de constructor (que serão passados new Names())
3. No _publicescopo da função, também há a Namesclasse com acesso this(_this ) referência da Namesclasse privada

class Names {
  constructor() {
    this.privateProperty = 'John';
    return _public(this, arguments);
  }
  privateMethod() { }
}

const names = new Names(1,2,3);
console.log(names.somePublicMethod); //[Function]
console.log(names.publicProperty); //'Jasmine'
console.log(names.privateMethod); //undefined
console.log(names.privateProperty); //undefind

function _public(_this, _arguments) {
  class Names {
    constructor() {
      this.publicProperty = 'Jasmine';
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

    somePublicMethod() {
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

  }
  return new Names(..._arguments);
}

2

Você pode tentar isso https://www.npmjs.com/package/private-members

Este pacote salvará os membros por instância.

const pvt = require('private-members');
const _ = pvt();

let Exemplo = (function () {    
    function Exemplo() {
        _(this).msg = "Minha Mensagem";
    }

    _().mensagem = function() {
        return _(this).msg;
    }

    Exemplo.prototype.showMsg = function () {
        let msg = _(this).mensagem();
        console.log(msg);
    };

    return Exemplo;
})();

module.exports = Exemplo;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.