Proprietà private nelle classi ES6 di JavaScript


444

È possibile creare proprietà private nelle classi ES6?

Ecco un esempio Come posso impedire l'accesso a instance.property?

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

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

5
Esiste effettivamente una proposta di fase 3 per questa funzione - tc39.github.io/proposal-class-fields github.com/tc39/proposal-class-fields
arty

@arty Ho fornito una risposta a questo con esempi: stackoverflow.com/a/52237988/1432509
Alister

Risposte:


165

I campi (e i metodi) privati ​​sono stati implementati nello standard ECMA . Puoi iniziare a usarli oggi con il preset babel 7 e stage 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

Mi chiedo come possano funzionare quei campi di classe. Al momento non è possibile utilizzare thisnel costruttore prima di chiamare super(). Eppure Babel li mette davanti a super.
seeker_of_bacon,

Come configurare ESLint per consentire la #privateCrapsintassi?
Marecky,

6
E che dire di eslint? Ho avuto un errore parser allo stesso segno. Babel sta lavorando, solo che eslint non può analizzare questa nuova sintassi js.
martonx,

6
Wow, questo è molto brutto. Hashtag è un personaggio valido. La proprietà non è davvero privata, o? .. L'ho controllato in TypeScript. I membri privati ​​non vengono compilati in privato o in sola lettura (dall'esterno). Dichiarato come un'altra proprietà (pubblica). (ES5).
Dominik,

2
Come scrivi metodi privati con questo? Posso fare questo #beep() {}:; e questo async #bzzzt() {}:?
Константин Ван,

277

Risposta breve, no, non esiste supporto nativo per proprietà private con classi ES6.

Ma potresti imitare quel comportamento non attaccando le nuove proprietà all'oggetto, ma tenendole all'interno di un costruttore di classi e usando getter e setter per raggiungere le proprietà nascoste. Nota che i getter e i setter vengono ridefiniti su ogni nuova istanza della 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
Mi piace questa soluzione al meglio. Sono d'accordo che non dovrebbe essere usato per il ridimensionamento ma è perfetto per le classi che di solito verranno istanziate una sola volta per inclusione.
Blake Regalia,

2
Inoltre, stai ridefinendo ogni singolo componente di questa classe ogni volta che ne viene creata una nuova.
Quentin Roy,

10
Questo è così strano! In ES6 stai creando più "piramidi di chiusura" rispetto a prima di ES6! La definizione delle funzioni ALL'INTERNO di un costruttore sembra più brutta di quanto non abbia fatto nell'esempio ES5 sopra.
Kokodoko,

1
Dal momento che l'OP chiede specificamente delle classi ES6, personalmente ritengo che questa sia una soluzione scadente, anche se tecnicamente funziona. Il limite principale è che ora ogni metodo di classe che utilizza variabili private deve essere dichiarato all'interno del costruttore, minando gravemente i vantaggi di avere la classsintassi in primo luogo.
NanoWizard,

10
Tutto ciò che fa è introdurre il riferimento indiretto. Ora come si fa a fare le getNamee setNameproprietà privata?
aij,

195

Per espandere la risposta di @ loganfsmyth:

Gli unici dati veramente privati ​​in JavaScript sono ancora variabili con ambito. Non è possibile avere proprietà private nel senso di proprietà accessibili internamente allo stesso modo delle proprietà pubbliche, ma è possibile utilizzare variabili con ambito per archiviare dati privati.

Variabili con ambito

L'approccio qui è quello di utilizzare l'ambito della funzione di costruzione, che è privata, per memorizzare i dati privati. Affinché i metodi abbiano accesso a questi dati privati, devono essere creati anche all'interno del costruttore, il che significa che li stai ricreando in ogni istanza. Questa è una penalità per prestazioni e memoria, ma alcuni credono che la penalità sia accettabile. La penalità può essere evitata per i metodi che non richiedono l'accesso ai dati privati ​​aggiungendoli al prototipo come al solito.

Esempio:

function Person(name) {
  let age = 20; // this is private
  this.name = name; // this is public

  this.greet = function () {
    // here we can access both name and age
    console.log(`name: ${this.name}, age: ${age}`);
  };
}

let joe = new Person('Joe');
joe.greet();

// here we can access name but not age

WeakMap con ambito

