Angenommen, ich habe ein Objekt foo
in meinem JavaScript-Code. foo
ist ein komplexes Objekt und wird woanders erzeugt. Wie kann ich den Prototyp des foo
Objekts ändern ?
Meine Motivation besteht darin, geeignete Prototypen für Objekte festzulegen, die von .NET in JavaScript-Literale serialisiert wurden.
Angenommen, ich habe den folgenden JavaScript-Code in eine ASP.NET-Seite geschrieben.
var foo = <%=MyData %>;
Angenommen, dies MyData
ist das Ergebnis des Aufrufs von .NET JavaScriptSerializer
für ein Dictionary<string,string>
Objekt.
Zur Laufzeit wird dies wie folgt:
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
Wie Sie sehen können, foo
wird eine Reihe von Objekten. Ich möchte foo
mit einem geeigneten Prototyp initialisieren können . Ich möchte das noch nicht ändern . Wie kann ich das machen?Object.prototype
Array.prototype
quelle
extend
oder Googlegoog.inherit
? Viele Entwickler bieten Möglichkeiten zum Erstellen von Vererbungen an, bevor sie den älterennew
Konstruktor aufrufen. DiesObject.create
geschah, bevor uns dies mitgeteilt wurde, und wir mussten uns nicht um das Überschreiben kümmernObject.prototype
.Antworten:
EDIT Feb. 2012: Die Antwort unten ist nicht mehr korrekt. __proto__ wird zu ECMAScript 6 als "normativ optional" hinzugefügt, was bedeutet, dass es nicht implementiert werden muss, aber wenn dies der Fall ist, muss es den vorgegebenen Regeln folgen. Dies ist derzeit ungelöst, wird aber zumindest offiziell Teil der JavaScript-Spezifikation sein.
Diese Frage ist viel komplizierter, als es auf den ersten Blick scheint, und übersteigt die Gehaltsstufe der meisten Menschen in Bezug auf das Wissen über Javascript-Interna.
Die
prototype
Eigenschaft eines Objekts wird verwendet, wenn neue untergeordnete Objekte dieses Objekts erstellt werden. Das Ändern spiegelt sich nicht im Objekt selbst wider, sondern wird reflektiert, wenn dieses Objekt als Konstruktor für andere Objekte verwendet wird, und hat keinen Sinn beim Ändern des Prototyps eines vorhandenen Objekts.Objekte haben eine interne Eigenschaft, die auf den aktuellen Prototyp verweist. Wenn eine Eigenschaft für ein Objekt aufgerufen wird, beginnt sie am Objekt und durchläuft dann die [[Prototyp]] - Kette, bis sie nach dem Stammobjekt-Prototyp eine Übereinstimmung findet oder fehlschlägt. Auf diese Weise ermöglicht Javascript das Erstellen und Ändern von Objekten zur Laufzeit. Es hat einen Plan für die Suche nach dem, was es braucht.
Die
__proto__
Eigenschaft ist in einigen Implementierungen vorhanden (viel jetzt): in jeder Mozilla-Implementierung, in allen mir bekannten Webkit-Implementierungen und in einigen anderen. Diese Eigenschaft verweist auf die interne Eigenschaft [[Prototyp]] und ermöglicht Änderungen nach der Erstellung von Objekten. Alle Eigenschaften und Funktionen werden aufgrund dieser verketteten Suche sofort auf den Prototyp umgeschaltet.Diese Funktion ist zwar jetzt standardisiert, aber immer noch kein erforderlicher Bestandteil von JavaScript. In unterstützenden Sprachen besteht eine hohe Wahrscheinlichkeit, dass Ihr Code in die Kategorie "nicht optimiert" eingestuft wird. JS-Engines müssen ihr Bestes geben, um Code zu klassifizieren, insbesondere "heißen" Code, auf den sehr oft zugegriffen wird, und wenn Sie etwas Besonderes wie das Ändern tun
__proto__
, werden sie Ihren Code überhaupt nicht optimieren.In diesem Beitrag https://bugzilla.mozilla.org/show_bug.cgi?id=607863 werden speziell die aktuellen Implementierungen
__proto__
und deren Unterschiede erläutert . Jede Implementierung macht es anders, weil es ein hartes und ungelöstes Problem ist. Alles in Javascript ist veränderbar, außer a.) Die Syntax b.) Hostobjekte (das DOM existiert technisch außerhalb von Javascript) und c.)__proto__
. Der Rest liegt vollständig in Ihren Händen und bei jedem anderen Entwickler, sodass Sie sehen können, warum__proto__
er wie ein schmerzender Daumen hervorsteht.Es gibt eine Sache,
__proto__
die dies ansonsten unmöglich macht: die Bezeichnung eines Objektprototyps zur Laufzeit getrennt von seinem Konstruktor. Dies ist ein wichtiger Anwendungsfall und einer der Hauptgründe, warum er__proto__
noch nicht tot ist. Es ist wichtig genug, dass dies ein ernstzunehmender Diskussionspunkt bei der Formulierung von Harmony war oder bald als ECMAScript 6 bekannt sein wird. Die Möglichkeit, den Prototyp eines Objekts während der Erstellung anzugeben, wird Teil der nächsten Version von Javascript sein Die Glocke, die__proto__
die Tage anzeigt , ist offiziell nummeriert.Kurzfristig können Sie verwenden,
__proto__
wenn Sie auf Browser abzielen, die dies unterstützen (nicht IE und kein IE wird dies jemals tun). Es ist wahrscheinlich, dass es in den nächsten 10 Jahren in Webkit und Moz funktioniert, da ES6 erst 2013 fertiggestellt wird.Brendan Eich - re: Ansatz neuer Objektmethoden in ES5 :
quelle
non-writable, configurable
würde __proto__ diese Bedenken beseitigen, indem der Benutzer gezwungen wird, die Eigenschaft explizit neu zu konfigurieren. Am Ende sind schlechte Praktiken mit dem Missbrauch der Fähigkeiten einer Sprache verbunden, nicht mit den Fähigkeiten selbst. Beschreibbares __proto__ ist keine Seltenheit. Es gibt viele andere nicht aufzählbare beschreibbare Eigenschaften, und obwohl es Gefahren gibt, gibt es auch Best Practices. Der Kopf eines Hammers sollte nicht einfach entfernt werden, weil er nicht ordnungsgemäß zum Verwunden von Personen verwendet werden kann.__proto__
ist erforderlich, da Object.create nur Objekte erzeugt, keine Funktionen, noch Symbole, Regexes, DOM-Elemente oder andere Hostobjekte. Wenn Sie möchten, dass Ihre Objekte aufrufbar oder auf andere Weise speziell sind, aber dennoch ihre Prototypenkette ändern, stecken Sie ohne__proto__
Einstellbarkeit oder Proxies fest__proto__
in JSON" beseitigt würde . Brendan Eichs andere Sorgen sind lächerlich. Rekursive Prototypketten oder die Verwendung eines Prototyps mit unzureichenden Methoden für das angegebene Objekt sind Programmiererfehler, keine Sprachfehler und sollten kein Faktor sein. Außerdem können sie mit Object.create genauso gut wie mit frei einstellbaren auftreten__proto__
.__proto__
.ES6 gibt schließlich Object.setPrototypeOf (Objekt, Prototyp) an, das bereits in Chrome und Firefox implementiert ist.
quelle
Sie können
constructor
eine Instanz eines Objekts verwenden, um den Prototyp eines Objekts an Ort und Stelle zu ändern. Ich glaube, das ist es, worum Sie bitten.Dies bedeutet, wenn Sie welche haben
foo
, ist eine Instanz vonFoo
:Sie können
bar
allen Instanzen von eine Eigenschaft hinzufügen,Foo
indem Sie folgende Schritte ausführen:Hier ist eine Geige, die den Proof-of-Concept zeigt: http://jsfiddle.net/C2cpw/ . Ich bin mir nicht sicher, wie ältere Browser mit diesem Ansatz abschneiden werden, aber ich bin mir ziemlich sicher, dass dies den Job ziemlich gut machen sollte.
Wenn Sie die Funktionalität in Objekte mischen möchten, sollte dieses Snippet die folgende Aufgabe erfüllen:
quelle
foo.__proto__.bar = 'bar';
Sie können
foo.__proto__ = FooClass.prototype
AFAIK, das von Firefox, Chrome und Safari unterstützt wird. Beachten Sie, dass die__proto__
Unterkunft nicht dem Standard entspricht und möglicherweise irgendwann verschwindet.Dokumentation: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto . Unter http://www.mail-archive.com/[email protected]/msg00392.html finden Sie auch eine Erklärung, warum es keine gibt
Object.setPrototypeOf()
und warum sie__proto__
veraltet ist.quelle
Sie können Ihre Proxy-Konstruktorfunktion definieren, dann eine neue Instanz erstellen und alle Eigenschaften vom ursprünglichen Objekt darauf kopieren.
Live-Demo: http://jsfiddle.net/6Xq3P/
Der
Custom
Konstruktor stellt den neuen Prototyp dar, alsoCustom.prototype
enthält sein Objekt alle neuen Eigenschaften, die Sie mit Ihrem ursprünglichen Objekt verwenden möchten.Innerhalb des
Custom
Konstruktors kopieren Sie einfach alle Eigenschaften vom ursprünglichen Objekt in das neue Instanzobjekt.Dieses neue Instanzobjekt enthält alle Eigenschaften des ursprünglichen Objekts (sie wurden im Konstruktor darauf kopiert) sowie alle darin definierten neuen Eigenschaften
Custom.prototype
(da das neue Objekt eineCustom
Instanz ist).quelle
Sie können den Prototyp eines JavaScript-Objekts nicht ändern, das bereits browserübergreifend instanziiert wurde. Wie andere bereits erwähnt haben, stehen folgende Optionen zur Verfügung:
__proto__
EigenschaftBeides ist nicht besonders gut, insbesondere wenn Sie ein Objekt rekursiv in innere Objekte schleifen müssen, um den gesamten Prototyp eines Elements effektiv zu ändern.
Alternative Lösung zur Frage
Ich werde einen abstrakteren Blick auf die Funktionalität werfen, die Sie sich wünschen.
Grundsätzlich ermöglichen Prototypen / Methoden nur die Möglichkeit, Funktionen basierend auf einem Objekt zu gruppieren.
Anstatt zu schreiben
du schreibst
Die obige Syntax wurde aufgrund der object.method () -Syntax als OOP bezeichnet. Einige der Hauptvorteile von OOPs gegenüber der herkömmlichen funktionalen Programmierung sind:
obj.replace('needle','replaced')
anstatt sich Namen wiestr_replace ( 'foo' , 'bar' , 'subject')
und die Position der verschiedenen Variablen merken zu müssenstring.trim().split().join()
) ist möglicherweise einfacher zu ändern und zu schreiben als verschachtelte Funktionenjoin(split(trim(string))
Leider können Sie in JavaScript (wie oben gezeigt) einen bereits vorhandenen Prototyp nicht ändern. Im Idealfall können Sie oben
Object.prototype
nur für das oben angegebene Objekt Änderungen vornehmen. Durch Änderungen könnenObject.prototype
jedoch möglicherweise Skripte beschädigt werden (was zu einer Kollision von Eigenschaften und zum Überschreiben führt).Es gibt keinen häufig verwendeten Mittelweg zwischen diesen beiden Programmierstilen und keine OOP-Methode zum Organisieren benutzerdefinierter Funktionen.
UnlimitJS bietet einen Mittelweg, mit dem Sie benutzerdefinierte Methoden definieren können. Es vermeidet:
Mit Ihrem obigen Code würde ich einfach einen Namespace von Funktionen erstellen, die Sie für das Objekt aufrufen möchten.
Hier ist ein Beispiel:
Weitere Beispiele finden Sie hier UnlimitJS . Wenn Sie
[Unlimit]()
eine Funktion aufrufen , kann die Funktion grundsätzlich als Methode für ein Objekt aufgerufen werden. Es ist wie ein Mittelweg zwischen der OOP und funktionalen Straßen.quelle
[[prototype]]
Soweit ich weiß, können Sie die Referenz bereits erstellter Objekte nicht ändern . Sie können die Prototyp-Eigenschaft der ursprünglichen Konstruktorfunktion ändern, aber wie Sie bereits kommentiert haben, ist dies der KonstruktorObject
, und das Ändern von JS-Kernkonstrukten ist eine schlechte Sache.Sie können jedoch ein Proxy-Objekt des erstellten Objekts erstellen, das die zusätzlichen Funktionen implementiert, die Sie benötigen. Sie können die zusätzlichen Methoden und Verhaltensweisen auch monkeypatchen, indem Sie sie direkt dem betreffenden Objekt zuweisen.
Vielleicht können Sie das, was Sie wollen, auf andere Weise bekommen, wenn Sie bereit sind, sich aus einem anderen Blickwinkel zu nähern: Was müssen Sie tun, um mit dem Prototyp herumzuspielen?
quelle
__proto__
Eigenschaft ändern . Dies funktioniert nur in Browsern, die die__proto__
Notation Chrome und Firefox unterstützen, und sie ist veraltet . Kurz gesagt, Sie können das[[prototype]]
Objekt ändern , sollten es aber wahrscheinlich nicht.Wenn Sie den Prototyp kennen, warum nicht ihn in den Code einfügen?
Sobald die Daten serialisiert sind, erhalten Sie
Jetzt brauchen Sie nur noch einen Konstruktor, der ein Array als Argument verwendet.
quelle
Es gibt keine Möglichkeit, es wirklich zu erben
Array
oder "unterzuordnen".Was Sie tun können, ist Folgendes ( WARNUNG: FESTERING CODE AHEAD ):
Dies funktioniert, verursacht jedoch gewisse Probleme für jeden, der seinen Pfad kreuzt (es sieht aus wie ein Array, aber es wird schief gehen, wenn Sie versuchen, es zu manipulieren).
Warum gehen Sie nicht den richtigen Weg und fügen Methoden / Eigenschaften direkt hinzu
foo
oder verwenden einen Konstruktor und speichern Ihr Array als Eigenschaft?quelle
Wenn Sie einen Prototyp im laufenden Betrieb erstellen möchten, ist dies eine der Möglichkeiten
quelle
quelle
prototype
ist statisch? Ich bin mir nicht sicher was du meinst.prototype
ist eine Eigenschaft einer Funktion, kein Objekt.Object.prototype
existiert;{}.prototype
nicht.Object.getPrototypeOf(foo)
Sie das Konstruktorprototypobjekt abrufen. Sie können die Eigenschaften dafür ändern, um den Prototyp des Objekts zu ändern. Es funktioniert nur in neueren Browsern, kann Ihnen jedoch nicht sagen, welche.Object.prototype
direkt Änderungen vornehmen, da dies höchstwahrscheinlich der Fall istObject.getPrototypeOf(foo)
. Nicht sehr nützlich.