Private Eigenschaften in JavaScript ES6-Klassen


444

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

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

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

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

5
Es gibt tatsächlich einen Vorschlag für Stufe 3 für diese Funktion - tc39.github.io/proposal-class-fields github.com/tc39/proposal-class-fields
arty

@arty Ich habe eine Antwort auf diese Frage
Alister

Antworten:


165

Private Felder (und Methoden) werden im ECMA-Standard implementiert . Sie können sie heute mit den Voreinstellungen für Babel 7 und Stufe 3 verwenden.

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

Ich frage mich, wie diese Klassenfelder funktionieren können. Sie können derzeit nicht thisim Konstruktor verwenden, bevor Sie aufrufen super(). Doch Babel stellt sie vor Super.
seeker_of_bacon

Wie konfiguriere ich ESLint, um #privateCrapSyntax zuzulassen ?
Marecky

6
Und was ist mit Eslint? Ich habe einen Parserfehler beim Gleichheitszeichen. Babel arbeitet, nur eslint kann diese neue js-Syntax nicht analysieren.
Martonx

6
Wow das ist sehr hässlich. Hashtag ist ein gültiger Charakter. Die Immobilie ist nicht wirklich privat, oder? Ich habe es in TypeScript überprüft. Private Mitglieder werden nicht privat oder schreibgeschützt (von außen) zusammengestellt. Einfach wie ein anderes (öffentliches) Eigentum deklariert. (ES5).
Dominik

2
Wie schreibt man damit private Methoden ? Kann ich das machen : #beep() {}; und das : async #bzzzt() {}?
4онстантин Ван

277

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

Sie können dieses Verhalten jedoch nachahmen, indem Sie die neuen Eigenschaften nicht an das Objekt anhängen, sondern in einem Klassenkonstruktor belassen und Getter und Setter verwenden, um die verborgenen Eigenschaften zu erreichen. Beachten Sie, dass die Getter und Setter für jede neue 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; }
}

1
Diese Lösung gefällt mir am besten. Ich bin damit einverstanden, dass es nicht für die Skalierung verwendet werden sollte, aber es ist perfekt für Klassen, die normalerweise nur einmal pro Include instanziiert werden.
Blake Regalia

2
Außerdem definieren Sie jede einzelne Komponente dieser Klasse jedes Mal neu, wenn eine neue erstellt wird.
Quentin Roy

10
Das ist so seltsam! In ES6 erstellen Sie mehr "Verschlusspyramiden" als vor ES6! Das Definieren von Funktionen INNERHALB eines Konstruktors sieht hässlicher aus als im obigen ES5-Beispiel.
Kokodoko

1
Da das OP speziell nach ES6-Klassen fragt, halte ich dies persönlich für eine schlechte Lösung, obwohl sie technisch funktioniert. Die Hauptbeschränkung besteht darin, dass jetzt jede Klassenmethode, die private Variablen verwendet, im Konstruktor deklariert werden muss, was die Vorteile der classSyntax in erster Linie erheblich untergräbt .
NanoWizard

10
Dies führt lediglich die Indirektion ein. Wie machen Sie nun die getNameund setNameEigenschaften privat?
aij

195

So erweitern Sie die Antwort von @ loganfsmyth:

Die einzigen wirklich privaten Daten in JavaScript sind Variablen mit Gültigkeitsbereich. Sie können nicht über private Eigenschaften im Sinne von Eigenschaften verfügen, auf die intern wie auf öffentliche Eigenschaften zugegriffen wird. Sie können jedoch Gültigkeitsbereichsvariablen zum Speichern privater Daten verwenden.

Gültigkeitsbereichsvariablen

Der Ansatz hier besteht darin, den Umfang der Konstruktorfunktion, die privat ist, zum Speichern privater Daten zu verwenden. Damit Methoden auf diese privaten Daten zugreifen können, müssen sie auch im Konstruktor erstellt werden. Dies bedeutet, dass Sie sie mit jeder Instanz neu erstellen. Dies ist eine Leistungs- und Gedächtnisstrafe, aber einige glauben, dass die Strafe akzeptabel ist. Die Strafe kann für Methoden vermieden werden, die keinen Zugriff auf private Daten benötigen, indem diese wie gewohnt zum Prototyp hinzugefügt werden.

Beispiel:

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

Eine WeakMap kann verwendet werden, um die Leistung und den Speicherverlust des vorherigen Ansatzes zu vermeiden. WeakMaps ordnen Daten Objekten (hier Instanzen) so zu, dass nur mit dieser WeakMap auf sie zugegriffen werden kann. Daher verwenden wir die Methode mit Gültigkeitsbereichsvariablen, um eine private WeakMap zu erstellen, und verwenden diese WeakMap dann, um private Daten abzurufen, die mit verknüpft sindthis . Dies ist schneller als die Methode mit Gültigkeitsbereichsvariablen, da alle Ihre Instanzen eine einzige WeakMap gemeinsam nutzen können. Sie müssen also keine Methoden neu erstellen, um sie auf ihre eigenen WeakMaps zugreifen zu lassen.