Una WeakMap può essere utilizzata per evitare le prestazioni dell'approccio precedente e la penalità di memoria. WeakMap associa i dati agli oggetti (qui, istanze) in modo tale da poter accedere solo tramite quella WeakMap. Quindi, utilizziamo il metodo delle variabili con ambito per creare una WeakMap privata, quindi utilizziamo quella WeakMap per recuperare i dati privati ​​associati this. Questo è più veloce del metodo delle variabili con ambito perché tutte le tue istanze possono condividere una singola WeakMap, quindi non è necessario ricreare i metodi solo per far loro accedere alle proprie WeakMap.

Esempio:

let Person = (function () {
  let privateProps = new WeakMap();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      privateProps.set(this, {age: 20}); // this is private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// here we can access joe's name but not age

In questo esempio viene utilizzato un oggetto per utilizzare una WeakMap per più proprietà private; potresti anche usare più WeakMap e usarle come age.set(this, 20), oppure scrivere un piccolo wrapper e usarlo in un altro modo, come privateProps.set(this, 'age', 0).

La privacy di questo approccio potrebbe teoricamente essere violata manomettendo l' WeakMapoggetto globale . Detto questo, tutto JavaScript può essere rotto da globi alterati. Il nostro codice è già basato sul presupposto che ciò non accada.

(Questo metodo potrebbe anche essere fatto con Map, ma WeakMapè meglio perché Mapcreerà perdite di memoria a meno che tu non sia molto attento, e per questo i due non sono altrimenti diversi.)

Mezza risposta: simboli con ambito

Un simbolo è un tipo di valore primitivo che può fungere da nome di proprietà. È possibile utilizzare il metodo della variabile con ambito per creare un simbolo privato, quindi archiviare i dati privati ​​in this[mySymbol].

La privacy di questo metodo può essere violata utilizzando Object.getOwnPropertySymbols, ma è piuttosto difficile da fare.

Esempio:

let Person = (function () {
  let ageKey = Symbol();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      this[ageKey] = 20; // this is intended to be private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${this[ageKey]}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// Here we can access joe's name and, with a little effort, age. ageKey is
// not in scope, but we can obtain it by listing all Symbol properties on
// joe with `Object.getOwnPropertySymbols(joe)`.

Mezza risposta: sottolinea

Il vecchio predefinito, basta usare una proprietà pubblica con un prefisso di sottolineatura. Sebbene non sia in alcun modo una proprietà privata, questa convenzione è abbastanza diffusa da fare un buon lavoro comunicando che i lettori dovrebbero considerare la proprietà come privata, il che spesso porta a termine il lavoro. In cambio di questo intervallo, otteniamo un approccio più facile da leggere, più facile da scrivere e più veloce.

Esempio:

class Person {
  constructor(name) {
    this.name = name; // this is public
    this._age = 20; // this is intended to be private
  }

  greet() {
    // Here we can access both name and age
    console.log(`name: ${this.name}, age: ${this._age}`);
  }
}

let joe = new Person('Joe');
joe.greet();

// Here we can access both joe's name and age. But we know we aren't
// supposed to access his age, which just might stop us.

Conclusione

A partire da ES2017, non esiste ancora un modo perfetto per fare proprietà private. Vari approcci hanno pro e contro. Le variabili con ambito sono veramente private; le WeakMap con ambito sono molto private e più pratiche delle variabili con ambito; I simboli con ambito sono ragionevolmente privati ​​e ragionevolmente pratici; i caratteri di sottolineatura sono spesso abbastanza privati ​​e molto pratici.


7
Il primo frammento di esempio ("variabili con ambito") è un antipattern totale: ogni oggetto restituito avrà una classe diversa. Non farlo. Se desideri metodi privilegiati, creali nel costruttore.
Bergi,

1
L'avvolgimento di una classe all'interno di una funzione sembra annullare innanzitutto lo scopo dell'uso delle classi. Se si utilizza già la funzione per creare un'istanza, è possibile inserire anche tutti i membri privati ​​/ pubblici all'interno di tale funzione e dimenticare l'intera parola chiave della classe.
Kokodoko,

2
@Bergi @Kokodoko Ho modificato l'approccio delle variabili con ambito in modo che sia leggermente più veloce e non si rompa instanceof. Devo ammettere che stavo pensando a quell'approccio come incluso solo per completezza e avrei dovuto pensare più a quanto è effettivamente capace.
tristan,

1
Spiegazione eccellente! Sono ancora sorpreso dal fatto che ES6 abbia effettivamente reso più difficile la simulazione di una variabile privata, in cui in ES5 si poteva semplicemente usare var e questo all'interno di una funzione per simulare privato e pubblico.
Kokodoko,

2
@Kokodoko Se si rinuncia alla classe e si inserisce tutto nella funzione, è necessario ripristinare l'implementazione dell'ereditarietà utilizzando il metodo prototipo. L'uso di extension sulle classi è di gran lunga un approccio più pulito, quindi è assolutamente accettabile utilizzare una classe all'interno di una funzione.
AndroidDev,

117

Aggiornamento: una proposta con una sintassi migliore è in arrivo. I contributi sono benvenuti.


Sì, esiste - per l'accesso con ambito negli oggetti - ES6 introduce Symbols .

I simboli sono unici, non è possibile accedere a uno dall'esterno se non con la riflessione (come i privati ​​in Java / C #) ma chiunque abbia accesso a un simbolo all'interno può utilizzarlo per l'accesso alla chiave:

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
Non puoi usare Object.getOwnPropertySymbols? ;)
Qantas 94 Heavy

41
@BenjaminGruenbaum: Apparentemente i simboli non garantiscono più la vera privacy: stackoverflow.com/a/22280202/1282216
d13

28
@trusktr tramite tre chiavi? No. Attraverso i simboli? Sì. Molto simile a come è possibile utilizzare la riflessione in linguaggi come C # e Java per accedere ai campi privati. I modificatori di accesso non riguardano la sicurezza, ma la chiarezza delle intenzioni.
Benjamin Gruenbaum,

9
Sembra che l'uso dei simboli sia simile al fare const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();. Non si tratta in realtà di privacy, è oscurità, nel senso di JavaScript tradizionale. Considererei JavaScript "privato" per indicare l'uso di chiusure per incapsulare le variabili. Tali variabili non sono quindi accessibili attraverso la riflessione.
trusktr,

13
Inoltre, ritengo che l'uso delle parole chiave privatee protectedsarebbe molto più pulito di Symbolo Name. Preferisco la notazione a punti piuttosto che la parentesi. Vorrei continuare a usare un punto per cose private. this.privateVar
trusktr,

33

La risposta è no". Ma puoi creare un accesso privato a proprietà come questa:

(Il suggerimento che i simboli potrebbero essere utilizzati per garantire la privacy era vero in una versione precedente della specifica ES6 ma non è più il caso: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604. html e https://stackoverflow.com/a/22280202/1282216 . Per una discussione più lunga su Simboli e privacy consultare: https://curiosity-driven.org/private-properties-in-javascript )


6
-1, questo non risponde davvero alla tua domanda. (Puoi usare chiusure con IIFE anche in ES5). Le proprietà private sono enumerabili attraverso la riflessione nella maggior parte delle lingue (Java, C #, ecc.). Lo scopo delle proprietà private è di comunicare l'intenzione ad altri programmatori e di non applicare la sicurezza.
Benjamin Gruenbaum

1
@BenjaminGruenbaum, lo so, vorrei avere una risposta migliore, non ne sono contento.
d13

Penso che i simboli siano ancora un modo valido per raggiungere membri inaccessibili durante l'ambiente di programmazione. Sì, possono ancora essere trovati se lo desideri davvero, ma non è questo il punto? Non dovresti archiviare informazioni sensibili al suo interno, ma non dovresti farlo comunque nel codice lato client. Ma funziona allo scopo di nascondere una proprietà o un metodo da una classe esterna.
Kokodoko,

L'uso di variabili con ambito a livello di un modulo come sostituto delle proprietà private in una classe porterà a un comportamento singleton.beil o comportamento simile a proprietà statitc. Le istanze di VAR verranno condivise.
Adrian Moisa,

30

L'unico modo per ottenere la vera privacy in JS è attraverso l'ambito, quindi non c'è modo di avere una proprietà che ne sia membro thissarà accessibile solo all'interno del componente. Il modo migliore per archiviare dati veramente privati ​​in ES6 è con una 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));
  }
}

Ovviamente questo è probabilmente un processo lento e decisamente brutto, ma fornisce privacy.

Tieni presente che ANCHE QUESTO non è perfetto, perché Javascript è così dinamico. Qualcuno potrebbe ancora fare

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

per acquisire i valori man mano che vengono archiviati, quindi se si desidera prestare maggiore attenzione, è necessario acquisire un riferimento locale .sete .getutilizzarlo in modo esplicito anziché fare affidamento sul prototipo sostituibile.

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
Come suggerimento, puoi evitare di usare una mappa debole per proprietà usando un oggetto come valore. In questo modo puoi anche ridurre il numero di mappe geta una per metodo (ad es const _ = privates.get(this); console.log(_.privateProp1);.).
Quentin Roy,

Sì, anche questa è totalmente un'opzione. Per lo più sono andato con questo poiché si associa più direttamente a ciò che un utente avrebbe scritto quando utilizzava proprietà reali.
loganfsmyth,

@loganfsmyth const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"significa che la tua proprietà è privata o no?
Barbu Barbu,

2
Perché ciò funzioni, il codice che accede alla proprietà avrebbe bisogno dell'accesso all'oggetto WeakMap, che normalmente verrebbe inserito all'interno di un modulo e inaccessibile
loganfsmyth,

22

Per riferimento futuro di altri utenti, sto ascoltando ora che la raccomandazione è di utilizzare WeakMaps per conservare i dati privati.

Ecco un esempio più chiaro e funzionante:

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
Tenere presente che queste proprietà sono statiche.
Michael Theriot,

8
Non ti ho sottovalutato, ma il tuo esempio di mappa debole è completamente sbagliato.
Benjamin Gruenbaum,

4
Vale a dire - Stai condividendo i dati tra tutte le istanze di classe e non per istanza - posso almeno risolverli?
Benjamin Gruenbaum,

1
In effetti, la mappa debole deve essere collegata a una determinata istanza. Vedere fitzgeraldnick.com/weblog/53 per un esempio.
ampliato il

2
Secondo MDN, i tipi di dati primitivi come Simboli non sono consentiti come chiave WeakMap. Documentazione MDN WeakMap
leepowell,

12

Dipende da chi chiedi :-)

