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"

348voto

Alister Punkte 24134

Private-Klassenmerkmale werden jetzt von der Mehrheit der Browser unterstützt.

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

325voto

MetalGodwin Punkte 3657

Aktualisierung: Siehe die Antwort anderer, diese ist veraltet.

Kurze Antwort, nein, es gibt keine nativen Unterstützung für private Eigenschaften mit ES6-Klassen.

Aber Sie könnten dieses Verhalten nachahmen, indem Sie die neuen Eigenschaften nicht an das Objekt anhängen, sondern sie im Klassenkonstruktor halten und Getter und Setter verwenden, um auf die versteckten Eigenschaften zuzugreifen. Beachten Sie, dass die Getter und Setter bei jeder neuen Instanz der Klasse neu definiert werden.

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

265voto

twhb Punkte 3934

Ja, füge dem Namen ein # als Präfix hinzu und nehme ihn in die Klassendefinition auf, nicht nur in den Konstruktor.

MDN Docs

Echte private Eigenschaften wurden schließlich in ES2022 hinzugefügt. Seit dem 01.01.2023 werden private Eigenschaften (Felder und Methoden) in allen gängigen Browsern seit mindestens einem Jahr unterstützt, aber 5-10% der Benutzer arbeiten immer noch mit älteren Browsern [Can I Use].

Beispiel:

class Person {
  #age

  constructor(name) {
    this.name = name; // dies ist öffentlich
    this.#age = 20; // dies ist privat
  }

  greet() {
    // hier können wir sowohl auf Name als auch auf Alter zugreifen
    console.log(`Name: ${this.name}, Alter: ${this.#age}`);
  }
}

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

// hier können wir auf den Namen, aber nicht auf das Alter zugreifen

Im Folgenden sind Methoden aufgeführt, um Eigenschaften in vor-ES2022-Umgebungen privat zu halten, mit verschiedenen Kompromissen.

Begrenzte Variablen

Der Ansatz hier besteht darin, den Bereich der Konstruktorfunktion, der privat ist, zu verwenden, um private Daten zu speichern. Damit Methoden Zugriff auf diese privaten Daten haben, müssen sie ebenfalls im Konstruktor erstellt werden, was bedeutet, dass sie mit jeder Instanz neu erstellt werden. Dies ist mit einer Leistungseinbuße und erhöhtem Speicherbedarf verbunden, kann aber akzeptabel sein. Die Strafe kann vermieden werden für Methoden, die keinen Zugriff auf private Daten benötigen, indem sie auf herkömmliche Weise deklariert werden.

Beispiel:

class Person {
  constructor(name) {
    let age = 20; // dies ist privat
    this.name = name; // dies ist öffentlich

    this.greet = () => {
      // hier können wir sowohl auf Name als auch auf Alter zugreifen
      console.log(`Name: ${this.name}, Alter: ${age}`);
    };
  }

  anotherMethod() {
    // hier können wir auf den Namen, aber nicht auf das Alter zugreifen
  }
}

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

// hier können wir auf den Namen, aber nicht auf das Alter zugreifen

Begrenzte WeakMap

Ein WeakMap kann verwendet werden, um die Leistung des obigen Ansatzes zu verbessern, jedoch auf Kosten von noch mehr Unordnung. WeakMaps verknüpfen Daten mit Objekten (hier, Klasseninstanzen) so, dass sie nur mit dieser WeakMap darauf zugreifen können. Daher verwenden wir die Methode der begrenzten Variablen zur Erstellung einer privaten WeakMap und verwenden dann diese WeakMap, um private Daten, die mit this verknüpft sind, abzurufen. Dies ist schneller als die Methode mit begrenzten Variablen, da alle Instanzen eine einzige WeakMap gemeinsam nutzen können und somit Methoden nicht neu erstellt werden müssen, um auf ihre eigenen WeakMaps zugreifen zu können.

Beispiel:

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

  return class Person {
    constructor(name) {
      this.name = name; // dies ist öffentlich
      privateProps.set(this, {age: 20}); // dies ist privat
    }

    greet() {
      // Hier können wir sowohl auf Name als auch auf Alter zugreifen
      console.log(`Name: ${this.name}, Alter: ${privateProps.get(this).age}`);
    }
  };
})();

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

// hier können wir auf den Namen, aber nicht auf das Alter zugreifen

Bei diesem Beispiel wird eine WeakMap mit Objektschlüsseln verwendet, um eine WeakMap für mehrere private Eigenschaften zu verwenden. Sie könnten auch mehrere WeakMaps verwenden und sie auf die gleiche Weise wie privateAge.set(this, 20) verwenden oder einen kleinen Wrapper schreiben und ihn auf andere Weise verwenden, z. B. privateProps.set(this, 'age', 0).