Beispiel:

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 diesem Beispiel wird ein Objekt verwendet, um eine WeakMap für mehrere private Eigenschaften zu verwenden. Sie können auch mehrere WeakMaps verwenden und sie wie verwenden age.set(this, 20)oder einen kleinen Wrapper schreiben und ihn auf andere Weise verwendenprivateProps.set(this, 'age', 0) .

Die Privatsphäre dieses Ansatzes könnte theoretisch durch Manipulationen an der Welt verletzt werden WeakMap Objekt . Das heißt, alle JavaScript kann durch verstümmelte Globals gebrochen werden. Unser Code basiert bereits auf der Annahme, dass dies nicht geschieht.

(Diese Methode könnte auch angewendet werden Map, WeakMapist jedoch besser, da dadurch MapSpeicherlecks entstehen, wenn Sie nicht sehr vorsichtig sind. Zu diesem Zweck unterscheiden sich die beiden nicht voneinander.)

Halbe Antwort: Symbole mit Gültigkeitsbereich

Ein Symbol ist eine Art primitiver Wert, der als Eigenschaftsname dienen kann. Mit der Methode der Gültigkeitsbereichsvariablen können Sie ein privates Symbol erstellen und dann private Daten unter speichernthis[mySymbol] .

Die Privatsphäre dieser Methode kann mit verletzt werden Object.getOwnPropertySymbols , ist aber etwas umständlich zu tun.

Beispiel:

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

Halbe Antwort: Unterstriche

Verwenden Sie bei der alten Standardeinstellung einfach eine öffentliche Eigenschaft mit einem Unterstrichpräfix. Obwohl diese Konvention in keiner Weise ein Privateigentum ist, ist sie weit genug verbreitet, um zu kommunizieren, dass die Leser das Eigentum als privat behandeln sollten, was häufig die Arbeit erledigt. Als Gegenleistung für diesen Fehler erhalten wir einen Ansatz, der einfacher zu lesen, einfacher zu tippen und schneller ist.

Beispiel:

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.

Fazit

Ab ES2017 gibt es noch keine perfekte Möglichkeit, private Immobilien zu erwerben. Verschiedene Ansätze haben Vor- und Nachteile. Gültigkeitsbereichsvariablen sind wirklich privat. WeakMaps mit Gültigkeitsbereich sind sehr privat und praktischer als Variablen mit Gültigkeitsbereich. Symbole mit Gültigkeitsbereich sind einigermaßen privat und recht praktisch. Unterstriche sind oft privat genug und sehr praktisch.


7
Das erste Beispiel-Snippet ("Gültigkeitsbereichsvariablen") ist ein Gesamt-Antimuster - jedes zurückgegebene Objekt hat eine andere Klasse. Tu das nicht. Wenn Sie privilegierte Methoden wünschen, erstellen Sie diese im Konstruktor.
Bergi

1
Das Umschließen einer Klasse in eine Funktion scheint den gesamten Zweck der Verwendung von Klassen zu zunichte zu machen. Wenn Sie die Funktion bereits zum Erstellen einer Instanz verwenden, können Sie auch alle Ihre privaten / öffentlichen Mitglieder in diese Funktion einfügen und das gesamte Klassenschlüsselwort vergessen.
Kokodoko

2
@Bergi @Kokodoko Ich habe den Ansatz für Bereichsvariablen so bearbeitet, dass er etwas schneller ist und nicht kaputt geht instanceof. Ich gebe zu, ich habe diesen Ansatz nur der Vollständigkeit halber als einbezogen angesehen und hätte mehr darüber nachdenken sollen, wozu er tatsächlich in der Lage ist.
Tristan

1
Hervorragende Erklärung! Ich bin immer noch überrascht, dass ES6 es tatsächlich schwieriger gemacht hat, eine private Variable zu simulieren, wobei Sie in ES5 einfach var und dies in einer Funktion verwenden können, um private und öffentliche zu simulieren.
Kokodoko

2
@Kokodoko Wenn Sie auf die Klasse verzichten und einfach alles in die Funktion einfügen, müssen Sie auch wieder die Vererbung mithilfe der Prototypmethode implementieren. Die Verwendung von "Erweitern auf Klassen" ist bei weitem ein sauberer Ansatz, daher ist die Verwendung einer Klasse innerhalb einer Funktion völlig akzeptabel.
AndroidDev

117

Update: Ein Vorschlag mit einer schöneren Syntax ist auf dem Weg. Beiträge sind willkommen.


Ja, es gibt - für den Zugriff auf Objekte mit Gültigkeitsbereich - ES6 führt Symbols ein .