Nessun privatemodificatore di proprietà è incluso nella proposta Classi minimamente minime che sembra essere entrata nella bozza corrente .

Tuttavia, potrebbe esserci supporto per nomi privati , che consente proprietà private - e probabilmente potrebbero essere utilizzati anche nelle definizioni di classe.


3
Suo altamente improbabile che i nomi privati ​​diventino ES6, anche se stanno pensando a qualche forma di cosa privata per ES7.
Qantas 94 Heavy

@ Qantas94: Pesanti sia i nomi privati ​​che i valori stringa univoci sono stati sostituiti dai simboli da quello che ho capito.
Benjamin Gruenbaum,

Sì, probabilmente diventerà Simboli. Tuttavia, i "simboli" attualmente contenuti nelle specifiche vengono utilizzati solo per descrivere proprietà interne come [[prototipo]] e non è possibile crearle e utilizzarle nel codice utente. Conosci alcuni documenti?
Bergi,

Ho appena realizzato che i moduli possono essere utilizzati per impostare la privacy. Combinato con simboli che potrebbero essere tutto ciò di cui avresti mai bisogno ...?
d13

1
@Cody: l'intero codice del modulo ha comunque un proprio ambito in ES6, non è necessario un IEFE. E sì, i simboli sono proposti per unicità (evitare le collisioni), non per la privacy.
Bergi

