Ich frage mich, wie man am besten ein JavaScript-Objekt mit Eigenschaften und Methoden erstellt.
Ich habe Beispiele gesehen, bei denen die Person alle Funktionen verwendet var self = this
und dann verwendet self.
, um sicherzustellen, dass der Bereich immer korrekt ist.
Dann habe ich Beispiele für .prototype
das Hinzufügen von Eigenschaften gesehen, während andere dies inline tun.
Kann mir jemand ein geeignetes Beispiel für ein JavaScript-Objekt mit einigen Eigenschaften und Methoden geben?
javascript
Michael Stum
quelle
quelle
self
reserviertes Wort? Wenn nicht, sollte es sein; daself
ist eine vordefinierte Variable, die sich auf das aktuelle Fenster bezieht.self === window
window
im Browser-Objektmodell wiedocument
oderframes
; Sie können den Bezeichner sicherlich als Variablennamen wiederverwenden. Obwohl ich es stilistisch vorziehevar that= this
, mögliche Verwirrung zu vermeiden. Obwohlwindow.self
es letztendlich sinnlos ist, gibt es selten einen Grund, es zu berühren.this
zu einer lokalen Variablen (z. B.self
) die Dateigröße.Antworten:
Es gibt zwei Modelle zum Implementieren von Klassen und Instanzen in JavaScript: das Prototyping und das Schließen. Beide haben Vor- und Nachteile, und es gibt viele erweiterte Variationen. Viele Programmierer und Bibliotheken haben unterschiedliche Ansätze und Dienstprogramme zur Klassenhandhabung, um einige der hässlicheren Teile der Sprache zu dokumentieren.
Das Ergebnis ist, dass Sie in gemischter Gesellschaft eine Mischung aus Metaklassen haben, die sich alle leicht unterschiedlich verhalten. Was noch schlimmer ist, das meiste JavaScript-Tutorial-Material ist schrecklich und bietet eine Art Zwischenkompromiss, um alle Grundlagen abzudecken, was Sie sehr verwirrt. (Wahrscheinlich ist der Autor auch verwirrt. Das Objektmodell von JavaScript unterscheidet sich stark von den meisten Programmiersprachen und ist an vielen Stellen geradezu schlecht gestaltet.)
Beginnen wir mit dem Prototyp . Dies ist die JavaScript-native Version, die Sie erhalten können: Es gibt ein Minimum an Overhead-Code, und instanceof funktioniert mit Instanzen dieser Art von Objekten.
Wir können der erstellten Instanz Methoden hinzufügen, indem wir
new Shape
sie in dieprototype
Suche dieser Konstruktorfunktion schreiben :Nun zur Unterklasse, soweit Sie das nennen können, was JavaScript für die Unterklasse tut. Wir tun dies, indem wir diese seltsame magische
prototype
Eigenschaft vollständig ersetzen :bevor Sie Methoden hinzufügen:
Dieses Beispiel wird funktionieren und Sie werden Code wie diesen in vielen Tutorials sehen. Aber Mann, das
new Shape()
ist hässlich: Wir instanziieren die Basisklasse, obwohl keine tatsächliche Form erstellt werden soll. Es passiert Arbeit in diesem einfachen Fall , weil JavaScript so schlampig ist: es erlaubt Null Argumente wobei in diesem Fall übergeben, werdenx
undy
wirdundefined
und ist mit dem Prototyp des zugewiesenthis.x
undthis.y
. Wenn die Konstruktorfunktion etwas Komplizierteres tun würde, würde sie flach auf das Gesicht fallen.Wir müssen also einen Weg finden, ein Prototypobjekt zu erstellen, das die gewünschten Methoden und anderen Elemente auf Klassenebene enthält, ohne die Konstruktorfunktion der Basisklasse aufzurufen. Dazu müssen wir mit dem Schreiben des Hilfecodes beginnen. Dies ist der einfachste Ansatz, den ich kenne:
Dadurch werden die Mitglieder der Basisklasse in ihrem Prototyp an eine neue Konstruktorfunktion übertragen, die nichts tut, und dann dieser Konstruktor verwendet. Jetzt können wir einfach schreiben:
statt der
new Shape()
Ungerechtigkeit. Wir haben jetzt einen akzeptablen Satz von Grundelementen für erstellte Klassen.Es gibt einige Verbesserungen und Erweiterungen, die wir bei diesem Modell berücksichtigen können. Zum Beispiel ist hier eine syntaktische Zuckerversion:
Jede Version hat den Nachteil, dass die Konstruktorfunktion nicht vererbt werden kann, wie dies in vielen Sprachen der Fall ist. Selbst wenn Ihre Unterklasse dem Konstruktionsprozess nichts hinzufügt, muss sie daran denken, den Basiskonstruktor mit den von der Basis gewünschten Argumenten aufzurufen. Dies kann leicht automatisiert werden
apply
, aber Sie müssen immer noch schreiben:Eine übliche Erweiterung besteht also darin, das Initialisierungsmaterial in seine eigene Funktion und nicht in den Konstruktor selbst aufzuteilen. Diese Funktion kann dann ganz gut von der Basis erben:
Jetzt haben wir für jede Klasse das gleiche Konstruktorfunktions-Boilerplate. Vielleicht können wir das in eine eigene Hilfsfunktion verschieben, damit wir es nicht weiter tippen müssen, anstatt es beispielsweise
Function.prototype.subclass
umzudrehen und die Funktion der Basisklasse Unterklassen ausspucken zu lassen:... was ein bisschen mehr wie andere Sprachen aussieht, wenn auch mit etwas ungeschickterer Syntax. Wenn Sie möchten, können Sie einige zusätzliche Funktionen hinzufügen. Vielleicht möchten Sie
makeSubclass
einen Klassennamen nehmen und sich daran erinnern und einen StandardtoString
verwenden, der ihn verwendet. Vielleicht möchten Sie den Konstruktor erkennen lassen, wenn er versehentlich ohne das aufgerufen wurdenew
Operator (was sonst oft zu einem sehr nervigen Debugging führen würde):Vielleicht möchten Sie alle neuen Mitglieder übergeben und
makeSubclass
sie dem Prototyp hinzufügen, damit SieClass.prototype...
nicht so viel schreiben müssen. Viele Klassensysteme tun dies, z.Es gibt viele potenzielle Merkmale, die Sie in einem Objektsystem für wünschenswert halten könnten, und niemand stimmt einer bestimmten Formel wirklich zu.
Der Verschlussweg also . Dies vermeidet die Probleme der prototypbasierten Vererbung von JavaScript, indem überhaupt keine Vererbung verwendet wird. Stattdessen:
Jetzt hat jede einzelne Instanz von
Shape
eine eigene Kopie dertoString
Methode (und aller anderen Methoden oder anderen Klassenmitglieder, die wir hinzufügen).Das Schlechte an jeder Instanz, die eine eigene Kopie jedes Klassenmitglieds hat, ist, dass sie weniger effizient ist. Wenn Sie mit einer großen Anzahl von Instanzen untergeordneter Klassen arbeiten, kann die prototypische Vererbung Ihnen besser dienen. Wie Sie sehen, ist es auch etwas ärgerlich, eine Methode der Basisklasse aufzurufen: Wir müssen uns daran erinnern, was die Methode war, bevor der Unterklassenkonstruktor sie überschrieb, sonst geht sie verloren.
[Auch weil hier keine Vererbung vorhanden ist,
instanceof
funktioniert der Operator nicht. Sie müssten Ihren eigenen Mechanismus zum Schnüffeln von Klassen bereitstellen, wenn Sie ihn benötigen. Während Sie die Prototypobjekte auf ähnliche Weise wie bei der Vererbung von Prototypen fummeln könnten , ist es ein bisschen schwierig und es lohnt sich nicht wirklich, es nur zu bekommeninstanceof
Arbeit zu gehen.]Das Gute an jeder Instanz mit einer eigenen Methode ist, dass die Methode dann an die spezifische Instanz gebunden sein kann, der sie gehört. Dies ist nützlich, weil JavaScript auf seltsame Weise
this
in Methodenaufrufen bindet. Dies hat zur Folge, dass, wenn Sie eine Methode von ihrem Eigentümer trennen:dann wird
this
innerhalb der Methode nicht wie erwartet die Circle-Instanz sein (es wird tatsächlich das globalewindow
Objekt sein, was weit verbreitetes Debugging-Leid verursacht). In der Realität geschieht dies normalerweise, wenn eine Methode genommen und einem oder zugewiesensetTimeout
wirdonclick
EventListener
im Allgemeinen.Bei der Prototyp-Methode müssen Sie für jede solche Aufgabe einen Abschluss hinzufügen:
oder in Zukunft (oder jetzt, wenn Sie Function.prototype hacken) können Sie dies auch tun mit
function.bind()
:Wenn Ihre Instanzen auf die Art des Schließens ausgeführt werden, erfolgt die Bindung kostenlos durch das Schließen über die Instanzvariable (normalerweise aufgerufen
that
oderself
, obwohl ich persönlich davon abraten würde, da dieseself
bereits eine andere, andere Bedeutung in JavaScript hat). Sie erhalten die Argumente1, 1
im obigen Snippet jedoch nicht kostenlos, sodass Sie immer noch einen weiteren Abschluss benötigen oder einen,bind()
wenn Sie dies tun müssen.Auch bei der Verschlussmethode gibt es viele Varianten. Sie können es vorziehen, ganz wegzulassen
this
, ein neues zu erstellenthat
und es zurückzugeben, anstatt dennew
Operator zu verwenden:Welcher Weg ist „richtig“? Beide. Welches das Beste ist"? Das hängt von Ihrer Situation ab. FWIW Ich tendiere zum Prototyping für echte JavaScript-Vererbung, wenn ich stark OO-Sachen mache, und zum Schließen für einfache Wegwerf-Seiteneffekte.
Beide Möglichkeiten sind für die meisten Programmierer jedoch nicht intuitiv. Beide haben viele mögliche unordentliche Variationen. Sie werden beide (sowie viele Zwischen- und allgemein fehlerhafte Schemata) treffen, wenn Sie den Code / die Bibliotheken anderer Personen verwenden. Es gibt keine allgemein akzeptierte Antwort. Willkommen in der wundervollen Welt der JavaScript-Objekte.
[Dies war Teil 94 von Warum JavaScript nicht meine Lieblingsprogrammiersprache ist.]
quelle
new
.Ich benutze dieses Muster ziemlich häufig - ich habe festgestellt, dass es mir ziemlich viel Flexibilität gibt, wenn ich es brauche. Im Gebrauch ähnelt es eher Klassen im Java-Stil.
Dies verwendet eine anonyme Funktion, die beim Erstellen aufgerufen wird und eine neue Konstruktorfunktion zurückgibt. Da die anonyme Funktion nur einmal aufgerufen wird, können Sie darin private statische Variablen erstellen (sie befinden sich innerhalb des Abschlusses und sind für die anderen Mitglieder der Klasse sichtbar). Die Konstruktorfunktion ist im Grunde ein Standard-Javascript-Objekt - Sie definieren darin private Attribute und öffentliche Attribute werden an die
this
Variable angehängt .Grundsätzlich kombiniert dieser Ansatz den Crockfordschen Ansatz mit Standard-Javascript-Objekten, um eine leistungsfähigere Klasse zu erstellen.
Sie können es wie jedes andere Javascript-Objekt verwenden:
quelle
this
Muss es trotzdem instanziiert werden, da es nicht verwendet wirdnew
?this
nicht erhalten in der verwendetenconstructor
Funktion, die wirdFoo
im Beispiel.constructor.prototype.myMethod = function () { ... }
.Douglas Crockford diskutiert dieses Thema ausführlich in The Good Parts . Er empfiehlt, den neuen Operator zu vermeiden , um neue Objekte zu erstellen. Stattdessen schlägt er vor, angepasste Konstruktoren zu erstellen. Zum Beispiel:
In Javascript ist eine Funktion ein Objekt und kann verwendet werden, um Objekte zusammen mit dem neuen Operator zu erstellen . Konventionell beginnen Funktionen, die als Konstruktoren verwendet werden sollen, mit einem Großbuchstaben. Sie sehen oft Dinge wie:
Falls Sie vergessen Sie verwenden neue Betreiber , während ein neues Objekt zu erhalten, was Sie ist eine ganz normale Funktion Anruf erhalten, und dies wird auf das globale Objekt stattdessen auf das neue Objekt gebunden.
quelle
new
invar that = {};
sowieso.Um von Bobince's Antwort fortzufahren
In es6 können Sie jetzt tatsächlich eine erstellen
class
Jetzt können Sie also Folgendes tun:
Erweitern Sie also einen Kreis (wie in der anderen Antwort), den Sie tun können:
Endet in es6 etwas sauberer und etwas leichter zu lesen.
Hier ist ein gutes Beispiel dafür in Aktion:
Code-Snippet anzeigen
quelle
Sie können dies auch mithilfe von Strukturen tun:
Dann :
quelle
Ein anderer Weg wäre http://jsfiddle.net/nnUY4/ (ich weiß nicht, ob diese Art der Handhabung der Objekterstellung und der Enthüllungsfunktionen einem bestimmten Muster folgt)
quelle
Wenn man den Trick verwendet, während eines Konstruktoraufrufs "this" zu schließen, dient dies dazu, eine Funktion zu schreiben, die von einem anderen Objekt als Rückruf verwendet werden kann, das keine Methode für ein Objekt aufrufen möchte. Es hat nichts mit "den Umfang korrekt machen" zu tun.
Hier ist ein Vanille-JavaScript-Objekt:
Wenn Sie lesen, was Douglas Crockford über JavaScript zu sagen hat, können Sie viel davon lesen . John Resig ist auch brillant. Viel Glück!
quelle
this
hat alles damit zu tun, "den Umfang korrekt zu machen".self=this
Obwohl es nicht zwingt,this
zu bestehen, ermöglicht es leicht ein "korrektes" Scoping über einen Verschluss.Closure
ist vielseitig. bobince hat die Ansätze von Prototyp und Verschluss beim Erstellen von Objekten gut zusammengefasst . Sie können jedoch einige Aspekte derOOP
Verwendung von Closure in einer funktionalen Programmierweise nachahmen . Denken Sie daran, dass Funktionen Objekte in JavaScript sind . Verwenden Sie die Funktion also anders als Objekt.Hier ist ein Beispiel für die Schließung:
Vor einiger Zeit bin ich auf den Artikel von Mozilla über Closure gestoßen. Folgendes springt mir in die Augen: "Mit einem Abschluss können Sie einige Daten (die Umgebung) einer Funktion zuordnen, die diese Daten verarbeitet. Dies weist offensichtliche Parallelen zur objektorientierten Programmierung auf, bei der Objekte es uns ermöglichen, einige Daten (die Eigenschaften des Objekts) zuzuordnen ) mit einer oder mehreren Methoden ". Es war das erste Mal, dass ich eine Parallelität zwischen Verschluss und klassischem OOP ohne Bezug zum Prototyp las.
Wie?
Angenommen, Sie möchten die Mehrwertsteuer einiger Artikel berechnen. Die Mehrwertsteuer bleibt wahrscheinlich während der Laufzeit eines Antrags stabil. Eine Möglichkeit, dies in OOP (Pseudocode) zu tun:
Grundsätzlich übergeben Sie einen Mehrwertsteuerwert an Ihren Konstruktor und Ihre Berechnungsmethode kann ihn über die Schließung bearbeiten . Anstatt jetzt eine Klasse / einen Konstruktor zu verwenden, übergeben Sie Ihre Mehrwertsteuer als Argument an eine Funktion. Da Sie nur an der Berechnung selbst interessiert sind, wird eine neue Funktion zurückgegeben, bei der es sich um die Berechnungsmethode handelt:
Identifizieren Sie in Ihrem Projekt Werte der obersten Ebene, die ein guter Kandidat für die Berechnung der Mehrwertsteuer sind. Als Faustregel gilt, wenn Sie dieselben Argumente immer wieder weitergeben, gibt es eine Möglichkeit, sie mithilfe von Closure zu verbessern. Keine Notwendigkeit, traditionelle Objekte zu erstellen.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
quelle
Ein Objekt erstellen
Der einfachste Weg, ein Objekt in JavaScript zu erstellen, ist die Verwendung der folgenden Syntax:
Dies eignet sich hervorragend zum strukturierten Speichern von Daten.
Bei komplexeren Anwendungsfällen ist es jedoch häufig besser, Instanzen von Funktionen zu erstellen:
Auf diese Weise können Sie mehrere Objekte erstellen, die denselben "Entwurf" verwenden, ähnlich wie Sie Klassen in z. Java.
Dies kann jedoch mithilfe eines Prototyps noch effizienter durchgeführt werden.
Wenn verschiedene Instanzen einer Funktion dieselben Methoden oder Eigenschaften verwenden, können Sie sie in den Prototyp dieses Objekts verschieben. Auf diese Weise hat jede Instanz einer Funktion Zugriff auf diese Methode oder Eigenschaft, muss jedoch nicht für jede Instanz dupliziert werden.
In unserem Fall ist es sinnvoll, die Methode
f
auf den Prototyp zu verschieben:Erbe
Eine einfache, aber effektive Methode zur Vererbung in JavaScript ist die Verwendung des folgenden Zweiliners:
Das ist ähnlich wie folgt:
Der Hauptunterschied zwischen beiden besteht darin, dass der Konstruktor von
A
bei Verwendung nicht ausgeführt wirdObject.create
, was intuitiver und der klassenbasierten Vererbung ähnlicher ist.Sie können jederzeit festlegen, dass der Konstruktor von optional ausgeführt werden soll,
A
wenn Sie eine neue Instanz von erstellenB
, indem Sie ihn dem Konstruktor hinzufügen vonB
:Wenn Sie alle Argumente von
B
an übergeben möchtenA
, können Sie auch Folgendes verwendenFunction.prototype.apply()
:Wenn Sie ein anderes Objekt in den Konstruktor Kette mixin wollen
B
, können Sie kombinierenObject.create
mitObject.assign
:Demo
Hinweis
Object.create
kann sicher in jedem modernen Browser verwendet werden, einschließlich IE9 +.Object.assign
funktioniert weder in einer IE-Version noch in einigen mobilen Browsern. Es wird empfohlen, PolyfillObject.create
und / oderObject.assign
wenn Sie sie verwenden und Browser unterstützen möchten, die sie nicht implementieren.Eine Polyfüllung finden Sie
Object.create
hier und eineObject.assign
hier .quelle
quelle
Zusätzlich zu der akzeptierten Antwort aus dem Jahr 2009. Wenn Sie moderne Browser ansprechen können, können Sie die Object.defineProperty verwenden .
Eine Liste der Desktop- und Mobilkompatibilität finden Sie in der Browserkompatibilitätsliste von Mozilla . Ja, IE9 + unterstützt es ebenso wie Safari Mobile.
quelle
Sie können dies auch versuchen
quelle
Erstens können Sie Ihre Präferenz ändern Methoden hinzufügen , anstatt den Konstruktor der auf die Instanz -
prototype
Objekt . Ich deklariere fast immer Methoden innerhalb des Konstruktors, weil ich Constructor Hijacking verwende sehr häufig für Zwecke in Bezug auf Vererbung und Dekoratoren verwende.So entscheide ich, wo welche Erklärungen geschrieben werden:
this
)var
Erklärungen Vorrang vorfunction
Erklärungen haben{}
und[]
)public
Erklärungen Vorrang vorprivate
Erklärungen habenFunction.prototype.bind
überthus
,self
,vm
,etc
this
aus dem Lexikalischen Bereich des Sperrraums zurück.Hier ist, warum diese helfen:
Konstrukteur entführt Modelldesign DesignmusterWie Sie sehen können, brauche ich das wirklich nicht,
thus
da ich es vorzieheFunction.prototype.bind
(oder.call
oder.apply
)thus
. In unsererSingleton
Klasse nennen wir es nicht einmal,thus
weil esINSTANCE
mehr Informationen vermittelt. DennModel
wir kehren zurück,this
damit wir den Konstruktor aufrufen können, indem.call
wir die Instanz zurückgeben, die wir an ihn übergeben haben. Redundant haben wir es der Variablen zugewiesenupdated
, obwohl es in anderen Szenarien nützlich ist.Außerdem ziehe ich es vor, Objektliterale mit dem
Bevorzugt Nicht bevorzugtnew
Schlüsselwort gegenüber {Klammern} zu erstellen:Wie Sie sehen können, kann letzterer die "Override" -Eigenschaft seiner Superklasse nicht überschreiben.
Wenn ich dem
Bevorzugt Nicht bevorzugtprototype
Objekt der Klasse Methoden hinzufüge , bevorzuge ich ein Objektliteral - mit oder ohne Verwendung desnew
Schlüsselworts:quelle
Ich möchte erwähnen, dass wir entweder einen Titel oder einen String verwenden können , um ein Objekt zu deklarieren.
Es gibt verschiedene Möglichkeiten, jeden Typ von ihnen aufzurufen. Siehe unten:
quelle
Grundsätzlich gibt es in JS kein Klassenkonzept, daher verwenden wir die Funktion als Klassenkonstruktor, der für die vorhandenen Entwurfsmuster relevant ist.
Bis jetzt hat JS keine Ahnung, dass Sie ein Objekt erstellen möchten. Hier kommt das neue Schlüsselwort.
Ref: Professionelles JS für Webentwickler - Nik Z.
quelle
class
in JS, wie Sie es in Ihrer Überschrift mit demfunction
Schlüsselwort erwähnt haben. Es ist kein Entwurfsmuster, sondern ein beabsichtigtes Merkmal der Sprache. Ich habe Sie nicht abgelehnt, aber es sieht so aus, als hätte es jemand anderes getan, weil die Frage knapp und fast irrelevant ist. Hoffe dieses Feedback hilft.