Privé-eigenschappen in JavaScript ES6-klassen

Is het mogelijk om privé-eigenschappen te maken in ES6-klassen?

Hier is een voorbeeld.
Hoe kan ik toegang tot instance.propertyvoorkomen?

class Something {
  constructor(){
    this.property = "test";
  }
}
var instance = new Something();
console.log(instance.property); //=> "test"

Antwoord 1, autoriteit 100%

Kort antwoord, nee, er is geen native ondersteuning voor privé-eigendommen met ES6-klassen.

Maar je zou dat gedrag kunnen nabootsen door de nieuwe eigenschappen niet aan het object te koppelen, maar ze binnen een klassenconstructor te houden, en getters en setters te gebruiken om de verborgen eigenschappen te bereiken. Merk op dat de getters en setters opnieuw worden gedefinieerd bij elke nieuwe instantie van de klasse.

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

Antwoord 2, autoriteit 88%

Private class-functiesbevindt zich in Fase 3-voorstel. De meeste functies worden ondersteunddoor alle belangrijke browsers.

class Something {
  #property;
  constructor(){
    this.#property = "test";
  }
  #privateMethod() {
    return 'hello world';
  }
  getPrivateMessage() {
      return this.#property;
  }
}
const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> test
console.log(instance.#property); //=> Syntax error

Antwoord 3, autoriteit 71%

Om het antwoord van @loganfsmyth uit te breiden:

De enige echt persoonlijke gegevens in JavaScript zijn nog steeds bereikvariabelen. U kunt geen privé-eigenschappen hebben in de zin van eigenschappen die intern op dezelfde manier toegankelijk zijn als openbare eigenschappen, maar u kunt bereikvariabelen gebruiken om privégegevens op te slaan.

Gevarieerde variabelen

De benadering hier is om het bereik van de constructorfunctie, die privé is, te gebruiken om privégegevens op te slaan. Om ervoor te zorgen dat methoden toegang hebben tot deze privégegevens, moeten ze ook binnen de constructor worden gemaakt, wat betekent dat u ze bij elke instantie opnieuw maakt. Dit is een prestatie- en geheugenstraf, maar sommigen geloven dat de straf acceptabel is. De straf kan worden vermeden voor methoden die geen toegang tot privégegevens nodig hebben door ze zoals gewoonlijk aan het prototype toe te voegen.

Voorbeeld:

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

Een WeakMap kan worden gebruikt om prestatie- en geheugenverlies van de vorige benadering te voorkomen. WeakMaps koppelen gegevens aan objecten (hier instanties) op een zodanige manier dat ze alleen toegankelijk zijn met die WeakMap. We gebruiken dus de methode met scoped-variabelen om een ​​privé WeakMap te maken en gebruiken vervolgens die WeakMap om privégegevens op te halen die zijn gekoppeld aan this. Dit is sneller dan de methode met scoped-variabelen, omdat al uw instanties een enkele WeakMap kunnen delen, dus u hoeft geen methoden opnieuw te maken om ze alleen toegang te geven tot hun eigen WeakMaps.

Voorbeeld:

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

Dit voorbeeld gebruikt een Object om één WeakMap te gebruiken voor meerdere privé-eigenschappen; je kunt ook meerdere WeakMaps gebruiken en ze gebruiken zoals age.set(this, 20), of een kleine wrapper schrijven en deze op een andere manier gebruiken, zoals privateProps.set(this, 'age', 0).

De privacy van deze benadering kan theoretisch worden geschonden door te knoeien met het globale WeakMap-object. Dat gezegd hebbende, alle JavaScript kan worden verbroken door verminkte globals. Onze code is al gebouwd in de veronderstelling dat dit niet gebeurt.

(Deze methode kan ook worden gedaan met Map, maar WeakMapis beter omdat Mapgeheugenlekken zal veroorzaken, tenzij je heel voorzichtig bent , en voor dit doel zijn de twee niet anders.)

Halfantwoord: symbolen met bereik

Een symbool is een type primitieve waarde die als eigenschapsnaam kan dienen. U kunt de methode met een bereikvariabele gebruiken om een ​​privésymbool te maken en vervolgens privégegevens op te slaan op this[mySymbol].

De privacy van deze methode kan worden geschonden met behulp van Object.getOwnPropertySymbols, maar is enigszins onhandig om te doen.

Voorbeeld:

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)`.

Halfantwoord: onderstrepingstekens

De oude standaard, gebruik gewoon een openbare eigenschap met een onderstrepingsteken voorvoegsel. Hoewel het op geen enkele manier een privé-eigendom is, is deze conventie wijdverbreid genoeg om goed te communiceren dat lezers het eigendom als privé moeten behandelen, wat vaak de klus klopt. In ruil voor deze tijdspanne krijgen we een aanpak die gemakkelijker te lezen, gemakkelijker te typen en sneller is.

Voorbeeld:

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.

Conclusie

Vanaf ES2017 is er nog steeds geen perfecte manier om privé-eigendommen te maken. Verschillende benaderingen hebben voor- en nadelen. Scoped variabelen zijn echt privé; scoped WeakMaps zijn erg privé en praktischer dan scoped variabelen; scoped Symbolen zijn redelijk privé en redelijk praktisch; underscores zijn vaak privé genoeg en erg praktisch.


Antwoord 4, autoriteit 4%

Hangt af van wie je het vraagt🙂

Er is geen privateeigenschapmodifier opgenomen in de Maximum minimale klassenvoorsteldat lijkt te zijn gemaakt in de huidige concept.

Er kan echter ondersteuning zijn voorprivénamen, waarmee privé-eigendommen mogelijk zijn – en dat zouden ze waarschijnlijk ook kunnen worden ook gebruikt in klassedefinities.


Antwoord 5, autoriteit 3%

Het gebruik van ES6-modules (aanvankelijk voorgesteld door @d13) werkt goed voor mij. Het bootst privé-eigendommen niet perfect na, maar je kunt er in ieder geval op vertrouwen dat eigendommen die privé zouden moeten zijn, niet buiten je klas zullen lekken. Hier is een voorbeeld:

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

De verbruikscode kan er dan als volgt uitzien:

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

Update (Belangrijk):

Zoals @DanyalAytekin in de opmerkingen heeft uiteengezet, zijn deze privé-eigendommen statisch, dus globaal van opzet. Ze zullen goed werken bij het werken met Singletons, maar wees voorzichtig met Transient-objecten. Uitbreiding van het bovenstaande voorbeeld:

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

Antwoord 6, autoriteit 3%

Ja – u kunt een ingekapselde eigenschap maken, maar dit is niet gedaan met toegangsmodifiers (publiek|privé), althans niet met ES6.

Hier is een eenvoudig voorbeeld hoe het kan met ES6:

1 Maak een klas aan met behulp van classwoord

2 Binnenin is de constructor een variabele met een blokbereik declareren met behulp van letOF constgereserveerde woorden -> omdat ze een blokbereik hebben, zijn ze niet toegankelijk van buitenaf (ingekapseld)

3 Om enige toegangscontrole (setters|getters) tot die variabelen toe te staan, kunt u de instantiemethode declareren in de constructor met behulp van: this.methodName=function(){}syntaxis

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

Laten we het nu controleren:

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

Antwoord 7, autoriteit 3%

Voltooiing van @d13 en de opmerkingen van @johnny-oshika en @DanyalAytekin:

Ik denk dat we in het voorbeeld van @johnny-oshika normale functies kunnen gebruiken in plaats van pijlfuncties en ze dan .bindmet het huidige object plus een _privatesobject als een kerrieparameter:

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

Voordelen die ik kan bedenken:

  • we kunnen privémethoden hebben (_greeten _updateMessagewerken als privémethoden, zolang we de referenties niet export)
  • hoewel ze niet in het prototype zitten, zullen de bovengenoemde methoden geheugen besparen omdat de instanties één keer worden gemaakt, buiten de klasse (in plaats van ze in de constructor te definiëren)
  • we lekken geen globals omdat we in een module zitten
  • we kunnen ook privé-eigenschappen hebben met behulp van het gebonden _privatesobject

Enkele nadelen die ik kan bedenken:

Een actief fragment is hier te vinden: http://www.webpackbin.com/NJgI5J8lZ


Antwoord 8, autoriteit 2%

Een andere benadering van “privé”

In plaats van te vechten tegen het feit dat privézichtbaarheid momenteel niet beschikbaar is in ES6, heb ik besloten om een ​​meer praktische benadering te volgen die prima werkt als je IDE JSDoc ondersteunt (bijv. Webstorm). Het idee is om de @private-tagte gebruiken. Wat de ontwikkeling betreft, zal de IDE voorkomen dat u toegang krijgt tot een privélid van buiten zijn klasse. Werkt redelijk goed voor mij en het is erg handig geweest voor het verbergen van interne methoden, dus de functie voor automatisch aanvullen laat me precies zien wat de klas echt wilde onthullen. Hier is een voorbeeld:

automatisch aanvullen met alleen openbare dingen


Antwoord 9, autoriteit 2%

O, zoveel exotische oplossingen! Ik geef meestal niet om privacy, dus ik gebruik “pseudo privacy”zoals het hier wordt gezegd. Maar als het me iets kan schelen (als daar speciale vereisten voor zijn), gebruik ik zoiets als in dit voorbeeld:

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

Een andere mogelijke implementatie van functie (constructor) Job:

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

Antwoord 10, autoriteit 2%

WeakMap

  • ondersteund in IE11 (symbolen niet)
  • hard-private (rekwisieten die symbolen gebruiken zijn soft-private vanwege Object.getOwnPropertySymbols)
  • kan er heel netjes uitzien (in tegenstelling tot sluitingen waarvoor alle rekwisieten en methoden in de constructor nodig zijn)

Definieer eerst een functie om WeakMap in te pakken:

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

Configureer vervolgens een referentie buiten uw klas:

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

Opmerking: classwordt niet ondersteund door IE11, maar ziet er in het voorbeeld schoner uit.


Antwoord 11, autoriteit 2%

Ik kwam dit bericht tegen toen ik op zoek was naar de beste werkwijze voor ‘privégegevens voor lessen’. Er werd vermeld dat een paar van de patronen prestatieproblemen zouden hebben.

Ik heb een paar jsperf-tests samengesteld op basis van de 4 hoofdpatronen uit het online boek “Exploring ES6”:

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

De tests zijn hier te vinden:

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

In Chrome 63.0.3239 / Mac OS X 10.11.6 waren de best presterende patronen ‘Privégegevens via constructoromgevingen’ en ‘Privégegevens via een naamgevingsconventie’. Voor mij presteerde Safari goed voor WeakMap, maar Chrome niet zo goed.

Ik ken de impact op het geheugen niet, maar het patroon voor ‘constructor-omgevingen’, waarvan sommigen hadden gewaarschuwd dat het een prestatieprobleem zou zijn, was zeer performant.

De 4 basispatronen zijn:

Privégegevens via constructoromgevingen

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

Privégegevens via constructoromgevingen 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();

Privégegevens via een naamgevingsconventie

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

Privégegevens via WeakMaps

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

Privégegevens via symbolen

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

Antwoord 12, autoriteit 2%

Persoonlijk vind ik het voorstel van de bind-operator::en zou het dan combineren met de genoemde oplossing @d13, maar blijf voorlopig bij het antwoord van @d13 waarbij je het sleutelwoord exportvoor je klas gebruikt en de privéfuncties in de module plaatst.

Er is nog een lastige oplossing die hier niet is genoemd en die volgt, is een meer functionele benadering en zou het mogelijk maken om alle privé-rekwisieten/-methoden binnen de klas te hebben.

Privé.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

commentaar erop wordt op prijs gesteld.


Antwoord 13, autoriteit 2%

Ik denk dat Benjamins antwoordwaarschijnlijk het beste is voor de meeste gevallen, totdat de taal native expliciet privévariabelen ondersteunt.

>

Als u echter om de een of andere reden toegang moet voorkomen met Object.getOwnPropertySymbols(), een methode die ik heb overwogen te gebruiken, is het toevoegen van een unieke, niet-configureerbare, niet-opsombare, niet-schrijfbare eigenschap die kan worden gebruikt als een eigenschapsidentificatie voor elk object in constructie (zoals een uniek Symbol, als je niet al een andere unieke eigenschap hebt zoals een id). Houd dan gewoon een kaart bij van de ‘private’ variabelen van elk object met die identifier.

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"

Het potentiële voordeel van deze aanpak ten opzichte van het gebruik van een WeakMapis snellere toegangstijdals de prestaties wordt een punt van zorg.


Antwoord 14

Ik geloof dat het mogelijk is om ‘het beste van twee werelden’ te krijgen door gebruik te maken van sluitingen in constructors. Er zijn twee varianten:

Alle gegevensleden zijn privé

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

Snippet uitvouwen


Antwoord 15

In feite is het mogelijk om symbolen en proxy’s te gebruiken. Je gebruikt de symbolen in de class scope en stelt twee traps in een proxy in: één voor het klassenprototype zodat de Reflect.ownKeys(instance) of Object.getOwnPropertySymbols je symbolen niet weggeven, de andere is voor de constructor zelf dus wanneer new ClassName(attrs)wordt aangeroepen, wordt de geretourneerde instantie onderschept en worden de eigen eigenschappensymbolen geblokkeerd.
Hier is de code:

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

Snippet uitvouwen


Antwoord 16

Zelfs Typescript kan het niet. Uit hun documentatie:

Als een lid als privé is gemarkeerd, is het niet toegankelijk van buiten de bijbehorende klasse. Bijvoorbeeld:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // Error: 'name' is private;

Maar getranspileerd op hun speeltuingeeft dit:

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

Dus hun “privé” zoekwoord is niet effectief.


Antwoord 17

Kom erg laat naar dit feest, maar ik heb de OP-vraag in een zoekopdracht gevonden, dus…
Ja, u kunt privé-eigendommen hebben door de klassedeclaratie in een sluiting te wikkelen

Er is een voorbeeld van hoe ik privémethoden heb in deze codepen. In het onderstaande fragment heeft de klasse Subscribable twee ‘private’ functies processen processCallbacks. Alle eigenschappen kunnen op deze manier worden toegevoegd en ze worden privé gehouden door het gebruik van de sluiting. IMO-privacy is een zeldzame behoefte als zorgen goed gescheiden zijn en Javascript niet opgeblazen hoeft te worden door meer syntaxis toe te voegen wanneer een afsluiting het werk netjes doet.

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

Ik vind deze aanpak prettig omdat het de zorgen goed scheidt en de zaken echt privé houdt. Het enige nadeel is de noodzaak om ‘zelf’ (of iets dergelijks) te gebruiken om naar ‘dit’ te verwijzen in de privé-inhoud.


Antwoord 18

Ja, dat kan helemaal, en vrij gemakkelijk ook. Dit wordt gedaan door uw privévariabelen en functies bloot te leggen door de prototype-objectgrafiek in de constructor te retourneren. Dit is niets nieuws, maar neem een ​​beetje js foo om de elegantie ervan te begrijpen. Op deze manier worden geen globale scoped of zwakke kaarten gebruikt. Het is een vorm van reflectie die in de taal is ingebouwd. Afhankelijk van hoe je hier gebruik van maakt; men kan ofwel een uitzondering forceren die de oproepstack onderbreekt, of de uitzondering begraven als een undefined. Dit wordt hieronder gedemonstreerd en u kunt hier

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

Snippet uitvouwen


Antwoord 19

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"

Antwoord 20

Een andere manier vergelijkbaar met de laatste twee geposte

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

Antwoord 21

Ik heb een heel eenvoudige oplossing gevonden, gebruik gewoon Object.freeze(). Het probleem is natuurlijk dat je later niets aan het object kunt toevoegen.

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

Antwoord 22

Deze code demonstreert privé en openbaar, statisch en niet-statisch, instantie- en klasseniveau, variabelen, methoden en eigenschappen.

https://codesandbox.io/s/class-demo-837bj

class Animal {
    static count = 0 // class static public
    static #ClassPriVar = 3 // class static private
    constructor(kind) {
        this.kind = kind // instance public property
        Animal.count++
        let InstancePriVar = 'InstancePriVar: ' + kind // instance private constructor-var
        log(InstancePriVar)
        Animal.#ClassPriVar += 3
        this.adhoc = 'adhoc' // instance public property w/out constructor- parameter
    }
    #PawCount = 4 // instance private var
    set Paws(newPawCount) {
        // instance public prop
        this.#PawCount = newPawCount
    }
    get Paws() {
        // instance public prop
        return this.#PawCount
    }
    get GetPriVar() {
        // instance public prop
        return Animal.#ClassPriVar
    }
    static get GetPriVarStat() {
        // class public prop
        return Animal.#ClassPriVar
    }
    PrintKind() {
        // instance public method
        log('kind: ' + this.kind)
    }
    ReturnKind() {
        // instance public function
        return this.kind
    }
    /* May be unsupported
    get #PrivMeth(){  // instance private prop
        return Animal.#ClassPriVar + ' Private Method'
    }
    static get #PrivMeth(){  // class private prop
        return Animal.#ClassPriVar + ' Private Method'
    }
    */
}
function log(str) {
    console.log(str)
}
// TESTING
log(Animal.count) // static, avail w/out instance
log(Animal.GetPriVarStat) // static, avail w/out instance
let A = new Animal('Cat')
log(Animal.count + ': ' + A.kind)
log(A.GetPriVar)
A.PrintKind()
A.Paws = 6
log('Paws: ' + A.Paws)
log('ReturnKind: ' + A.ReturnKind())
log(A.adhoc)
let B = new Animal('Dog')
log(Animal.count + ': ' + B.kind)
log(B.GetPriVar)
log(A.GetPriVar) // returns same as B.GetPriVar. Acts like a class-level property, but called like an instance-level property. It's cuz non-stat fx requires instance.
log('class: ' + Animal.GetPriVarStat)
// undefined
log('instance: ' + B.GetPriVarStat) // static class fx
log(Animal.GetPriVar) // non-stat instance fx
log(A.InstancePriVar) // private
log(Animal.InstancePriVar) // private instance var
log('PawCount: ' + A.PawCount) // private. Use getter
/* log('PawCount: ' + A.#PawCount) // private. Use getter
log('PawCount: ' + Animal.#PawCount) // Instance and private. Use getter */

Snippet uitvouwen


Antwoord 23

Toen ik het vorige antwoord las, dacht ik dat dit voorbeeld de bovenstaande oplossingen kan samenvatten

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

UPDATE

is het nu mogelijk om echte privé-eigenschappen en -methoden te maken (voorlopig in ieder geval in op Chrome gebaseerde browsers).

De syntaxis is best netjes

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

Houd er rekening mee dat u voor het ophalen van statische verwijzingen this.constructor.#privateniet zou gebruiken, omdat dit zijn subklassen zou afbreken. U moet een verwijzing naar de juiste klasse gebruiken om de statische privéverwijzingen op te halen (die alleen beschikbaar zijn binnen de methoden van die klasse), dwz MyClass.#private.


Antwoord 24

De meeste antwoorden zeggen dat het onmogelijk is, of vereisen dat je een WeakMap of Symbol gebruikt, wat ES6-functies zijn waarvoor waarschijnlijk polyfills nodig zijn. Er is echter een andere manier! Bekijk dit eens:

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

Snippet uitvouwen


Antwoord 25

Zie dit antwoordvoor een schone & eenvoudige ‘klasse’-oplossing met een privé- en openbare interface en ondersteuning voor compositie


Antwoord 26

Ik gebruik dit patroon en het heeft altijd gewerkt voor mij

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

Other episodes