10

L'uso dei moduli ES6 (inizialmente proposto da @ d13) funziona bene per me. Non imita perfettamente le proprietà private, ma almeno puoi essere sicuro che le proprietà che dovrebbero essere private non perderanno al di fuori della tua classe. Ecco un esempio:

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

Quindi il codice che consuma può apparire così:

import Something from './something.js';

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

Aggiornamento (importante):

Come sottolineato da @DanyalAytekin nei commenti, queste proprietà private sono statiche, quindi di portata globale. Funzioneranno bene quando si lavora con i Singleton, ma bisogna fare attenzione agli oggetti Transitori. Estendere l'esempio sopra:

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
Buono per private static.
Danyal Aytekin,

@DanyalAytekin: è un ottimo punto. Queste proprietà private sono statiche e quindi di portata globale. Ho aggiornato la mia risposta per riflettere questo.
Johnny Oshika,

Più imparo sulla programmazione funzionale (in particolare Elm e Haskell), più credo che i programmatori JS trarrebbero beneficio da un approccio alla "modularità" basato su moduli piuttosto che da uno basato sulla classe OOP. Se pensiamo ai moduli ES6 come le basi per la creazione di applicazioni e dimentichiamo completamente le classi, credo che potremmo finire con applicazioni molto migliori nel complesso. Qualche utente Elm o Haskell esperto può commentare questo approccio?
d13,