Symbole sind einzigartig. Sie können nur von außen auf eines zugreifen (außer bei Privaten in Java / C #). Jeder, der Zugriff auf ein Symbol im Inneren hat, kann es jedoch für den Schlüsselzugriff verwenden:

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
Kannst du nicht benutzen Object.getOwnPropertySymbols? ;)
Qantas 94 Heavy

41
@BenjaminGruenbaum: Anscheinend gewährleisten Symbole keine echte Privatsphäre mehr: stackoverflow.com/a/22280202/1282216
d13

28
@trusktr durch drei Schlüssel? Durch die Symbole? Ja. Sehr ähnlich wie Sie Reflektion in Sprachen wie C # und Java verwenden können, um auf private Felder zuzugreifen. Bei Zugriffsmodifikatoren geht es nicht um Sicherheit, sondern um klare Absichten.
Benjamin Gruenbaum

9
Die Verwendung von Symbolen scheint ähnlich zu sein. const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();Dies ist nicht wirklich Privatsphäre, sondern Dunkelheit im Sinne von herkömmlichem JavaScript. Ich würde "privates" JavaScript als die Verwendung von Verschlüssen zum Einkapseln von Variablen betrachten. Diese Variablen sind daher nicht durch Reflexion zugänglich.
Trusktr

13
Auch glaube ich , dass die Verwendung privateund protectedwäre so viel sauberer als Schlüsselwörter Symboloder Name. Ich bevorzuge die Punktnotation gegenüber der Klammernotation. Ich würde gerne weiterhin einen Punkt für private Dinge verwenden. this.privateVar
Trusktr

33

Die Antwort ist nein". Sie können jedoch einen privaten Zugriff auf Eigenschaften wie diese erstellen:

(Der Vorschlag, dass Symbole verwendet werden könnten, um den Datenschutz zu gewährleisten, war in einer früheren Version der ES6-Spezifikation wahr, ist jedoch nicht mehr der Fall: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604. HTML und https://stackoverflow.com/a/22280202/1282216 . Eine längere Diskussion über Symbole und Datenschutz finden Sie unter: https://curiosity-driven.org/private-properties-in-javascript )


6
-1, dies beantwortet Ihre Frage nicht wirklich. (Sie können auch Verschlüsse mit IIFEs in ES5 verwenden). Private Eigenschaften können durch Reflektion in den meisten Sprachen (Java, C # usw.) aufgelistet werden. Bei privaten Immobilien geht es darum, anderen Programmierern die Absicht zu vermitteln und die Sicherheit nicht durchzusetzen.
Benjamin Gruenbaum

1
@BenjaminGruenbaum, ich weiß, ich wünschte ich hätte eine bessere Antwort, ich bin auch nicht glücklich damit.
13.

Ich denke, Symbole sind immer noch ein gültiger Weg, um unzugängliche Mitglieder in der Programmierumgebung zu erreichen. Ja, sie können immer noch gefunden werden, wenn Sie wirklich wollen, aber das ist nicht der Punkt, oder? Sie sollten keine vertraulichen Informationen darin speichern, aber Sie sollten dies trotzdem nicht im clientseitigen Code tun. Es dient jedoch dazu, eine Eigenschaft oder Methode vor einer externen Klasse zu verbergen.
Kokodoko

Die Verwendung von Variablen auf der Ebene eines Moduls als Ersatz für private Eigenschaften in einer Klasse führt zu einem singleton.behavior oder einem Verhalten, das den statitc-Eigenschaften ähnelt. Instanzen von vars werden gemeinsam genutzt.
Adrian Moisa

30

Der einzige Weg, um echte Privatsphäre in JS zu erhalten, ist das Scoping. Es gibt also keine Möglichkeit, eine Eigenschaft, zu der ein Mitglied gehört this, nur innerhalb der Komponente zugänglich zu machen. Der beste Weg, um wirklich private Daten in ES6 zu speichern, ist eine 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));
  }
}

Offensichtlich ist dies wahrscheinlich langsam und definitiv hässlich, aber es bietet Privatsphäre.

Denken Sie daran, dass auch dies nicht perfekt ist, weil Javascript so dynamisch ist. Jemand könnte es noch tun

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

Um Werte zu erfassen, während sie gespeichert werden, müssen Sie, wenn Sie besonders vorsichtig sein möchten , einen lokalen Verweis auf diesen erfassen .setund .getexplizit verwenden, anstatt sich auf den überschreibbaren Prototyp zu verlassen.

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
Als Vorschlag können Sie vermeiden, eine schwache Karte pro Eigenschaft zu verwenden, indem Sie ein Objekt als Wert verwenden. Auf diese Weise können Sie auch die Anzahl der Karten getauf eine pro Methode reduzieren (z const _ = privates.get(this); console.log(_.privateProp1);. B. ).
Quentin Roy

