Propriedades privadas nas classes de JavaScript ES6

É possível criar propriedades privadas em classes ES6?

Eis um exemplo. Como posso impedir o acesso a instance.property?

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

var instance = new Something();
console.log(instance.property); //=> "test"
Author: Kirill Fuchs, 2014-03-03

30 answers

Os campos privados (e métodos) estão a ser implementados na normaECMA . Você pode começar a usá-los hoje com babel 7 e Fase 3 predefinida.

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
 202
Author: Alister, 2020-01-03 18:16:01
Resposta curta: Não, Não há suporte nativo para propriedades privadas com classes ES6.

Mas você poderia 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 escondidas. Note que os getters e setters são redefinidos em 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; }
}
 296
Author: MetalGodwin, 2018-05-11 19:33:47

Para expandir a resposta de @loganfsmyth:

Os únicos dados verdadeiramente privados em JavaScript ainda são as variáveis estudadas. Você não pode ter propriedades privadas no sentido de propriedades acessadas internamente da mesma forma que propriedades públicas, mas você pode usar variáveis escopadas para armazenar dados privados.

Variáveis analisadas

A abordagem aqui é usar o escopo da função construtora, que é privada, para armazenar dados privados. Para métodos de ter acesso a este privado os dados que eles devem ser criados dentro do construtor também, o que significa que você está recriando-os com cada instância. Esta é uma pena de desempenho e memória, mas alguns acreditam que a pena é 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

Scoped WeakMap

Um mapa fraco pode ser usado para evitar o desempenho e penalidade de memória da abordagem anterior. Dados associados do smakmaps com objetos (aqui, instâncias) de tal forma que só pode ser acessado usando esse WeakMap. Então, usamos o método das variáveis escopadas para criar um WeakMap privado, então usamos esse WeakMap para recuperar dados privados associados a this. Isto é mais rápido do que o método das variáveis escopadas porque todas as suas instâncias podem compartilhar um único WeakMap, então 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 objecto para usar um WeakMap para várias propriedades privadas; Você também pode usar vários WeakMaps e usá-los como age.set(this, 20), ou escrever uma pequena embalagem e usá-la de outra forma, como privateProps.set(this, 'age', 0).

A privacidade desta abordagem pode, teoricamente,ser violada por interferência no objecto global. Dito isto, todo JavaScript pode ser quebrado por globais mutilados. O nosso código já foi construído na suposição de que isto não está a acontecer.

(este método também pode ser feito com Map, mas WeakMap é melhor porque Vai criar fugas de memória a não ser que tenhas muito cuidado, e para isso os dois não são diferentes.)

Meia Resposta: Símbolos Escopados

Um símbolo é um tipo de valor primitivo que pode servir como um nome de propriedade. Você pode usar o método da variável escopo para criar um símbolo privado,em seguida, armazenar dados privados em this[mySymbol].

A privacidade deste método pode ser violada usando {[[12]}, mas é um pouco estranho para 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 antigo padrão, basta usar uma propriedade pública com um prefixo sublinhado. Embora não seja uma propriedade privada de qualquer forma, esta convenção é prevalente o suficiente para que faça um bom trabalho comunicando que os leitores devem tratar a propriedade como privada, o que muitas vezes faz o trabalho feito. Em troca deste lapso, obtemos uma abordagem que é mais fácil de ler, mais fácil de digitar, e rapidamente.

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

A partir de ES2017, ainda não há uma forma perfeita de fazer propriedades privadas. Várias abordagens têm prós e contras. Escopo de variáveis são verdadeiramente privado; escopo WeakMaps são muito particulares e mais prático do que com escopo de variáveis; escopo Símbolos são razoavelmente privado e razoavelmente prático; sublinhados são, muitas vezes, privados suficiente e muito prático.
 207
Author: tristan, 2017-11-27 23:08:19

Actualização: uma proposta com uma sintaxe melhor está a caminho. As contribuições são bem-vindas.


Sim, há - para acesso escopo em objectos - ES6 introduz Symbol s .

Os símbolos são únicos, você não pode ter acesso a um de fora excepto com reflexão (como as partes privadas em Java/C#), mas qualquer um que tem acesso a um símbolo no interior 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
 118
Author: Benjamin Gruenbaum, 2020-10-06 03:29:30
A resposta é"não". Mas você pode criar acesso privado a propriedades como esta: (A sugestão de que os símbolos poderiam ser usados para garantir a privacidade era verdadeira numa versão anterior do Spec ES6, mas já não é a 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 veja: https://curiosity-driven.org/private-properties-in-javascript)
 34
Author: d13, 2017-05-23 12:18:36

A única maneira de obter a verdadeira privacidade em JS é através de escopo, então não há maneira de ter uma propriedade que é um membro de this que será acessível apenas dentro do componente. A melhor maneira de armazenar dados verdadeiramente privados em 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));
  }
}
É óbvio que isto é provavelmente lento, e definitivamente feio, mas proporciona privacidade. Lembre-se que nem isto é perfeito, porque o Javascript é tão dinâmico. Alguém ainda pode fazer.
var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};