1
Nell'aggiornamento, il secondo a.say(); // adovrebbe essereb.say(); // b
grokky

provato let _message = null, non così bello, quando si chiama più volte costruttore, si incasina.
Littlee,

9

Completando @ d13 e i commenti di @ johnny-oshika e @DanyalAytekin:

Immagino che nell'esempio fornito da @ johnny-oshika potremmo usare le normali funzioni invece delle funzioni freccia e quindi .bindquelle con l'oggetto corrente più un _privatesoggetto come parametro al 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

Vantaggi a cui posso pensare:

  • possiamo avere metodi privati ​​( _greete _updateMessageagire come metodi privati ​​purché non facciamo exportriferimento)
  • sebbene non siano sul prototipo, i metodi sopra menzionati risparmieranno memoria perché le istanze vengono create una volta, al di fuori della classe (invece di definirle nel costruttore)
  • non perdiamo nessun globale poiché siamo all'interno di un modulo
  • possiamo anche avere proprietà private usando l' _privatesoggetto associato

Alcuni inconvenienti che mi vengono in mente:

Uno snippet in esecuzione è disponibile qui: http://www.webpackbin.com/NJgI5J8lZ


8

Sì: è possibile creare proprietà incapsulate , ma non è stato fatto con modificatori di accesso (pubblico | privato) almeno non con ES6.

Ecco un semplice esempio di come si può fare con ES6:

1 Crea classe usando class parola

2 All'interno del costruttore è possibile dichiarare la variabile con ambito di blocco usando let OR const parole riservate -> poiché sono a scopo di blocco a cui non è possibile accedere dall'esterno (incapsulato)

3 Per consentire un certo controllo dell'accesso (setter | getter) a quelle variabili puoi dichiarare il metodo di istanza all'interno del suo costruttore usando: this.methodName=function(){}sintassi

"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;
            }
        }
    }

Ora controlliamo:

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
Questa è (per ora) l'unica soluzione a questo problema, nonostante il fatto che tutti i metodi dichiarati nel costruttore siano ridecressi per ogni istanza della classe. Questa è una pessima idea per quanto riguarda le prestazioni e l'utilizzo della memoria. I metodi di classe devono essere dichiarati al di fuori dell'ambito del costruttore.
Freezystem

@Freezystem First: in primo luogo quelli sono metodi di istanza (non metodi Class). Secondo domanda del PO era: _ Come posso impedire l'accesso a instance.property?_ e la mia risposta è: un esempio di come ... Terzo se hai qualche idea migliore - ascoltiamolo
Nikita Kurtin

1
Non stavo dicendo che avevi torto, ho detto che la tua soluzione era il miglior compromesso per raggiungere la variabile privata nonostante il fatto che una copia di ogni metodo di istanza viene creata ogni volta che chiami new Something();perché i tuoi metodi sono dichiarati nel costruttore per avere accesso a questi variabili private. Ciò potrebbe causare un notevole consumo di memoria se si creano molte istanze della classe, quindi problemi di prestazioni. I metodi avrebbero dovuto essere dichiarati al di fuori dell'ambito del costruttore. Il mio commento è stato più una spiegazione degli svantaggi della tua soluzione che una critica.
Freezystem,

1
Ma non è una cattiva pratica definire tutta la tua classe all'interno del costruttore? Non stiamo semplicemente "hackerando" javascript ora? Basta guardare qualsiasi altro linguaggio di programmazione OOP e vedrai che il costruttore non è pensato per definire una classe.
Kokodoko,