Ja, das ist auch eine Option. Ich habe mich meistens dafür entschieden, da es direkter dem entspricht, was ein Benutzer bei der Verwendung von Immobilien geschrieben hätte.
Loganfsmyth

@loganfsmyth const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"bedeutet das, dass Ihre Immobilie privat ist oder nicht?
Barbu Barbu

2
Damit dies funktioniert, muss der Code, der auf die Eigenschaft zugreift, auf das WeakMap-Objekt zugreifen, das normalerweise in einem Modul
gespeichert

22

Als zukünftige Referenz für andere Betrachter höre ich jetzt, dass die Empfehlung lautet, WeakMaps zum Speichern privater Daten zu verwenden.

Hier ist ein klareres, funktionierendes Beispiel:

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
Beachten Sie, dass diese Eigenschaften statisch sind.
Michael Theriot

8
Ich habe dich nicht abgelehnt, aber dein Beispiel für eine schwache Karte ist völlig falsch.
Benjamin Gruenbaum

4
Nämlich - Sie teilen die Daten zwischen allen Klasseninstanzen und nicht pro Instanz - darf ich sie zumindest beheben?
Benjamin Gruenbaum

1
In der Tat muss die Schwachkarte an eine bestimmte Instanz angehängt werden. Ein Beispiel finden Sie unter fitzgeraldnick.com/weblog/53 .
Widged

2
Laut MDN sind primitive Datentypen wie Symbole nicht als WeakMap-Schlüssel zulässig. MDN WeakMap-Dokumentation
Leepowell

12

Kommt drauf an wen du fragst :-)

Der Vorschlag für maximal minimale Klassen enthält keinen privateEigenschaftsmodifikator, der anscheinend in den aktuellen Entwurf aufgenommen wurde .

Es könnte jedoch sein Unterstützung für private Namen , die private Eigenschaften zulassen - und sie könnten wahrscheinlich auch in Klassendefinitionen verwendet werden.


3
Es ist höchst unwahrscheinlich, dass private Namen es in ES6 schaffen, obwohl sie an eine private Sache für ES7 denken.
Qantas 94 Heavy

@ Qantas94Heavy sowohl private Namen als auch eindeutige Zeichenfolgenwerte wurden nach meinem Verständnis durch Symbole ersetzt.
Benjamin Gruenbaum

Ja, es wird wahrscheinlich zu Symbolen. Allerdings werden die derzeit in der Spezifikation enthaltenen "Symbole" nur zur Beschreibung interner Eigenschaften wie [[Prototyp]] verwendet, und es gibt keine Möglichkeit, sie im Benutzercode zu erstellen und zu verwenden. Kennen Sie einige Dokumente?
Bergi

Ich habe gerade festgestellt, dass Module zum Einstellen der Privatsphäre verwendet werden können. Kombiniert mit Symbolen, die vielleicht alles sind, was Sie jemals brauchen würden ...?
13.

1
@Cody: Ihre gesamte Modulcode hat seinen eigenen Anwendungsbereich in ES6 sowieso keine Notwendigkeit für eine IEFE. Und ja, Symbole dienen der Eindeutigkeit (Vermeidung von Kollisionen) und nicht der Privatsphäre.
Bergi

10

Die Verwendung von ES6-Modulen (ursprünglich von @ d13 vorgeschlagen) funktioniert für mich gut. Es ahmt private Eigenschaften nicht perfekt nach, aber zumindest können Sie sicher sein, dass Eigenschaften, die privat sein sollten, nicht außerhalb Ihrer Klasse auslaufen. Hier ist ein Beispiel:

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

Dann kann der konsumierende Code folgendermaßen aussehen:

import Something from './something.js';

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

Update (wichtig):

Wie @DanyalAytekin in den Kommentaren dargelegt hat, sind diese privaten Eigenschaften statisch und daher global. Sie funktionieren gut, wenn Sie mit Singletons arbeiten, aber Sie müssen auf vorübergehende Objekte achten. Das obige Beispiel erweitern:

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
Gut für private static.
Danyal Aytekin

@DanyalAytekin: Das ist ein sehr guter Punkt. Diese privaten Eigenschaften sind statisch und daher global. Ich habe meine Antwort aktualisiert, um dies widerzuspiegeln.
Johnny Oshika

Je mehr ich über funktionale Programmierung lerne (insbesondere Elm und Haskell), desto mehr glaube ich, dass JS-Programmierer von einem modulbasierten Ansatz zur "Modularität" profitieren würden, anstatt von einem auf OOP-Klassen basierenden. Wenn wir uns ES6-Module als Grundlage für das Erstellen von Anwendungen vorstellen und Klassen vollständig vergessen, werden wir meiner Meinung nach insgesamt viel bessere Anwendungen erhalten. Könnten erfahrene Elm- oder Haskell-Benutzer diesen Ansatz kommentieren?
d13