Die Datenschutzmaßnahmen dieses Ansatzes könnten theoretisch durch Manipulation des globalen WeakMap-Objekts umgangen werden. Gesagt sei jedoch, dass das gesamte JavaScript durch veränderte Globale gefährdet ist.

(Dieser Ansatz könnte auch mit Map durchgeführt werden, aber WeakMap ist besser, da Map Speicherlecks verursacht, es sei denn, man ist sehr vorsichtig, und für diesen Zweck sind die beiden ansonsten nicht unterschiedlich.)

Halb-Antwort: Begrenzte Symbole

Ein Symbol ist ein Typ von primitivem Wert, der als Eigenschaftsname anstelle eines Strings dienen kann. Sie können die Methode der begrenzten Variablen verwenden, um ein privates Symbol zu erstellen und dann private Daten unter this[mySymbol] zu speichern.

Die Datenschutzmaßnahmen dieses Ansatzes können mit Object.getOwnPropertySymbols umgangen werden, sind jedoch etwas umständlich.

Beispiel:

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

  return class Person {
    constructor(name) {
      this.name = name; // dies ist öffentlich
      this[ageKey] = 20; // dies soll privat sein
    }

    greet() {
      // Hier können wir sowohl auf Name als auch auf Alter zugreifen
      console.log(`Name: ${this.name}, Alter: ${this[ageKey]}`);
    }
  }
})();

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

// Hier können wir auf Joe's Namen und, mit etwas Mühe, auf sein Alter zugreifen. Wir können
// nicht direkt auf ageKey zugreifen, aber wir können ihn erhalten, indem wir alle Symbol-Eigenschaften
// auf `joe` mit `Object.getOwnPropertySymbols(joe)` auflisten.

Beachten Sie, dass das Festlegen einer Eigenschaft als nicht aufzählbar mit Object.defineProperty nicht verhindert, dass sie in Object.getOwnPropertySymbols enthalten ist.

Halbabtörn: Unterstriche

Die alte Konvention besteht einfach darin, eine öffentliche Eigenschaft mit einem Unterstrichpräfix zu verwenden. Dies hält sie nicht privat, vermittelt jedoch gut an die Leser, dass sie als privat behandelt werden sollten, was oft ausreicht. Im Gegenzug erhalten wir einen Ansatz, der leichter lesbar, leichter zu tippen und schneller als die anderen Workarounds ist.

Beispiel:

class Person {
  constructor(name) {
    this.name = name; // dies ist öffentlich
    this._age = 20; // dies soll privat sein
  }

  greet() {
    // Hier können wir sowohl auf Name als auch auf Alter zugreifen
    console.log(`Name: ${this.name}, Alter: ${this._age}`);
  }
}

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

// Hier können wir sowohl auf Joe's Namen als auch auf sein Alter zugreifen. Aber wir wissen, dass wir nicht auf sein Alter zugreifen sollen, was uns möglicherweise stoppt.

Zusammenfassung

  • ES2022: großartig, aber noch nicht von allen Besuchern unterstützt
  • Begrenzte Variablen: privat, langsamer, umständlich
  • Begrenzte WeakMaps: hackbar, umständlich
  • Begrenzte Symbole: aufzählbar und hackbar, etwas umständlich
  • Unterstriche: nur eine Anfrage nach Datenschutz, keine anderen Nachteile

123voto

Benjamin Gruenbaum Punkte 259076

Aktualisierung: Ein Vorschlag mit schönerer Syntax ist auf dem Weg. Beiträge sind willkommen.


Ja, es gibt - für den begrenzten Zugriff auf Objekte - ES6 führt Symbole ein.

Symbole sind einzigartig, man kann nicht von außen darauf zugreifen außer mit Reflektion (wie bei privaten in Java/C#), aber jeder, der Zugriff auf ein Symbol von innen hat, kann es zur Schlüsselzugriff verwenden:

var property = Symbol();
class Something {
    constructor(){
        this[property] = "test";
    }
}

var instance = new Something();

console.log(instance.property); //=> undefined, nur mit Zugriff auf das Symbol zugreifbar

35voto

d13 Punkte 10147

Die Antwort lautet "Nein". Aber Sie können den privaten Zugriff auf Eigenschaften wie folgt erstellen:

(Der Vorschlag, dass Symbole verwendet werden könnten, um die Privatsphäre zu gewährleisten, war in einer früheren Version des ES6-Standards wahr, ist es aber nicht mehr der Fall: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html und https://stackoverflow.com/a/22280202/1282216. Für eine ausführlichere Diskussion über Symbole und Privatsphäre siehe: https://curiosity-driven.org/private-properties-in-javascript)

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