1
Sì, questo è quello che intendevo e la tua soluzione funziona! Sto solo dicendo che in generale sono sorpreso che ES6 abbia aggiunto una parola chiave "class", ma ha rimosso l'elegante soluzione di lavorare con var e questo per ottenere l'incapsulamento.
Kokodoko

8

Un approccio diverso al "privato"

Invece di lottare contro il fatto che la visibilità privata non è attualmente disponibile in ES6, ho deciso di adottare un approccio più pratico che vada bene se il tuo IDE supporta JSDoc (ad esempio Webstorm). L'idea è di usare il@private tag . Per quanto riguarda lo sviluppo, l'IDE ti impedirà di accedere a qualsiasi membro privato al di fuori della sua classe. Funziona abbastanza bene per me ed è stato davvero utile per nascondere i metodi interni, quindi la funzione di completamento automatico mi mostra esattamente ciò che la classe voleva davvero esporre. Ecco un esempio:

completamento automatico che mostra solo materiale pubblico


1
Il problema è che non vogliamo accedere alle variabili private tramite l'Editor, non vogliamo proteggere le variabili private dall'esterno - E questo è ciò che fa pubblico / privato. Se il tuo codice è finito, puoi accedere (e l'importante pensa: sovrascrivere ) queste variabili dall'esterno della classe. Il tuo @privatecommento non può impedire questi, è solo una funzione per la documentazione generazione e you'r IDE.
Adrian Preuss,

Sì, ne sono consapevole. È solo che è abbastanza per me e può essere sufficiente per altre persone là fuori. So che non sta davvero rendendo le mie variabili private; mi sta solo avvisando di non provare ad accedervi dall'esterno (solo, ovviamente, se io e il mio team stiamo utilizzando un IDE che supporta questa funzione). Javascript (e altre lingue, come Python) non è stato progettato tenendo conto dei livelli di accesso. Le persone fanno ogni sorta di cose per implementare in qualche modo quella funzionalità, ma alla fine finiamo semplicemente per hackerare il linguaggio per raggiungere questo obiettivo. Ho deciso di seguire un approccio più "naturale", se vuoi.
Lucio Paiva,

6

WeakMap

  • supportato in IE11 (i simboli non sono)
  • hard-private (gli oggetti di scena che usano i simboli sono soft-private a causa di Object.getOwnPropertySymbols)
  • può sembrare davvero pulito (a differenza delle chiusure che richiedono tutti i puntelli e i metodi nel costruttore)

Innanzitutto, definisci una funzione per avvolgere WeakMap:

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

Quindi, costruisci un riferimento al di fuori della tua 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: la classe non è supportata da IE11, ma sembra più pulita nell'esempio.


6

Oh, così tante soluzioni esotiche! Di solito non mi interessa la privacy, quindi uso la "pseudo privacy" come si dice qui . Ma se ti interessa (se ci sono alcuni requisiti speciali per questo) uso qualcosa come in questo esempio:

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

Un'altra possibile implementazione della funzione (costruttore) Job:

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

5

Personalmente mi piace la proposta dell'operatore bind :: e poi la combinerei con la soluzione @ d13 menzionata, ma per ora atteniti alla risposta di @ d13 dove usi ilexport parola chiave per la tua classe e metti le funzioni private nel modulo.

c'è un'altra soluzione difficile che non è stata menzionata qui che segue è un approccio più funzionale e gli permetterebbe di avere tutti gli oggetti / metodi privati ​​all'interno della 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

commenti su di esso sarebbero apprezzati.


In genere mi piace l'approccio. Feedback: 1. è necessario un modulo private.js diverso per ogni classe per evitare lo scontro. 2. Non mi piace il potenziale di allungare davvero il costruttore definendo in linea ciascuno dei tuoi metodi privati. 3. Sarebbe bello se tutti i metodi di classe fossero in un unico file.
Doug Coburn,

5

Mi sono imbattuto in questo post quando ho cercato le migliori pratiche per "dati privati ​​per le classi". È stato menzionato che alcuni dei modelli avrebbero problemi di prestazioni.

Ho messo insieme alcuni test jsperf basati sui 4 modelli principali del libro online "Exploring ES6":

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

I test possono essere trovati qui:

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

In Chrome 63.0.3239 / Mac OS X 10.11.6, i modelli con le migliori prestazioni erano "Dati privati ​​tramite ambienti di costruzione" e "Dati privati ​​tramite una convenzione di denominazione". Per me Safari ha funzionato bene per WeakMap, ma Chrome non è stato così bene.

Non conosco l'impatto della memoria, ma il modello per "ambienti di costruzione" che alcuni avevano avvertito sarebbe stato un problema di prestazioni era molto performante.

I 4 modelli di base sono:

Dati privati ​​tramite ambienti di costruzione

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();

Dati privati ​​tramite ambienti di costruzione 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();

Dati privati ​​tramite una convenzione di denominazione

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();

Dati privati ​​tramite 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();

Dati privati ​​tramite simboli

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

Credo che sia possibile ottenere il "meglio di entrambi i mondi" usando chiusure all'interno di costruttori. Esistono due varianti:

Tutti i membri dei dati sono privati

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);
   }
};