1
Im Update sollte der zweite a.say(); // aseinb.say(); // b
grokky

versucht let _message = nullWeg, nicht so cool, wenn Konstruktor mehrmals aufrufen, es vermasselt.
Littlee

9

Vervollständigung von @ d13 und den Kommentaren von @ johnny-oshika und @DanyalAytekin:

Ich denke, in dem Beispiel von @ johnny-oshika könnten wir normale Funktionen anstelle von Pfeilfunktionen verwenden und diese dann .bindmit dem aktuellen Objekt plus einem _privatesObjekt als Curry-Parameter:

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

Vorteile, die mir einfallen:

  • Wir können private Methoden haben ( _greetund uns _updateMessagewie private Methoden verhalten, solange wir nicht exportdie Referenzen haben)
  • Obwohl sie nicht im Prototyp enthalten sind, sparen die oben genannten Methoden Speicher, da die Instanzen außerhalb der Klasse einmal erstellt werden (anstatt sie im Konstruktor zu definieren).
  • Wir lecken keine Globals, da wir uns in einem Modul befinden
  • Mit dem gebundenen _privatesObjekt können wir auch private Eigenschaften haben

Einige Nachteile, die mir einfallen:

Ein laufendes Snippet finden Sie hier: http://www.webpackbin.com/NJgI5J8lZ


8

Ja - Sie können gekapselte Eigenschaften erstellen , dies wurde jedoch nicht mit Zugriffsmodifikatoren (öffentlich | privat) durchgeführt, zumindest nicht mit ES6.

Hier ist ein einfaches Beispiel, wie es mit ES6 gemacht werden kann:

1 Erstellen Sie eine Klasse mit class Wort

2 In seinem Konstruktor deklarieren Sie die Variable mit Blockbereich mit let OR const reservierten Wörtern -> da sie Blockbereich sind, kann von außen nicht auf sie zugegriffen werden (gekapselt)

3 Um eine gewisse Zugriffssteuerung (Setter | Getter) auf diese Variablen zu ermöglichen, können Sie die Instanzmethode in ihrem Konstruktor mithilfe der folgenden this.methodName=function(){}Syntax deklarieren

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

Lassen Sie es uns jetzt überprüfen:

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
Dies ist (vorerst) die einzige Lösung für dieses Problem, obwohl alle im Konstruktor deklarierten Methoden für jede Instanz der Klasse neu deklariert werden. Dies ist eine ziemlich schlechte Idee in Bezug auf Leistung und Speichernutzung. Klassenmethoden sollten außerhalb des Konstruktorbereichs deklariert werden.
Freezystem

@Freezystem First: Zuerst sind dies Instanzmethoden (keine Klassenmethoden). Die zweite OP-Frage lautete: _ Wie kann ich den Zugriff auf instance.property verhindern
?

1
Ich habe nicht gesagt, dass Sie sich geirrt haben. Ich habe gesagt, dass Ihre Lösung der beste Kompromiss ist, um eine private Variable zu erreichen, obwohl bei jedem Aufruf eine Kopie jeder Instanzmethode erstellt wird, new Something();da Ihre Methoden im Konstruktor deklariert sind, um Zugriff auf diese zu haben private Variablen. Dies kann zu einem hohen Speicherverbrauch führen, wenn Sie eine große Anzahl von Instanzen Ihrer Klasse erstellen, sodass Leistungsprobleme auftreten. Methoden sollten außerhalb des Konstruktorbereichs deklariert worden sein. Mein Kommentar war eher eine Erklärung Ihrer Lösungsnachteile als eine Kritik.
Freezystem

1
Aber ist es nicht eine schlechte Praxis, Ihre gesamte Klasse im Konstruktor zu definieren? Hacken wir jetzt nicht einfach Javascript? Schauen Sie sich einfach eine andere OOP-Programmiersprache an, und Sie werden sehen, dass der Konstruktor keine Klasse definieren soll.
Kokodoko

1
Ja, das habe ich gemeint, und Ihre Lösung funktioniert! Ich sage nur, dass ich im Allgemeinen überrascht bin, dass ES6 ein 'class'-Schlüsselwort hinzugefügt hat, aber die elegante Lösung der Arbeit mit var und diesem entfernt hat, um eine Kapselung zu erreichen.
Kokodoko

8

Ein anderer Ansatz für "privat"

Anstatt gegen die Tatsache anzukämpfen, dass private Sichtbarkeit in ES6 derzeit nicht verfügbar ist, habe ich mich für einen praktischeren Ansatz entschieden, der gut funktioniert, wenn Ihre IDE JSDoc unterstützt (z. B. Webstorm). Die Idee ist, das @privateTag zu verwenden . In Bezug auf die Entwicklung verhindert die IDE, dass Sie von außerhalb ihrer Klasse auf private Mitglieder zugreifen können. Funktioniert ziemlich gut für mich und war sehr nützlich, um interne Methoden auszublenden. Die Funktion zur automatischen Vervollständigung zeigt mir, was die Klasse wirklich aussetzen wollte. Hier ist ein Beispiel:

