Gibt es eine Möglichkeit, "private" Variablen (die im Konstruktor definierten) für prototypdefinierte Methoden verfügbar zu machen?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
Das funktioniert:
t.nonProtoHello()
Das geht aber nicht:
t.prototypeHello()
Ich bin es gewohnt, meine Methoden im Konstruktor zu definieren, aber ich entferne mich aus mehreren Gründen davon.
javascript
private-members
Morgancodes
quelle
quelle
Antworten:
Nein, das gibt es nicht. Das wäre im Wesentlichen umgekehrt.
Im Konstruktor definierte Methoden haben Zugriff auf private Variablen, da alle Funktionen Zugriff auf den Bereich haben, in dem sie definiert wurden.
Auf einem Prototyp definierte Methoden werden nicht im Bereich des Konstruktors definiert und haben keinen Zugriff auf die lokalen Variablen des Konstruktors.
Sie können immer noch private Variablen haben, aber wenn Sie Methoden auf dem Prototyp definiert wollen den Zugang zu ihnen haben, sollten Sie Getter und Setter auf der Definition
this
Objekt, das die Prototyp - Methoden (zusammen mit allem anderen) wird Zugang haben. Beispielsweise:quelle
secret
eine Eigenschaft von machenthis
. JavaScript unterstützt private Variablen mit Prototypen einfach nicht, da Prototypen an den Call-Site-Kontext gebunden sind, nicht an den 'Creation-Site'-Kontext.person.getSecret()
dann?Update: Mit ES6 gibt es einen besseren Weg:
Kurz gesagt, Sie können das Neue verwenden
Symbol
, um private Felder zu erstellen.Hier ist eine großartige Beschreibung: https://curiosity-driven.org/private-properties-in-javascript
Beispiel:
Für alle modernen Browser mit ES5:
Sie können nur Verschlüsse verwenden
Der einfachste Weg, Objekte zu konstruieren, besteht darin, die prototypische Vererbung insgesamt zu vermeiden. Definieren Sie einfach die privaten Variablen und öffentlichen Funktionen innerhalb des Abschlusses, und alle öffentlichen Methoden haben privaten Zugriff auf die Variablen.
Oder Sie können nur Prototypen verwenden
In JavaScript ist die prototypische Vererbung in erster Linie eine Optimierung . Es ermöglicht mehreren Instanzen, Prototypmethoden gemeinsam zu nutzen, anstatt dass jede Instanz ihre eigenen Methoden hat.
Der Nachteil ist, dass dies
this
das einzige ist, was sich jedes Mal unterscheidet, wenn eine prototypische Funktion aufgerufen wird.Daher müssen alle privaten Felder über zugänglich sein
this
, was bedeutet, dass sie öffentlich sind. Wir halten uns also nur an Namenskonventionen für_private
Felder.Mischen Sie keine Verschlüsse mit Prototypen
Ich denke, Sie sollten Abschlussvariablen nicht mit Prototypmethoden mischen. Sie sollten das eine oder andere verwenden.
Wenn Sie einen Abschluss verwenden, um auf eine private Variable zuzugreifen, können Prototypmethoden nicht auf die Variable zugreifen. Sie müssen also den Verschluss freigeben
this
, was bedeutet, dass Sie ihn auf die eine oder andere Weise öffentlich belichten. Mit diesem Ansatz gibt es sehr wenig zu gewinnen.Welches wähle ich?
Verwenden Sie für wirklich einfache Objekte einfach ein einfaches Objekt mit Verschlüssen.
Wenn Sie eine prototypische Vererbung benötigen - für Vererbung, Leistung usw. -, halten Sie sich an die Namenskonvention "_private" und kümmern Sie sich nicht um Abschlüsse.
Ich verstehe nicht, warum JS-Entwickler sich so sehr bemühen, Felder wirklich privat zu machen.
quelle
_private
Namenskonvention immer noch die beste Lösung, wenn Sie die prototypische Vererbung nutzen möchten.Symbol
, das eine hervorragende Möglichkeit ist, private Felder zu erstellen. Hier ist eine gute Erklärung: curiosity-driven.org/private-properties-in-javascriptSymbol
in einem Verschluss aufbewahren, der Ihre gesamte Klasse umfasst. Auf diese Weise können alle Prototypmethoden das Symbol verwenden, es wird jedoch niemals außerhalb der Klasse verfügbar gemacht.Object.getOwnPropertySymbols
. Das ist also nur Privatsphäre durch Dunkelheit.toString
. Dies unterscheidet sich nicht von Java oder C # ... private Mitglieder sind weiterhin über Reflection erreichbar, werden jedoch normalerweise stark verdeckt. Das alles stärkt meinen letzten Punkt: "Ich verstehe nicht, warum JS-Entwickler sich so sehr bemühen, Felder wirklich privat zu machen."Als ich das las, klang es wie eine schwierige Herausforderung, also beschloss ich, einen Weg zu finden. Was ich mir ausgedacht habe, war CRAAAAZY , aber es funktioniert total.
Zuerst habe ich versucht, die Klasse in einer Sofortfunktion zu definieren, damit Sie auf einige der privaten Eigenschaften dieser Funktion zugreifen können. Dies funktioniert und ermöglicht es Ihnen, einige private Daten abzurufen. Wenn Sie jedoch versuchen, die privaten Daten festzulegen, werden Sie bald feststellen, dass alle Objekte denselben Wert haben.
Es gibt viele Fälle, in denen dies angemessen wäre, wenn Sie konstante Werte wie Ereignisnamen haben möchten, die von Instanzen gemeinsam genutzt werden. Im Wesentlichen verhalten sie sich jedoch wie private statische Variablen.
Wenn Sie innerhalb Ihrer im Prototyp definierten Methoden unbedingt Zugriff auf Variablen in einem privaten Namespace benötigen, können Sie dieses Muster ausprobieren.
Ich würde mich über Feedback von jedem freuen, der einen Fehler bei dieser Vorgehensweise sieht.
quelle
i
dies allen Instanzen hinzugefügt wurde. Es ist also nicht vollständig "transparent" undi
könnte dennoch manipuliert werden.siehe Doug Crockfords Seite dazu . Sie müssen dies indirekt mit etwas tun, das auf den Bereich der privaten Variablen zugreifen kann.
ein anderes Beispiel:
Anwendungsfall:
quelle
_set
über zu belichtenset
? Warumset
nennst du es nicht gleich ?Ich schlage vor, es wäre wahrscheinlich eine gute Idee, "eine Prototypzuweisung in einem Konstruktor zu haben" als ein Javascript-Anti-Pattern zu beschreiben. Denk darüber nach. Es ist viel zu riskant.
Was Sie dort beim Erstellen des zweiten Objekts (dh b) tatsächlich tun, ist die Neudefinition dieser Prototypfunktion für alle Objekte, die diesen Prototyp verwenden. Dadurch wird der Wert für Objekt a in Ihrem Beispiel effektiv zurückgesetzt. Es funktioniert, wenn Sie eine gemeinsam genutzte Variable möchten und zufällig alle Objektinstanzen im Voraus erstellen, aber es fühlt sich viel zu riskant an.
Ich habe in einem Javascript, an dem ich kürzlich gearbeitet habe, einen Fehler gefunden, der genau auf dieses Anti-Muster zurückzuführen ist. Es wurde versucht, einen Drag & Drop-Handler für das zu erstellende Objekt festzulegen, dies wurde jedoch für alle Instanzen durchgeführt. Nicht gut.
Doug Crockfords Lösung ist die beste.
quelle
@ Kai
Das wird nicht funktionieren. Wenn Sie tun
Dann
t2.prototypeHello
wird auf den privaten Bereich von t zugegriffen.@AnglesCrimes
Der Beispielcode funktioniert einwandfrei, erstellt jedoch tatsächlich ein "statisches" privates Mitglied, das von allen Instanzen gemeinsam genutzt wird. Es ist möglicherweise nicht die Lösung, nach der Morgancodes gesucht haben.
Bisher habe ich keinen einfachen und sauberen Weg gefunden, dies zu tun, ohne einen privaten Hash und zusätzliche Bereinigungsfunktionen einzuführen. Eine private Mitgliedsfunktion kann bis zu einem gewissen Grad simuliert werden:
quelle
privateFoo
ist völlig privat und somit unsichtbar, wenn man eine bekommtnew Foo()
. Nur hierbar()
gibt es eine öffentliche Methode, auf die zugegriffen werden kannprivateFoo
. Sie können denselben Mechanismus für einfache Variablen und Objekte verwenden. Beachten Sie jedoch immer, dass dieseprivates
tatsächlich statisch sind und von allen von Ihnen erstellten Objekten gemeinsam genutzt werden.Ja es ist möglich. Das PPF-Designmuster löst dieses Problem.
PPF steht für Private Prototype Functions. Basic PPF löst diese Probleme:
Zum ersten nur:
So einfach ist das. Beispielsweise:
...
Lesen Sie die ganze Geschichte hier:
PPF-Entwurfsmuster
quelle
Sie können dies tatsächlich mithilfe der Accessor-Überprüfung erreichen :
Dieses Beispiel stammt aus meinem Beitrag über prototypische Funktionen und private Daten und wird dort ausführlicher erläutert.
quelle
In aktuellen JavaScript, ich bin ziemlich sicher , dass es ein und nur ein Weg , um privat Zustand , zugänglich von Prototyp - Funktionen, ohne etwas hinzuzufügen Öffentlichkeit zu
this
. Die Antwort ist, das Muster "schwache Karte" zu verwenden.Um es zusammenzufassen: Die
Person
Klasse hat eine einzige schwache Zuordnung, wobei die Schlüssel die Instanzen von Person sind und die Werte einfache Objekte sind, die für die private Speicherung verwendet werden.Hier ist ein voll funktionsfähiges Beispiel: (Spielen Sie unter http://jsfiddle.net/ScottRippey/BLNVr/ )
Wie gesagt, dies ist wirklich der einzige Weg, um alle 3 Teile zu erreichen.
Es gibt jedoch zwei Einschränkungen. Erstens kostet dies die Leistung - jedes Mal, wenn Sie auf die privaten Daten zugreifen, handelt es sich um einen
O(n)
Vorgang, bei demn
die Anzahl der Instanzen angegeben wird. Sie möchten dies also nicht tun, wenn Sie eine große Anzahl von Instanzen haben. Zweitens müssen Sie aufrufen, wenn Sie mit einer Instanz fertig sinddestroy
. Andernfalls werden die Instanz und die Daten nicht durch Müll gesammelt, und es kommt zu einem Speicherverlust.Und deshalb möchte ich mich an meine ursprüngliche Antwort "Du solltest nicht" halten.
quelle
Es gibt einen einfacheren Weg, indem Sie die Verwendung von
bind
undcall
Methoden nutzen.Durch Festlegen privater Variablen für ein Objekt können Sie den Bereich dieses Objekts nutzen.
Beispiel
Diese Methode ist nicht ohne Nachteile. Da der Bereichskontext effektiv überschrieben wird, haben Sie keinen Zugriff außerhalb des
_private
Objekts. Es ist jedoch nicht unmöglich, weiterhin Zugriff auf den Bereich des Instanzobjekts zu gewähren. Sie können den Kontext (this
) des Objekts als zweites Argument an die öffentlichen Werte in der Prototypfunktion übergebenbind
odercall
weiterhin darauf zugreifen.Zugang zu öffentlichen Werten
quelle
Versuch es!
quelle
caller
hängt von einer implementierungsabhängigen Erweiterung ab, die im strengen Modus nicht zulässig ist.Folgendes habe ich mir ausgedacht.
Das Hauptproblem bei dieser Implementierung besteht darin, dass die Prototypen bei jeder Instanziierung neu definiert werden.
quelle
Es gibt einen sehr einfachen Weg, dies zu tun
JavaScript-Prototypen sind golden.
quelle
SharedPrivate.prototype
dathis.constructor.prototype
es keine große Sache ist, getP und setP mehrmals neu zu definieren ...Ich bin zu spät zur Party, aber ich denke, ich kann dazu beitragen. Hier, sehen 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
Können Sie die Variablen nicht in einen höheren Bereich stellen?
quelle
Sie können auch versuchen, eine Methode nicht direkt für den Prototyp, sondern für die Konstruktorfunktion wie folgt hinzuzufügen:
quelle
Hier ist etwas, das ich mir ausgedacht habe, als ich versucht habe, die einfachste Lösung für dieses Problem zu finden. Vielleicht könnte es für jemanden nützlich sein. Ich bin neu in Javascript, daher kann es durchaus zu Problemen mit dem Code kommen.
quelle
Ich hatte heute genau die gleiche Frage und nachdem ich die erstklassige Antwort von Scott Rippey ausgearbeitet hatte, kam ich auf eine sehr einfache Lösung (IMHO), die sowohl mit ES5 kompatibel als auch effizient ist. Sie ist auch sicher gegen Namenskonflikte (die Verwendung von _private scheint unsicher). .
Getestet mit Ringojs und Nodejs. Ich bin gespannt auf Ihre Meinung.
quelle
Wie ist das? Verwenden eines privaten Accessors. Je nach Anwendungsfall können Sie die Variablen nur abrufen, aber nicht festlegen.
quelle
Ich habe eine Lösung, bin mir aber nicht sicher, ob sie fehlerfrei ist.
Damit es funktioniert, müssen Sie die folgende Struktur verwenden:
Hier ist der Code:
Dies funktioniert so, dass eine Instanzfunktion "this.getPrivateFields" für den Zugriff auf das private Variablenobjekt "privateFields" bereitgestellt wird. Diese Funktion gibt jedoch nur das Objekt "privateFields" innerhalb des definierten Hauptabschlusses zurück (auch Prototypfunktionen, die "this.getPrivateFields" verwenden "müssen innerhalb dieses Verschlusses definiert werden).
Ein zur Laufzeit erzeugter und schwer zu erratender Hash wird als Parameter verwendet, um sicherzustellen, dass das Objekt "privateFields" auch dann nicht zurückgegeben wird, wenn "getPrivateFields" außerhalb des Schließungsbereichs aufgerufen wird.
Der Nachteil ist, dass wir TestClass nicht mit mehr Prototypfunktionen außerhalb des Verschlusses erweitern können.
Hier ist ein Testcode:
BEARBEITEN: Mit dieser Methode ist es auch möglich, private Funktionen zu "definieren".
quelle
Ich habe heute damit herumgespielt und dies war die einzige Lösung, die ich finden konnte, ohne Symbole zu verwenden. Das Beste daran ist, dass eigentlich alles ganz privat sein kann.
Die Lösung basiert auf einem selbst entwickelten Modullader, der im Grunde genommen zum Vermittler für einen privaten Speichercache wird (unter Verwendung einer schwachen Karte).
quelle
Ich weiß, dass es mehr als ein Jahrzehnt her ist, seit dies gefragt wurde, aber ich habe gerade zum n-ten Mal in meinem Programmiererleben darüber nachgedacht und eine mögliche Lösung gefunden, die ich noch nicht ganz mag . Ich habe diese Methode noch nicht dokumentiert gesehen, daher werde ich sie als "privates / öffentliches Dollar-Muster" oder _ $ / $ -Muster bezeichnen .
Das Konzept verwendet eine ClassDefinition- Funktion, die eine Konstruktorfunktion zurückgibt, die ein Interface- Objekt zurückgibt . Die einzige Methode
$
der Schnittstelle besteht darin , einname
Argument zu empfangen , um die entsprechende Funktion im Konstruktorobjekt aufzurufen, und alle zusätzlichen Argumente, die danach übergeben werdenname
werden, werden im Aufruf übergeben.Die global definierte Hilfsfunktion
ClassValues
speichert alle Felder in einem Objekt nach Bedarf. Es definiert die_$
Funktion, über die auf sie zugegriffen werden sollname
. Dies folgt einem kurzen get / set-Muster. Wennvalue
es übergeben wird, wird es als neuer Variablenwert verwendet.Die global definierte Funktion verwendet
Interface
ein Objekt und einValues
Objekt, um eine_interface
Funktion mit einer einzigen Funktion zurückzugeben$
, die untersuchtobj
, ob eine nach dem Parameter benannte Funktion gefunden wurde ,name
und sievalues
als Objekt mit Gültigkeitsbereich aufruft . Die an übergebenen zusätzlichen Argumente$
werden beim Funktionsaufruf übergeben.Im folgenden Beispiel
ClassX
wird das Ergebnis von zugeordnetClassDefinition
, welches dieConstructor
Funktion ist.Constructor
kann eine beliebige Anzahl von Argumenten erhalten.Interface
ist, was externer Code nach dem Aufrufen des Konstruktors erhält.Es macht keinen Sinn, nicht prototypisierte Funktionen zu haben
Constructor
, obwohl Sie sie im Konstruktorfunktionskörper definieren könnten. Alle Funktionen werden mit dem öffentlichen Dollar-Muster aufgerufenthis.$("functionName"[, param1[, param2 ...]])
. Auf die privaten Werte wird mit dem privaten Dollarmuster zugegriffenthis._$("valueName"[, replacingValue]);
. DaInterface
es keine Definition für gibt_$
, können externe Objekte nicht auf die Werte zugreifen. Da jeder Prototyp eines Funktionskörpersthis
auf dasvalues
Objekt in der Funktion festgelegt ist$
, erhalten Sie Ausnahmen, wenn Sie Constructor-Geschwisterfunktionen direkt aufrufen. Das _ $ / $ -Muster muss auch in prototypisierten Funktionskörpern befolgt werden. Unten Beispielverwendung.Und die Konsolenausgabe.
Das _ $ / $ -Muster ermöglicht die vollständige Vertraulichkeit von Werten in Klassen mit vollständigem Prototyp. Ich weiß nicht, ob ich das jemals benutzen werde oder ob es Fehler hat, aber hey, es war ein gutes Rätsel!
quelle
ES6 WeakMaps
Durch die Verwendung eines einfachen Musters in ES6 WeakMaps können private Mitgliedsvariablen abgerufen werden, die über die Prototypfunktionen erreichbar sind .
Eine detailliertere Erklärung dieses Musters finden Sie hier
quelle
Sie müssen 3 Dinge in Ihrem Code ändern:
var privateField = "hello"
durchthis.privateField = "hello"
.privateField
durchthis.privateField
.privateField
mitthis.privateField
.Der endgültige Code wäre der folgende:
quelle
this.privateField
wäre kein privates Feld. es ist von außen zugänglich:t.privateField
Sie können eine Prototypzuweisung innerhalb der Konstruktordefinition verwenden.
Die Variable ist für die vom Prototyp hinzugefügte Methode sichtbar, aber alle Instanzen der Funktionen greifen auf dieselbe SHARED-Variable zu.
Ich hoffe das kann nützlich sein.
quelle