564 Stimmen

Private Eigenschaften in JavaScript ES6 Klassen

Ist es möglich, private Eigenschaften in ES6-Klassen zu erstellen?

Hier ist ein Beispiel. Wie kann ich den Zugriff auf instance.property verhindern?

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

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

4voto

Paul Whipp Punkte 14811

Ich komme sehr spät zu dieser Party, aber ich bin auf die OP-Frage in einer Suche gestoßen, also... Ja, Sie können private Eigenschaften haben, indem Sie die Klassendeklaration in einem Closure umhüllen

Es gibt ein Beispiel, wie ich private Methoden in diesem Codepen habe. Im folgenden Beispiel hat die Klasse Subscribable zwei 'private' Funktionen process und processCallbacks. Auf diese Weise können beliebige Eigenschaften hinzugefügt werden und sie bleiben privat durch die Verwendung des Closures. Meiner Meinung nach ist Privatsphäre selten erforderlich, wenn die Anliegen gut getrennt sind, und Javascript muss nicht durch Hinzufügen von mehr Syntax aufgebläht werden, wenn ein Closure die Arbeit ordentlich erledigt.

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

Ich mag diesen Ansatz, weil er die Anliegen schön trennt und die Dinge wirklich privat hält. Der einzige Nachteil ist die Notwendigkeit, 'self' (oder etwas Ähnliches) zu verwenden, um auf 'this' im privaten Inhalt zu verweisen.

4voto

1-14x0r Punkte 1677

Ja, das geht absolut und auch ziemlich einfach. Dies wird erreicht, indem Sie Ihre privaten Variablen und Funktionen freigeben, indem Sie den Prototyp-Objektgraphen im Konstruktor zurückgeben. Das ist nichts Neues, erfordert aber ein wenig Verständnis von JavaScript, um die Eleganz davon zu erkennen. Auf diese Weise wird weder globaler Scope noch WeakMaps verwendet. Es handelt sich um eine Form von Reflektion, die in die Sprache integriert ist. Je nachdem, wie Sie dies nutzen, kann ein Fehler erzwungen werden, der den Aufrufer unterbricht, oder der Fehler als undefined abgefangen werden. Dies ist unten demonstriert und weitere Informationen zu diesen Funktionen finden Sie 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

3voto

asdru Punkte 1067

Beim Lesen der vorherigen Antwort dachte ich, dass dieses Beispiel die oben genannten Lösungen zusammenfassen kann

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('hidden abrufen');
            return this[hidden];
        }

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

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

        get privileged(){
            console.log('privilegierte Methode aufrufen');
            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

Jetzt ist es möglich, echte private Eigenschaften und Methoden zu erstellen (zumindest vorerst in Chrome-basierten Browsern).

Die Syntax ist ziemlich ordentlich

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

    // Auch die Verwendung ist ziemlich unkompliziert
    method(){
        return (
            this.#privateMethod() +
            this.#privateProperty +
            MyClass.#privateStatic +
            MyClass.#privateStaticMethod() +
            MyClass.#privateStaticGetter
        )
    }
}

new MyClass().method()
// gibt 15 zurück

Beachten Sie, dass zum Abrufen von statischen Referenzen nicht this.constructor.#private verwendet würde, weil es seine Unterklassen brechen würde. Sie müssen eine Referenz auf die richtige Klasse verwenden, um ihre statischen privaten Referenzen abzurufen (die nur innerhalb der Methoden dieser Klasse verfügbar sind), d.h. MyClass.#private.

3voto

johny why Punkte 1780

Dieser Code demonstriert private und öffentliche, statische und nicht-statische, Instanz- und Klassenvariablen, Methoden und Eigenschaften.

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

class Animal {
    static count = 0 // Klassen-Static öffentlich
    static #ClassPriVar = 3 // Klassen-Static privat

    constructor(kind) {
        this.kind = kind // Instanz-Öffentliche Eigenschaft
        Animal.count++
        let InstancePriVar = 'InstancePriVar: ' + kind // Instanz-Privater Konstruktor-Var
        log(InstancePriVar)
        Animal.#ClassPriVar += 3
        this.adhoc = 'adhoc' // Instanz-Öffentliche Eigenschaft ohne Konstruktor-Parameter
    }

    #PawCount = 4 // Instanz-Privater Var

    set Paws(newPawCount) {
        // Instanz-Öffentliche Eigenschaft
        this.#PawCount = newPawCount
    }

    get Paws() {
        // Instanz-Öffentliche Eigenschaft
        return this.#PawCount
    }

    get GetPriVar() {
        // Instanz-Öffentliche Eigenschaft
        return Animal.#ClassPriVar
    }

    static get GetPriVarStat() {
        // Klassen-Öffentliche Eigenschaft
        return Animal.#ClassPriVar
    }

    PrintKind() {
        // Instanz-Öffentliche Methode
        log('kind: ' + this.kind)
    }

    ReturnKind() {
        // Instanz-Öffentliche Funktion
        return this.kind
    }

    /* Möglicherweise nicht unterstützt

    get #PrivMeth(){  // Instanz-Privat-Eigenschaft
        return Animal.#ClassPriVar + ' Private Method'
    }

    static get #PrivMeth(){  // Klassen-Privat-Eigenschaft
        return Animal.#ClassPriVar + ' Private Method'
    }
    */
}

function log(str) {
    console.log(str)
}

// TESTEN

log(Animal.count) // Static, verfügbar ohne Instanz
log(Animal.GetPriVarStat) // Static, verfügbar ohne Instanz

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) // Gibt dasselbe wie B.GetPriVar zurück. Verhält sich wie eine Klassen-Eigenschaft, wird aber wie eine Instanz-Eigenschaft aufgerufen. Das liegt daran, dass nicht statische fx eine Instanz erfordert.

log('class: ' + Animal.GetPriVarStat)

// undefiniert
log('instance: ' + B.GetPriVarStat) // Statische Klassen-Fx
log(Animal.GetPriVar) // Nicht-statische Instanz-Fx
log(A.InstancePriVar) // privat
log(Animal.InstancePriVar) // Private Instanz-Var
log('PawCount: ' + A.PawCount) // Privat. Verwenden Sie den Getter
/* log('PawCount: ' + A.#PawCount) // Privat. Verwenden Sie den Getter
log('PawCount: ' + Animal.#PawCount) // Instanz und privat. Verwenden Sie den Getter */

3voto

Nikola Andreev Punkte 634

Ich habe eine sehr einfache Lösung gefunden, einfach Object.freeze() verwenden. Natürlich ist das Problem, dass Sie später nichts mehr dem Objekt hinzufügen können.

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

let cat = new Cat('Garfield', 5)
cat.age = 6 // funktioniert nicht, wirft sogar einen Fehler im Strict-Modus

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X