Autovervollständigung zeigt nur öffentliche Inhalte


1
Das Problem ist, dass wir nicht über den Editor auf die privaten Variablen zugreifen wollen, sondern die privaten Variablen nicht von außen schützen wollen - und genau das tut public / private. Wenn Ihr Code fertig ist, können Sie von außerhalb der Klasse auf diese Variablen zugreifen (und die wichtigen Überlegungen: überschreiben ). Ihr @privateKommentar kann dies nicht verhindern, es ist nur eine Funktion zur Dokumentationserstellung und Sie sind IDE.
Adrian Preuß

Ja, das ist mir bewusst. Es ist nur so, dass das genug für mich ist und vielleicht auch für andere Leute da draußen. Ich weiß, dass es meine Variablen nicht wirklich privat macht. Es warnt mich nur davor, von außen darauf zuzugreifen (natürlich nur, wenn mein Team und ich alle eine IDE verwenden, die diese Funktion unterstützt). Javascript (und andere Sprachen wie Python) wurden nicht für Zugriffsebenen entwickelt. Die Leute tun alles Mögliche, um diese Funktionalität irgendwie zu implementieren, aber am Ende hacken wir nur die Sprache, um dies zu erreichen. Ich habe mich für einen "natürlicheren" Ansatz entschieden, wenn Sie so wollen.
Lucio Paiva

6

Schwache Karte

  • wird in IE11 unterstützt (Symbole nicht)
  • hart-privat (Requisiten mit Symbolen sind aufgrund von weich-privat Object.getOwnPropertySymbols)
  • kann sehr sauber aussehen (im Gegensatz zu Verschlüssen, die alle Requisiten und Methoden im Konstruktor erfordern)

Definieren Sie zunächst eine Funktion zum Umschließen von WeakMap:

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

Erstellen Sie dann eine Referenz außerhalb Ihrer Klasse:

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

Hinweis: Die Klasse wird von IE11 nicht unterstützt, sieht im Beispiel jedoch sauberer aus.


6

Oh, so viele exotische Lösungen! Ich kümmere mich normalerweise nicht um die Privatsphäre, deshalb verwende ich "Pseudo-Privatsphäre", wie es hier gesagt wird . Aber wenn es mich interessiert (wenn es einige spezielle Anforderungen dafür gibt), verwende ich so etwas wie in diesem Beispiel:

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

Eine weitere mögliche Implementierung der Funktion (Konstruktor) Job:

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

5

Persönlich gefällt mir der Vorschlag des Bindungsoperators :: und ich würde ihn dann mit der erwähnten Lösung @ d13 kombinieren, aber vorerst bei der Antwort von @ d13 bleiben, bei der Sie das exportSchlüsselwort für Ihre Klasse verwenden und die privaten Funktionen in das Modul einfügen .

Es gibt eine weitere schwierige Lösung, die hier nicht erwähnt wurde. Die folgenden sind funktionaler und ermöglichen es ihr, alle privaten Requisiten / Methoden innerhalb der Klasse zu haben.

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

Kommentare dazu wären willkommen.


Generell gefällt mir der Ansatz. Feedback: 1. Sie benötigen für jede Klasse ein anderes private.js-Modul, um Konflikte zu vermeiden. 2. Ich mag das Potenzial nicht, den Konstruktor wirklich lang zu machen, indem ich jede Ihrer privaten Methoden inline definiere. 3. Es wäre schön, wenn alle Klassenmethoden in einer Datei wären.
Doug Coburn

5

Ich bin auf diesen Beitrag gestoßen, als ich nach der besten Vorgehensweise für "private Daten für Klassen" gesucht habe. Es wurde erwähnt, dass einige der Muster Leistungsprobleme aufweisen würden.

Ich habe einige jsperf-Tests zusammengestellt, die auf den 4 Hauptmustern aus dem Online-Buch "Exploring ES6" basieren:

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

Die Tests finden Sie hier:

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

In Chrome 63.0.3239 / Mac OS X 10.11.6 waren die Muster mit der besten Leistung "Private Daten über Konstruktorumgebungen" und "Private Daten über eine Namenskonvention". Für mich hat Safari eine gute Leistung für WeakMap erbracht, Chrome jedoch nicht so gut.

Ich kenne die Auswirkungen auf den Speicher nicht, aber das Muster für "Konstruktorumgebungen", von dem einige gewarnt hatten, dass es sich um ein Leistungsproblem handelt, war sehr leistungsfähig.