Para apanhar valores à medida que são armazenados, por isso, se quiser ter um cuidado extra, terá de capturar uma referência local a .set e .get para usar explicitamente em vez de depender do protótipo que pode ser substituído.

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));
  }
}
 30
Author: loganfsmyth, 2016-01-03 22:25:51

Para futura referência de outros sobre lookers, estou a ouvir agora que a recomendação é usar WeakMaps para guardar dados privados.

Aqui está um exemplo mais claro e funcional:

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}`)
      }
    }
  }
}
 22
Author: Community, 2017-05-23 12:34:59

Depende de a quem perguntas. :-)

O modificador de propriedades está incluído no classes máximas mínimas proposta que parece ter chegado ao projecto actual.

No entanto, pode haver apoio para nomes privados, o que permite propriedades privadas - e eles provavelmente poderiam ser usados em Definições de classe também.
 12
Author: Bergi, 2014-03-03 22:20:11

Usar módulos ES6 (inicialmente propostos por @d13) funciona bem para mim. Ele não imita propriedades privadas perfeitamente, mas pelo menos você pode estar confiante de que propriedades que devem ser privadas não vai vazar fora de sua classe. Aqui está um exemplo:

Alguma coisa.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');
  }
};

Então o código consumidor pode ser assim:

import Something from './something.js';

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

Actualização (Importante):

Como @DanyalAytekin delineou nos comentários, Estas propriedades privadas são estáticas, portanto, de âmbito global. Eles vão trabalhar bem ao trabalhar com Singletons, mas cuidado deve ser tomado para objetos transitórios. Extensão do 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
 10
Author: Johnny Oshika, 2018-03-27 00:24:17

Sim - você pode criar propriedades encapsuladas, 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 pode ser feito com ES6:

1 Criar classe usando classe palavra

2 dentro do seu construtor declara a variável em bloco usando let ou const as palavras reservadas -> uma vez que são em bloco-escopo não podem ser acedidas a partir do exterior (encapsuladas)

3 a permitir algum controlo de acesso (setters|getters) a essas variáveis você pode declarar o método de instância dentro do seu 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
 9
Author: Nikita Kurtin, 2016-02-08 15:28:17

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

Acho que no exemplo fornecido por @johnny-oshika poderíamos usar funções normais em vez de funções de seta e depois {[[2]} com o objecto actual mais um objecto _privates como um parâmetro de Carriagem:

Alguma coisa.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 que me lembro:

  • podemos ter métodos privados (_greet e _updateMessage agir como métodos privados, desde que não façamos as referências. Apesar de não estarem no protótipo, os métodos acima mencionados salvarão a memória porque as instâncias são criadas uma vez, fora da classe (em oposição a defini-las no construtor) {[[27]} Não divulgamos nenhum Globo, já que estamos dentro de um módulo.
  • nós também podemos ter propriedades privadas usando o objeto binded _privates

Posso pensar em alguns inconvenientes.:

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

 9
Author: efidiles, 2016-04-24 18:04:14

Uma abordagem diferente de "privado"

Em vez de lutar contra o fato de que a visibilidade privada está atualmente indisponível no ES6, decidi tomar uma abordagem mais prática que faz muito bem se o seu IDE suporta JSDoc (por exemplo, Webstorm). A ideia é usar o @private tag . No que diz respeito ao desenvolvimento, a IDE impedirá o acesso a qualquer membro privado de fora da sua classe. Funciona muito bem para mim e tem sido muito útil para esconder métodos internos o recurso Auto-completo mostra-me o que a classe realmente significava expor. Aqui está um exemplo:

auto-complete showing just public stuff

 7
Author: Lucio Paiva, 2017-10-01 20:27:25
Tantas soluções exóticas! Normalmente não me importo com a privacidade, por isso Uso "pseudo-Privacidade" Como é dito aqui. Mas se me importar (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 Possível implementação 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)
}
 7
Author: Sergey, 2018-11-10 08:02:36

WeakMap

  • suportado na IE11 (Os símbolos não são)
  • hard-private (os adereços que usam símbolos são soft-private devido a Object.getOwnPropertySymbols)
  • pode parecer realmente limpo (ao contrário dos fechos que requerem todos os adereços e métodos no construtor) {[[10]}

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

function Private() {
  const map = new WeakMap();
  return obj => {
    let props = map.get(obj);
    if (!props) {
      props = {};
      map.set(obj, props);
    }
    return props;
  };
}
Então, 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 pela IE11, mas parece mais limpa no exemplo.

 6
Author: kevlened, 2017-11-27 01:24:42
Pessoalmente, gosto da proposta do operador de ligação. :: e então combiná-lo-ia com a solução @D13 mencionada, mas por agora fique com a resposta de @d13 onde você usa a palavra-chave export para a sua classe e coloque as funções privadas no módulo.

Há mais uma solução difícil que não foi mencionado aqui que segue é uma abordagem mais funcional e lhe permitiria ter todos os adereços/métodos privados dentro do classe.

Soldado.js

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

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

Agradecia que comentasse o assunto.

 5
Author: Robin F., 2017-04-09 18:24:46

Eu acho que a resposta de Benjamin é provavelmente a melhor para a maioria dos casos até que a linguagem nativamente suporta variáveis explicitamente privadas.

No entanto, se, por alguma razão, tiver de impedir o acesso com Object.getOwnPropertySymbols(), um método que eu considerei usar é anexar uma propriedade única, não-configurável, não-enumerável, não-escrita que pode ser usada como um identificador de propriedade para cada objeto na construção (como um único Symbol, Se você já não tem algum outro único propriedade como um ([3]}). 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 desta abordagem sobre a utilização de uma WeakMap é tempo de acesso mais rápido se o desempenho se tornar uma preocupação.

 5
Author: NanoWizard, 2017-12-29 12:29:52
Deparei-me com este post ao procurar as melhores práticas para "dados privados para aulas". Foi mencionado que alguns dos padrões teriam problemas de desempenho. Fiz alguns testes jsperf baseados nos 4 padrões principais do livro online "Exploring ES6":

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

Os testes podem ser encontrados. toma.

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

In Chrome 63.0.3239 / Mac OS X 10.11.6, the best performing patterns were " Private data via constructor environments "and"Private data via a naming convention". Para mim Safari correu bem para o WeakMap, mas o cromo não tão bem.

Não sei o impacto da memória, mas o padrão para "ambientes Construtores" que alguns avisaram que seria um problema de desempenho era muito performativo.

O 4 básico os padrões são:

Dados privados através de ambientes de construção

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 através de ambientes de construção 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 através 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 através de mapas fracos

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 através de 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();
 5
Author: MarkM, 2018-01-08 23:48:16
Acredito que é possível obter o melhor de ambos os mundos usando fechos dentro de construtores. Existem duas variações:

Todos os membros de 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: Isto é reconhecidamente feio. Se você conhece uma solução melhor, por favor, 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
Author: JSInitiate, 2016-09-20 16:09:24
Na verdade, é possível usar símbolos e Proxies. Você usa os símbolos no escopo da classe e coloca duas armadilhas em um proxy: uma para o protótipo da classe para que o reflexo.ownKeys (instância) ou o objecto.o getOwnPropertySymbols não entrega os seus símbolos, o outro é para o construtor em si, por isso quando new ClassName(attrs) for chamado, a instância devolvida será interceptada e terá os 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: é por isso que precisamos de uma armadilha para estes objectos.

 4
Author: Francisco Neto, 2017-02-27 23:12:13
Nem mesmo o dactilógrafo consegue fazê-lo. Da sua documentação :

Quando um membro é marcado como privado, não pode ser acedido de fora da sua classe que 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 no seu recreio Isto dá:

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    return Animal;
}());
console.log(new Animal("Cat").name);
Então a palavra-chave" privada " deles é ineficaz.
 4
Author: Michael Franzl, 2017-04-18 21:16:07
Cheguei muito tarde a esta festa, mas fiz a pergunta da OP numa busca... Sim, você pode ter propriedades privadas ao embrulhar a declaração de classe num Fecho

Há um exemplo de como tenho métodos privados neste codepen. No excerto abaixo, a classe Subescritível tem duas funções 'privadas' process e processCallbacks. Todas as propriedades podem ser adicionadas desta forma e elas são mantidas privadas através do uso do fechamento. A privacidade da OMI é uma necessidade rara se preocupações são bem separadas e Javascript não precisa se tornar inchado, adicionando mais sintaxe quando um fechamento faz bem o trabalho.

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)}}}})();
Gosto desta abordagem porque separa as preocupações e mantém as coisas verdadeiramente privadas. A única desvantagem é a necessidade de usar "self" (ou algo semelhante) para se referir a "isto" no conteúdo privado.
 4
Author: Paul Whipp, 2017-09-07 05:45:21
Sim, pode, e muito facilmente também. Isto é feito expondo suas variáveis e funções privadas, retornando o gráfico do objeto protótipo no construtor. Isto não é nada de novo, mas tome um pouco de js foo para entender a elegância dele. Desta forma, não se utiliza scoped global, ou weakmaps. É uma forma de reflexão construída na linguagem. Dependendo de como você alavancar isso; pode-se forçar uma exceção que interrompe a pilha de chamadas, ou enterrar a exceção como um undefined. Presente é demonstrado abaixo, e pode ler mais sobre estas características 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
 4
Author: 1-14x0r, 2019-12-07 00:39:52
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"
 3
Author: Ilya Zarembsky, 2016-05-01 22:04:27
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
 3
Author: Jayesbe, 2017-11-17 16:15:11

Ao ler a resposta anterior, pensei que este exemplo pudesse resumir as soluções acima

const friend = Symbol('friend');

const ClassName = ((hidden, hiddenShared = 0) => {

    class ClassName {
        constructor(hiddenPropertyValue, prop){
            this[hidden] = hiddenPropertyValue * ++hiddenShared;
            this.prop = prop
        }

        get hidden(){
            console.log('getting hidden');
            return this[hidden];
        }

        set [friend](v){
            console.log('setting hiddenShared');
            hiddenShared = v;
        }

        get counter(){
            console.log('getting hiddenShared');
            return hiddenShared;
        }

        get privileged(){
            console.log('calling privileged method');
            return privileged.bind(this);
        }
    }

    function privileged(value){
        return this[hidden] + value;
    }

    return ClassName;
})(Symbol('hidden'), 0);

const OtherClass = (() => class OtherClass extends ClassName {
    constructor(v){
        super(v, 100);
        this[friend] = this.counter - 1;
    }
})();

Actualizar

Agora é possível fazer verdadeiras propriedades privadas e métodos (pelo menos em navegadores baseados em cromos por agora).

A sintaxe é muito limpa

class MyClass {
    #privateProperty = 1
    #privateMethod() { return 2 }
    static #privateStatic = 3
    static #privateStaticMethod(){return 4}
    static get #privateStaticGetter(){return 5}

    // also using is quite straightforward
    method(){
        return (
            this.#privateMethod() +
            this.#privateProperty +
            MyClass.#privateStatic +
            MyClass.#privateStaticMethod() +
            MyClass.#privateStaticGetter
        )
    }
}

new MyClass().method()
// returns 15

Note que para obter referências estáticas você não usaria {[[2]}, porque iria travar as suas subclasses. Você deve usar uma referência à classe adequada, a fim de recuperar suas referências privadas estáticas (que estão disponíveis apenas dentro dos métodos dessa classe), ie MyClass.#private.

 3
Author: asdru, 2020-08-21 16:06:57

A maioria das respostas ou dizem que é impossível, ou exigem que você use um WeakMap ou símbolo, que são características ES6 que provavelmente necessitariam de polifills. Mas há outra maneira! Olha para isto.

// 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 a este método Padrão de acesso . A ideia essencial é que temos umFecho , Umachave dentro do fecho, e criamos um objecto privado (no construtor) que só pode ser acedido se tiver a chave.

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

 2
Author: guitarino, 2017-04-11 17:26:43

Ver esta resposta para uma solução 'class' limpa e simples com uma interface privada e pública e suporte para a composição

 2
Author: kofifus, 2017-09-23 02:09:40

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

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
 2
Author: Nikola Andreev, 2018-03-07 07:48:26

Eu uso este 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
Author: Yami Teru, 2018-05-16 13:31:20
Na verdade, é possível.
1. Primeiro, crie a classe e no construtor retorne a chamada função _public.
2. Na chamada função _public passar a referência this (para obter o acesso a todos os métodos e adereços privados), e todos os argumentos de constructor (Isso será passado new Names())
3. No âmbito da função _public Existe também a classe Names com acesso a this (_this) referência da classe Names 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
Author: Paweł, 2018-08-24 05:22:54