Alcuni membri sono privati

NOTA: questo è certamente brutto. Se conosci una soluzione migliore, modifica questa risposta.

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

In effetti è possibile usare Simboli e Proxy. Si utilizzano i simboli nell'ambito della classe e si impostano due trap in un proxy: uno per il prototipo di classe in modo che Reflect.ownKeys (istanza) o Object.getOwnPropertySymbols non dia via i simboli, l'altro è per il costruttore stesso quindi quando new ClassName(attrs)viene chiamato, l'istanza restituita verrà intercettata e i simboli delle proprietà verranno bloccati. Ecco il codice:

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()funziona così: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))ecco perché abbiamo bisogno di una trappola per questi oggetti.


Grazie, proverò i simboli :) Da tutte le risposte di cui sopra sembra il modo più semplice per creare un membro della classe inaccessibile :)
Kokodoko,

4

Persino Typescript non può farlo. Dalla loro documentazione :

Quando un membro è contrassegnato come privato, non è possibile accedervi dall'esterno della sua classe contenente. Per esempio:

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

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

Ma traspilato nel loro parco giochi questo dà:

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

Quindi la loro parola chiave "privata" è inefficace.


2
Bene, è ancora efficace perché impedisce la "cattiva" programmazione, mentre si trova nell'IDE. Ti mostra quali membri dovresti e non dovresti usare. Penso che sia la ragione principale per l'utilizzo di privati ​​e pubblici. (Ad esempio, quando si compila C # in codice macchina, il privato sarà ancora privato? Chi lo sa?). Quando leggi le altre risposte, sembra che l'uso di @Symbol possa anche rendere inaccessibile un membro. Ma anche i simboli possono ancora essere trovati dalla console.
Kokodoko,

L'errore TypeScript si verifica durante la transpile di TypeScript in JavaScript? (Come il controllo del tipo avviene in tempo di transpite. Piuttosto che qualche meccanismo privato di runtime.)
Eljay

4

Venendo molto tardi a questa festa, ma ho risposto alla domanda OP in una ricerca, quindi ... Sì, puoi avere proprietà private racchiudendo la dichiarazione di classe in una chiusura

C'è un esempio di come ho metodi privati ​​in questo codice . Nello snippet di seguito, la classe Sottoscrivibile ha due funzioni "private" processe processCallbacks. Tutte le proprietà possono essere aggiunte in questo modo e vengono mantenute private attraverso l'uso della chiusura. La privacy dell'IMO è una necessità rara se le preoccupazioni sono ben separate e Javascript non ha bisogno di gonfiarsi aggiungendo più sintassi quando una chiusura fa bene il lavoro.

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)}}}})();

Mi piace questo approccio perché separa bene le preoccupazioni e mantiene le cose veramente private. L'unico aspetto negativo è la necessità di utilizzare "sé" (o qualcosa di simile) per fare riferimento a "questo" nel contenuto privato.


4

Penso che la risposta di Benjamin sia probabilmente la migliore per la maggior parte dei casi fino a quando il linguaggio non supporta nativamente variabili esplicitamente private.

Tuttavia, se per qualche motivo è necessario impedire l'accesso con Object.getOwnPropertySymbols() , un metodo che ho considerato di utilizzare è quello di allegare una proprietà unica, non configurabile, non enumerabile, non scrivibile che può essere utilizzata come identificatore di proprietà per ciascun oggetto in costruzione (come un unico Symbol, se non hai già qualche altra proprietà unica come una id). Quindi mantieni una mappa delle variabili 'private' di ogni oggetto usando quell'identificatore.

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"