Die 4 Grundmuster sind:

Private Daten über Konstruktorumgebungen

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

Private Daten über Konstruktorumgebungen 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();

Private Daten über eine Namenskonvention

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

Private Daten über 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();

Private Daten über Symbole

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

Ich glaube, es ist möglich, mit Verschlüssen in Konstruktoren das Beste aus beiden Welten herauszuholen. Es gibt zwei Variationen:

Alle Datenmitglieder sind privat

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

Einige Mitglieder sind privat

HINWEIS: Dies ist zugegebenermaßen hässlich. Wenn Sie eine bessere Lösung kennen, bearbeiten Sie diese Antwort.

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

Tatsächlich ist es möglich, Symbole und Proxies zu verwenden. Sie verwenden die Symbole im Klassenbereich und legen zwei Traps in einem Proxy fest: einen für den Klassenprototyp, damit die Symbole Reflect.ownKeys (Instanz) oder Object.getOwnPropertySymbols Ihre Symbole nicht verraten, der andere für den Konstruktor selbst Wenn new ClassName(attrs)also aufgerufen wird, wird die zurückgegebene Instanz abgefangen und die eigenen Eigenschaftssymbole blockiert. Hier ist der 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 });
}

Reflect.ownKeys()funktioniert so: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))Deshalb brauchen wir eine Falle für diese Objekte.


Danke, ich werde Symbole ausprobieren :) Aus all den obigen Antworten scheint es der einfachste Weg zu sein, ein unzugängliches Klassenmitglied zu erstellen :)
Kokodoko

4

Selbst Typescript kann das nicht. Aus ihrer Dokumentation :

Wenn ein Mitglied als privat markiert ist, kann von außerhalb seiner enthaltenden Klasse nicht auf es zugegriffen werden. Zum Beispiel:

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

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

Aber auf ihrem Spielplatz transpiliert gibt dies:

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

Ihr "privates" Schlüsselwort ist also unwirksam.


