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"

2voto

kofifus Punkte 14007

Siehe diese Antwort für eine saubere und einfache 'class'-Lösung mit einer privaten und öffentlichen Schnittstelle und Unterstützung für Komposition

2voto

guitarino Punkte 348

Die meisten Antworten sagen entweder, dass es unmöglich ist oder dass du ein WeakMap oder Symbol verwenden musst, die ES6-Funktionen sind und wahrscheinlich Polyfills erfordern würden. Es gibt jedoch einen anderen Weg! Schau dir das hier an:

// 1. Erstelle Closure
var SomeClass = function() {
  // 2. Erstelle `key` innerhalb einer Closure
  var key = {};
  // Funktion zur Erstellung des privaten Speichers
  var private = function() {
    var obj = {};
    // Gib Funktion zurück, um auf den privaten Speicher mit `key` zuzugreifen
    return function(testkey) {
      if(key === testkey) return obj;
      // Wenn `key` falsch ist, kann auf den Speicher nicht zugegriffen werden
      console.error('Kann nicht auf private Eigenschaften zugreifen');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Erstelle privaten Speicher
    this._ = private();
    // 4. Greife auf den privaten Speicher mit dem `key` zu
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Verwendung der Eigenschaft aus dem Prototyp
  };
  return SomeClass;
}();

// Kann private Eigenschaft von innerhalb des Prototyps aus zugreifen
var instance = new SomeClass();
instance.test(); // `200` protokolliert

// Kann private Eigenschaft von außerhalb der Closure nicht zugreifen
var wrong_key = {};
instance._(wrong_key); // undefined; Fehler protokolliert

Ich nenne diese Methode Zugriffsmuster. Die wesentliche Idee ist, dass wir eine Closure haben, ein key innerhalb der Closure und wir erstellen ein privates Objekt (im Konstruktor), das nur zugegriffen werden kann, wenn du den key hast.

Wenn du interessiert bist, kannst du mehr darüber in meinem Artikel lesen. Mit dieser Methode kannst du pro Objekt Eigenschaften erstellen, auf die außerhalb der Closure nicht zugegriffen werden kann. Daher kannst du sie im Konstruktor oder im Prototyp verwenden, aber nicht an anderen Stellen. Ich habe diese Methode noch nirgendwo gesehen, aber ich denke, sie ist wirklich kraftvoll.

1voto

Abgesehen von den gegebenen Antworten können Sie auch einen Proxy verwenden, um "private Eigenschaften" zu erstellen, indem Sie nur den Proxy für den öffentlichen Code verfügbar machen. Die Instanz ist nur für den Konstruktor, gebundene Methoden und den Proxy selbst als receiver verfügbar.

Dies hat einige Vorteile gegenüber der Verwendung von Symbolen und WeakMaps.

  • Symbole sind aufzählbar und können von einem Proxy erfasst werden.
  • WeakMaps versagen, wenn die Instanz als instance !== new Proxy(instance) proxiziert wird.

Scheitern von WeakMap z.B.

const map = new WeakMap()

const instance = new SomeClass()
map.set(instance, 'foo')
// Irgendwo auf dem Weg im Code eines Drittanbieters
const proxy = new Proxy(instance, {})
assert(map.set(instance) === map.get(proxy)) // scheitern
const proxy2 = new Proxy(proxy, {})
// mehr Kopfschmerzen

Verwendung eines Proxy, um eine Instanz mit privaten Eigenschaften zu validieren

getProxy = (instance) => new Proxy(instance, {

    get: (target, name, receiver) => {
        console.log('get', { target, name, receiver })
        if (name[0] === '_') throw new Error('Kann nicht auf private Eigenschaft ' + name + ' zugreifen')
        return Reflect.get(target, name, receiver) 
    },
    set: (target, name, value, receiver) => {
        console.log('set', { target, name, value, receiver })
        if (name[0] === '_') throw new Error('Kann den privaten Wert ' + name + ' nicht setzen')
        return Reflect.set(target, name, value, receiver) 
    }

})

class PublicClass {

    constructor() {
        Object.defineProperty(this, '_privateProp', { enumerable: false, writable: true, configurable: false })
        return getProxy(this) // kann als Dekorator ausgelagert werden
    }

    getPrivatePropFail() {
        return this._privateProp // fehlerhaft
    }

    getPrivateProp = () => {
        return this._privateProp // okay
    }

    setPrivateProp = (value) => {
        return this._privateProp = value // okay
    }

}

pub = new PublicClass()

try {
    console.log('get pub._privateProp', pub._privateProp)
} catch(e) {
    console.error(e) 
}
try {
    console.log('set pub._privateProp', pub._privateProp = 'Sie versagen')
} catch(e) {
    console.error(e) 
}
pub.setPrivateProp('Sie okay')
console.log('pub.getPrivateProp()', pub.getPrivateProp())
console.log('pub', Object.keys(pub))

Die Vorteile dieses Ansatzes

  • Die Validierung des Zugriffs auf private Eigenschaften ist dekoriert (optional) auf der Instanz.
  • Die privaten Eigenschaften sind im Konsolen-, Debugging- und Testumgebungen inspizierbar und einfache Eigenschaften (keine Symbole oder Maps)
  • Sie kontrollieren die Validierung und Fehlerbehandlung

Die Nachteile

  • Der Proxy fügt Overhead und eine Abstraktionsebene hinzu
  • Das Debuggen zeigt den Proxy(), der das Objekt umgibt
  • Methoden, die auf private Eigenschaften zugreifen, müssen Pfeilfunktionen sein
  • Sie können die privaten Eigenschaften versehentlich preisgeben, wenn Sie die Instanz durch das Hinzufügen einer Methode getSelf = () => this

Anmerkungen:

Aufgrund des Overheads könnte diese Methode in Szenarien verwendet werden, in denen die Eigenschaftskapselung und die Klarheit des Debuggens den Overhead überwiegen. Zum Beispiel beim Befüllen von Modellen aus dem Speicher. z.B. model.setJSON(json) würde sicherstellen, dass keine privaten Eigenschaften verunstaltet werden.

Diese Methode kann weiter angepasst werden, um eine bessere Kapselung zu bieten, indem eine WeakMap zusammen mit dem Proxy verwendet wird, um sicherzustellen, dass "private" Eigenschaften nicht sichtbar sind, aber der Zugriff auf die WeakMap mit derselben Instanz in jedem Bereich ermöglicht wird. Dies geht jedoch zulasten der Lesbarkeit und des Debuggens.

1voto

frodeborli Punkte 1363

Ich bemerke, dass es hier Dutzende von Antworten gibt. Ich möchte meine Lösung teilen, die echte private Variablen in ES6-Klassen und in älterem JS gewährleistet.

var MyClass = (function() {
    var $ = new WeakMap();
    function priv(self) {
       var r = $.get(self);
       if (!r) $.set(self, r={});
       return r;
    }

    return class { /* verwenden Sie priv(this).prop innerhalb Ihrer Klasse */ } 
}();

Die Privatsphäre ist dadurch gewährleistet, dass die Außenwelt keinen Zugriff auf $ hat.

Wenn die Instanz verschwindet, wird der WeakMap die Daten freigeben.

Dies funktioniert definitiv in reinem Javascript, und ich glaube, dass sie in ES6-Klassen funktionieren, aber ich habe nicht getestet, ob $ innerhalb des Bereichs von Elementmethoden verfügbar sein wird.

1voto

Rohìt Jíndal Punkte 15250

Nach ES2022 können wir private Eigenschaften und Methoden in unserer JavaScript-Klasse hinzufügen.

Wir können private Felder definieren, indem wir ihren Namen mit # voranstellen.

Demo:

class Something {
  #property = "test"; // Durch Hinzufügen von # gefolgt von dem Eigenschaftsnamen machen wir es privat.
}

var instance = new Something();

console.log(instance.#property); // Uncaught SyntaxError: Private field '#property' must be declared in an enclosing class

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