Il potenziale vantaggio di questo approccio rispetto all'uso di a WeakMapè un tempo di accesso più rapido se le prestazioni diventano un problema.


1
Correggimi se sbaglio, ma questo codice non contiene perdite di memoria poiché privateVars memorizzerà comunque le variabili private di un oggetto anche se l'oggetto è già distrutto?
Russell Santos,

Hai ragione @RussellSantos, supponendo che gli oggetti dovranno essere raccolti in un punto. Grazie per averlo sottolineato. Nel mio esempio ho aggiunto un destroy()metodo che dovrebbe essere chiamato dal codice using ogni volta che un oggetto deve essere rimosso.
NanoWizard,

4

Sì, è possibile, e abbastanza facilmente. Questo viene fatto esponendo le variabili e le funzioni private restituendo il grafico dell'oggetto prototipo nel costruttore. Questa non è una novità, ma prenditi un po 'di js foo per capirne l'eleganza. In questo modo non viene utilizzato l'ambito globale o le mappe deboli. È una forma di riflessione integrata nel linguaggio. A seconda di come sfruttarlo; si può forzare un'eccezione che interrompe lo stack di chiamate o seppellire l'eccezione come un undefined. Questo è dimostrato di seguito e può leggere di più su queste funzionalità qui

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
È meglio evitare solo le risposte al codice. Sarebbe meglio se tu potessi spiegare come il tuo codice risponde alla domanda del PO
Stewart_R

Questo è davvero come rendere una variabile di sola lettura più di una variabile privata. Una variabile privata non dovrebbe essere accessibile all'esterno. console.log(instance.property)dovrebbe lanciarti o darti indefinito, non restituirti "test".
oooyaya,

3

Un altro modo simile agli ultimi due pubblicati

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

La maggior parte delle risposte afferma che è impossibile o richiede l'uso di una WeakMap o di un Symbol, che sono funzionalità ES6 che probabilmente richiederebbero i polyfill. C'è comunque un altro modo! Dai un'occhiata a questo:

// 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

Chiamo questo metodo accessor pattern . L'idea essenziale è che abbiamo una chiusura , una chiave all'interno della chiusura e creiamo un oggetto privato (nel costruttore) a cui è possibile accedere solo se si dispone della chiave .

Se sei interessato, puoi leggere di più al riguardo nel mio articolo . Utilizzando questo metodo, è possibile creare proprietà per oggetto a cui non è possibile accedere al di fuori della chiusura. Pertanto, puoi usarli nel costruttore o nel prototipo, ma non altrove. Non ho visto questo metodo usato da nessuna parte, ma penso che sia davvero potente.


La domanda era su come raggiungere questo obiettivo nelle classi ES6.
Michael Franzl,

È possibile utilizzare lo stesso metodo esatto nelle classi ES6. Le classi ES6 sono principalmente lo zucchero oltre a funzioni come quelle presentate nel mio esempio. È del tutto possibile che il poster originale stia utilizzando un transpiler, nel qual caso WeakMaps o Symbols richiederanno comunque polifillamenti. La mia risposta è valida a prescindere.
guitarino,

2

Vedi questa risposta per una soluzione 'class' pulita e semplice con un'interfaccia pubblica e privata e supporto per la composizione


2

Ho trovato una soluzione molto semplice, basta usare Object.freeze(). Ovviamente il problema è che non puoi aggiungere nulla all'oggetto in seguito.

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

questo disabiliterà anche il metodo setter comesetName(name) { this.name = name; }
ngakak,

2

Uso questo modello e ha sempre funzionato per me

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

In realtà è possibile.
1. Innanzitutto, creare la classe e nel costruttore restituire la _publicfunzione chiamata .
2. Nella _publicfunzione chiamata passa il thisriferimento (per ottenere l'accesso a tutti i metodi e oggetti di scena privati) e tutti gli argomenti da constructor (che verranno passati new Names())
3. Nell'ambito della _publicfunzione c'è anche la Namesclasse con l'accesso a this(_this ) riferimento della Namesclasse privata

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

Puoi provare questo https://www.npmjs.com/package/private-members

Questo pacchetto salverà i membri per istanza.

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.