2
Nun, es ist immer noch effektiv, weil es "schlechte" Programmierung in der IDE verhindert. Es zeigt Ihnen, welche Mitglieder Sie verwenden sollten und welche nicht. Ich denke, das ist der Hauptgrund für die Verwendung von privat und öffentlich. (Wenn Sie beispielsweise C # zu Maschinencode kompilieren, ist privat immer noch privat? Wer weiß?). Beim Lesen der anderen Antworten scheint es, dass die Verwendung von @Symbol auch dazu führen kann, dass ein Mitglied nicht mehr erreichbar ist. Aber auch Symbole können noch von der Konsole aus gefunden werden.
Kokodoko

Tritt der TypeScript-Fehler beim Transpilen von TypeScript in JavaScript auf? (Wie die Typprüfung zur Übergangszeit stattfindet. Anstelle eines privaten Laufzeitmechanismus.)
Eljay

4

Ich komme sehr spät zu dieser Party, aber ich habe die OP-Frage bei einer Suche getroffen, also ... Ja, Sie können private Eigenschaften haben, indem Sie die Klassendeklaration in einen Abschluss einschließen

Es gibt ein Beispiel dafür, wie ich private Methoden in diesem Codepen habe . Im folgenden Snippet verfügt die Subscribeable-Klasse über zwei 'private' Funktionen processund processCallbacks. Alle Eigenschaften können auf diese Weise hinzugefügt werden und werden durch die Verwendung des Verschlusses privat gehalten. IMO-Datenschutz ist eine seltene Notwendigkeit, wenn Bedenken gut getrennt sind und Javascript nicht durch Hinzufügen weiterer Syntax aufgebläht werden muss, wenn ein Abschluss die Aufgabe ordnungsgemäß 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 Bedenken gut voneinander trennt und die Dinge wirklich privat hält. Der einzige Nachteil ist die Notwendigkeit, "Selbst" (oder etwas Ähnliches) zu verwenden, um im privaten Inhalt auf "Dies" zu verweisen.


4

Ich denke, Benjamins Antwort ist wahrscheinlich die beste für die meisten Fälle, bis die Sprache nativ explizit private Variablen unterstützt.

Wenn Sie jedoch aus irgendeinem Grund den Zugriff mit verhindern müssen Object.getOwnPropertySymbols(), ist eine Methode, die ich in Betracht gezogen habe, das Anhängen einer eindeutigen, nicht konfigurierbaren, nicht aufzählbaren, nicht beschreibbaren Eigenschaft, die als Eigenschaftskennung für jedes Objekt bei der Erstellung verwendet werden kann (z. B. eine Symboleindeutige Eigenschaft, wenn Sie noch keine andere eindeutige Eigenschaft wie eine haben id). Behalten Sie dann einfach eine Karte der "privaten" Variablen jedes Objekts unter Verwendung dieser Kennung.

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"

Der potenzielle Vorteil dieses Ansatzes gegenüber der Verwendung von a WeakMapist eine schnellere Zugriffszeit, wenn die Leistung zu einem Problem wird.


1
Korrigieren Sie mich, wenn ich falsch liege, aber würde dieser Code nicht Speicherlecks enthalten, da privateVars die privaten Variablen eines Objekts auch dann speichert, wenn das Objekt bereits zerstört ist?
Russell Santos

@RussellSantos Sie sind richtig, vorausgesetzt, die Objekte müssen irgendwann Müll gesammelt werden. Vielen Dank für den Hinweis. In meinem Beispiel habe ich eine destroy()Methode hinzugefügt , die vom using-Code aufgerufen werden sollte, wenn ein Objekt entfernt werden muss.
NanoWizard

4

Ja, das kann man und das auch ganz einfach. Dazu werden Ihre privaten Variablen und Funktionen verfügbar gemacht, indem das Prototyp-Objektdiagramm im Konstruktor zurückgegeben wird. Das ist nichts Neues, aber nehmen Sie sich ein bisschen Zeit, um die Eleganz zu verstehen. Auf diese Weise werden keine globalen Gültigkeitsbereiche oder Schwachkarten verwendet. Es ist eine in die Sprache eingebaute Form der Reflexion. Abhängig davon, wie Sie dies nutzen. Man kann entweder eine Ausnahme erzwingen, die den Aufrufstapel unterbricht, oder die Ausnahme als begraben undefined. Dies wird unten demonstriert und kann hier mehr über diese Funktionen gelesen werden

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
Es ist am besten, nur Code-Antworten zu vermeiden. Es wäre besser, wenn Sie erklären könnten, wie Ihr Code die Frage des OP beantwortet
Stewart_R

Auf diese Weise kann eine schreibgeschützte Variable mehr als eine private Variable erstellt werden. Eine private Variable sollte nicht nach außen zugänglich sein. console.log(instance.property)sollte werfen oder dir undefiniert geben, nicht zurück geben "Test".
oooyaya

3

Ein anderer Weg ähnlich den letzten beiden geposteten

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

Die meisten Antworten sagen entweder, dass dies unmöglich ist, oder Sie müssen eine WeakMap oder ein Symbol verwenden. Hierbei handelt es sich um ES6-Funktionen, für die wahrscheinlich Polyfüllungen erforderlich wären. Es gibt jedoch einen anderen Weg! Schauen Sie sich das an:

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

Ich nenne diese Methode Accessor-Muster . Die wesentliche Idee ist, dass wir einen Abschluss haben , einen Schlüssel innerhalb des Abschlusses, und wir erstellen ein privates Objekt (im Konstruktor), auf das nur zugegriffen werden kann, wenn Sie den Schlüssel haben .

Wenn Sie interessiert sind, können Sie mehr darüber in meinem Artikel lesen . Mit dieser Methode können Sie Objekteigenschaften erstellen, auf die außerhalb des Abschlusses nicht zugegriffen werden kann. Daher können Sie sie im Konstruktor oder Prototyp verwenden, aber nirgendwo anders. Ich habe diese Methode nirgendwo gesehen, aber ich denke, sie ist wirklich mächtig.


Die Frage war, wie dies in ES6-Klassen erreicht werden kann.
Michael Franzl

Sie können in ES6-Klassen genau dieselbe Methode verwenden. ES6-Klassen sind hauptsächlich Zucker neben Funktionen, wie ich sie in meinem Beispiel vorgestellt habe. Es ist durchaus möglich, dass das Originalposter einen Transpiler verwendet. In diesem Fall sind für WeakMaps oder Symbole weiterhin Polyfills erforderlich. Meine Antwort ist trotzdem gültig.
Guitarino


2

Ich habe eine sehr einfache Lösung gefunden, benutze sie einfach Object.freeze(). Das Problem ist natürlich, dass Sie dem Objekt später nichts mehr 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 // doesn't work, even throws an error in strict mode

Dies wird auch Setter-Methode wiesetName(name) { this.name = name; }
Ngakak

2

Ich benutze dieses Muster und es hat immer für mich funktioniert

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

Eigentlich ist es ist möglich.
1. Erstellen Sie zunächst die Klasse und geben Sie im Konstruktor die aufgerufene _publicFunktion zurück.
2. Übergeben Sie in der aufgerufenen _publicFunktion die thisReferenz (um den Zugriff auf alle privaten Methoden und Requisiten zu erhalten) und alle Argumente von constructor (die übergeben werden new Names()).
3. Im _publicFunktionsumfang befindet sich auch die NamesKlasse mit dem Zugriff auf this(_this ) Bezug der privaten NamesKlasse

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

Sie können dies unter https://www.npmjs.com/package/private-members versuchen

Dieses Paket speichert die Mitglieder nach Instanz.

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;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.