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"
Antworten:
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.
quelle
this
im Konstruktor verwenden, bevor Sie aufrufensuper()
. Doch Babel stellt sie vor Super.#privateCrap
Syntax zuzulassen ?#beep() {}
; und das :async #bzzzt() {}
?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
ES5
quelle
class
Syntax in erster Linie erheblich untergräbt .getName
undsetName
Eigenschaften privat?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:
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 sind
this
. 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:
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
,WeakMap
ist jedoch besser, da dadurchMap
Speicherlecks 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 speichern
this[mySymbol]
.Die Privatsphäre dieser Methode kann mit verletzt werden
Object.getOwnPropertySymbols
, ist aber etwas umständlich zu tun.Beispiel:
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:
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.
quelle
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.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
Symbol
s 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:
quelle
Object.getOwnPropertySymbols
? ;)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.private
undprotected
wäre so viel sauberer als SchlüsselwörterSymbol
oderName
. Ich bevorzuge die Punktnotation gegenüber der Klammernotation. Ich würde gerne weiterhin einen Punkt für private Dinge verwenden.this.privateVar
Die Antwort ist nein". Sie können jedoch einen privaten Zugriff auf Eigenschaften wie diese erstellen:
export
Schlüsselwort veröffentlicht.(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 )
quelle
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.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
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
.set
und.get
explizit verwenden, anstatt sich auf den überschreibbaren Prototyp zu verlassen.quelle
get
auf eine pro Methode reduzieren (zconst _ = privates.get(this); console.log(_.privateProp1);
. B. ).const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"
bedeutet das, dass Ihre Immobilie privat ist oder nicht?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:
quelle
Kommt drauf an wen du fragst :-)
Der Vorschlag für maximal minimale Klassen enthält keinen
private
Eigenschaftsmodifikator, 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.
quelle
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
Dann kann der konsumierende Code folgendermaßen aussehen:
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:
quelle
private static
.a.say(); // a
seinb.say(); // b
let _message = null
Weg, nicht so cool, wenn Konstruktor mehrmals aufrufen, es vermasselt.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
.bind
mit dem aktuellen Objekt plus einem_privates
Objekt als Curry-Parameter:etwas.js
main.js
Vorteile, die mir einfallen:
_greet
und uns_updateMessage
wie private Methoden verhalten, solange wir nichtexport
die Referenzen haben)_privates
Objekt können wir auch private Eigenschaften habenEinige Nachteile, die mir einfallen:
Ein laufendes Snippet finden Sie hier: http://www.webpackbin.com/NJgI5J8lZ
quelle
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 deklarierenLassen Sie es uns jetzt überprüfen:
quelle
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.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
@private
Tag 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:quelle
@private
Kommentar kann dies nicht verhindern, es ist nur eine Funktion zur Dokumentationserstellung und Sie sind IDE.Schwache Karte
Object.getOwnPropertySymbols
)Definieren Sie zunächst eine Funktion zum Umschließen von WeakMap:
Erstellen Sie dann eine Referenz außerhalb Ihrer Klasse:
Hinweis: Die Klasse wird von IE11 nicht unterstützt, sieht im Beispiel jedoch sauberer aus.
quelle
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:
Eine weitere mögliche Implementierung der Funktion (Konstruktor)
Job
:quelle
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 dasexport
Schlü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
Test.js
Kommentare dazu wären willkommen.
quelle
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
Private Daten über Konstruktorumgebungen 2
Private Daten über eine Namenskonvention
Private Daten über WeakMaps
Private Daten über Symbole
quelle
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
Einige Mitglieder sind privat
HINWEIS: Dies ist zugegebenermaßen hässlich. Wenn Sie eine bessere Lösung kennen, bearbeiten Sie diese Antwort.
quelle
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:Reflect.ownKeys()
funktioniert so:Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))
Deshalb brauchen wir eine Falle für diese Objekte.quelle
Selbst Typescript kann das nicht. Aus ihrer Dokumentation :
Aber auf ihrem Spielplatz transpiliert gibt dies:
Ihr "privates" Schlüsselwort ist also unwirksam.
quelle
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
process
undprocessCallbacks
. 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.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.
quelle
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. eineSymbol
eindeutige Eigenschaft, wenn Sie noch keine andere eindeutige Eigenschaft wie eine habenid
). Behalten Sie dann einfach eine Karte der "privaten" Variablen jedes Objekts unter Verwendung dieser Kennung.Der potenzielle Vorteil dieses Ansatzes gegenüber der Verwendung von a
WeakMap
ist eine schnellere Zugriffszeit, wenn die Leistung zu einem Problem wird.quelle
destroy()
Methode hinzugefügt , die vom using-Code aufgerufen werden sollte, wenn ein Objekt entfernt werden muss.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 werdenquelle
quelle
console.log(instance.property)
sollte werfen oder dir undefiniert geben, nicht zurück geben "Test".Ein anderer Weg ähnlich den letzten beiden geposteten
quelle
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:
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.
quelle
In dieser Antwort finden Sie eine saubere und einfache "Klassen" -Lösung mit einer privaten und öffentlichen Schnittstelle und Unterstützung für die Komposition
quelle
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.quelle
setName(name) { this.name = name; }
Ich benutze dieses Muster und es hat immer für mich funktioniert
quelle
Eigentlich ist es ist möglich.
1. Erstellen Sie zunächst die Klasse und geben Sie im Konstruktor die aufgerufene
_public
Funktion zurück.2. Übergeben Sie in der aufgerufenen
_public
Funktion diethis
Referenz (um den Zugriff auf alle privaten Methoden und Requisiten zu erhalten) und alle Argumente vonconstructor
(die übergeben werdennew Names()
).3. Im
_public
Funktionsumfang befindet sich auch dieNames
Klasse mit dem Zugriff aufthis
(_this ) Bezug der privatenNames
Klassequelle
Sie können dies unter https://www.npmjs.com/package/private-members versuchen
Dieses Paket speichert die Mitglieder nach Instanz.
quelle