Unterscheidet sich Kyle Simpsons "OLOO-Muster (Objekte, die mit anderen Objekten verknüpft sind)" in irgendeiner Weise vom Prototyp-Entwurfsmuster? Was genau führt sein Muster ein, außer es durch etwas zu prägen, das speziell auf "Verknüpfen" (das Verhalten von Prototypen) hinweist und klarstellt, dass hier kein "Kopieren" stattfindet (ein Verhalten von Klassen)?
Hier ist ein Beispiel für Kyles Muster aus seinem Buch "Sie kennen JS nicht: this & Object Prototypes":
var Foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this.me;
}
};
var Bar = Object.create(Foo);
Bar.speak = function() {
alert("Hello, " + this.identify() + ".");
};
var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");
b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."
javascript
design-patterns
shmuli
quelle
quelle
Antworten:
OLOO umfasst die Prototypkette unverändert, ohne dass eine andere (IMO verwirrende) Semantik erforderlich ist, um die Verknüpfung herzustellen.
Diese beiden Schnipsel haben also genau das gleiche Ergebnis, kommen aber unterschiedlich dahin.
Konstruktorform:
OLOO Form:
In beiden Snippets ist ein
x
Objekt[[Prototype]]
mit einem Objekt (Bar.prototype
oderBarObj
) verknüpft, das wiederum mit dem dritten Objekt (Foo.prototype
oderFooObj
) verknüpft ist .Die Beziehungen und die Delegierung sind zwischen den Snippets identisch. Die Speichernutzung ist zwischen den Snippets identisch. Die Fähigkeit, viele "Kinder" zu erstellen (auch bekannt als viele Objekte wie "
x1
Durch"x1000
usw.), ist zwischen den Snippets identisch. Die Leistung der Delegierung (x.y
undx.z
) ist zwischen den Snippets identisch. Die Leistung bei der Objekterstellung ist mit OLOO langsamer, aber die Überprüfung der Integrität zeigt, dass die langsamere Leistung wirklich kein Problem darstellt.Ich behaupte, OLOO bietet an, dass es viel einfacher ist, die Objekte einfach auszudrücken und direkt zu verknüpfen, als sie indirekt über den Konstruktor / die
new
Mechanismen zu verknüpfen . Letzteres gibt vor, über Klassen zu sein, ist aber wirklich nur eine schreckliche Syntax zum Ausdrücken von Delegierung ( Randnotiz: ES6-class
Syntax auch!).OLOO schneidet gerade den Mittelsmann aus.
Hier ist ein weiterer Vergleich von
class
vs OLOO.quelle
Object.create(...)
um ein Vielfaches langsamer alsnew
. jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…Ich habe Kyles Buch gelesen und fand es sehr informativ, insbesondere die Details darüber, wie
this
es gebunden ist.Vorteile:
Für mich gibt es ein paar große Profis von OLOO:
1. Einfachheit
OLOO
Object.create()
erstellt ein neues Objekt, das[[prototype]]
mit einem anderen Objekt verknüpft ist . Sie müssen nicht verstehen, dass Funktionen eineprototype
Eigenschaft haben oder sich über mögliche Fallstricke Gedanken machen, die sich aus ihrer Änderung ergeben.2. Sauberere Syntax
Dies ist fraglich, aber ich bin der Meinung, dass die OLOO-Syntax (in vielen Fällen) übersichtlicher und prägnanter ist als der "Standard" -Javascript-Ansatz, insbesondere wenn es um Polymorphismus (
super
Aufrufe im Stil) geht.Nachteile:
Ich denke, es gibt ein fragwürdiges Stück Design (eines, das tatsächlich zu Punkt 2 oben beiträgt), und das hat mit Schatten zu tun:
Die Idee dahinter ist, dass Objekte ihre eigenen spezifischeren Funktionen haben, die dann intern an Funktionen weiter unten in der Kette delegieren. Beispielsweise haben Sie möglicherweise ein
resource
Objekt mit einersave()
Funktion, die eine JSON-Version des Objekts an den Server sendet, aber Sie haben möglicherweise auch einclientResource
Objekt mit einerstripAndSave()
Funktion, die zuerst Eigenschaften entfernt, die nicht an den Server gesendet werden sollen .Das potenzielle Problem ist: Wenn jemand anderes vorbeikommt und beschließt, ein
specialResource
Objekt zu erstellen, das die gesamte Prototypenkette nicht vollständig kennt, kann er sich vernünftigerweise dazu entschließen, einen Zeitstempel für den letzten Speichervorgang unter einer aufgerufenen Eigenschaft zu speichernsave
, die diesave()
Basisfunktionalität überschattet Dasresource
Objekt zwei Glieder entlang der Prototypenkette:Dies ist ein besonders konstruiertes Beispiel, aber der Punkt ist , dass speziell nicht Shadowing andere Eigenschaften auf einige unangenehme Situationen und schweren Einsatz eines Thesaurus führen!
Eine bessere Illustration hierfür wäre möglicherweise eine
init
Methode - besonders ergreifend, da OOLO Konstruktortypfunktionen umgeht. Da wahrscheinlich jedes verwandte Objekt eine solche Funktion benötigt, kann es eine mühsame Übung sein, sie angemessen zu benennen, und die Einzigartigkeit kann es schwierig machen, sich zu merken, welche zu verwenden sind.* Eigentlich ist es nicht besonders vernünftig (
lastSaved
wäre viel besser, aber es ist nur ein Beispiel.)quelle
[[Prototype]]
Systems selbst, nicht speziell von OLOO.b.timeStampedSave();
statt in dera.timeStampedSave();
letzten Zeile des Code-Snippets.Die Diskussion in "Sie kennen JS nicht: this & Object Prototypes" und die Präsentation des OLOO regen zum Nachdenken an und ich habe eine Menge gelernt, die durch das Buch gegangen sind. Die Vorzüge des OLOO-Musters sind in den anderen Antworten gut beschrieben. Ich habe jedoch die folgenden Beschwerden über Haustiere (oder es fehlt etwas, das mich daran hindert, es effektiv anzuwenden):
1
Wenn eine "Klasse" "eine andere" Klasse "im klassischen Muster erbt, können die beiden Funktionen als ähnliche Syntax deklariert werden ( " Funktionsdeklaration "oder" Funktionsanweisung " ):
Im Gegensatz dazu werden im OLOO-Muster verschiedene syntaktische Formen verwendet, um die Basis und die abgeleiteten Objekte zu definieren:
Wie Sie im obigen Beispiel sehen können, kann das Basisobjekt mithilfe der Objektliteralnotation definiert werden, während dieselbe Notation nicht für das abgeleitete Objekt verwendet werden kann. Diese Asymmetrie nervt mich.
2
Im OLOO-Muster besteht das Erstellen eines Objekts aus zwei Schritten:
Object.create
Rufen Sie eine benutzerdefinierte, nicht standardmäßige Methode auf, um das Objekt zu initialisieren (an die Sie sich erinnern müssen, da sie von Objekt zu Objekt unterschiedlich sein kann):
Im Gegensatz dazu verwenden Sie im Prototypmuster den Standardoperator
new
:3
Im klassischen Muster kann ich "statische" Dienstprogrammfunktionen erstellen, die nicht direkt auf einen "Augenblick" angewendet werden, indem ich sie direkt der "Klassen" -Funktion (im Gegensatz zu ihrer
.prototype
) zuweise. ZB wie Funktionsquare
im folgenden Code:Im Gegensatz dazu sind im OLOO-Muster alle "statischen" Funktionen (über die [[Prototyp]] - Kette) auch für die Objektinstanzen verfügbar:
quelle
Wie Kyle erklärt, sind zwei Objekte, wenn sie miteinander
[[Prototype]]
verbunden sind, nicht wirklich voneinander abhängig. stattdessen sind sie einzelne Objekte. Sie verknüpfen ein Objekt mit dem anderen über eine[[Prototype]]
Verknüpfung, die Sie jederzeit ändern können. Wenn Sie zwei[[Prototype]]
verknüpfte Objekte, die im OLOO-Stil erstellt wurden, als voneinander abhängig betrachten, sollten Sie auch die Objekte berücksichtigen, die durchconstructor
Aufrufe erstellt wurden.Jetzt denken für eine Sekunde denken Sie über
foo
bar
undbaz
wie bei jedem anderen-abhängig zu sein?Jetzt machen wir dasselbe mit diesem
constructor
Stilcode-Der einzige Unterschied b / w der letzteren und dem ersteren Code ist , dass in letzterer ein
foo
,bar
,baz
bbjects zu jedem-seitig durch beliebige Objekte ihrer verknüpft sindconstructor
Funktion (Foo.prototype
,Bar.prototype
,Baz.prototype
) , aber in dem früheren eine (OLOO
Art) sie direkt miteinander verbunden sind. Beide Wege Sie nur die Verknüpfungfoo
,bar
,baz
miteinander, direkt in der ehemaligen ein und indirekt in letzterem ein. In beiden Fällen sind die Objekte jedoch unabhängig voneinander, da es sich nicht wirklich um eine Instanz einer Klasse handelt, die nach der Instanziierung nicht von einer anderen Klasse geerbt werden kann. Sie können jederzeit ändern, welches Objekt ein Objekt auch delegieren soll.Sie sind also alle unabhängig voneinander.
Ja das ist ja möglich-
Verwenden wir
Tech
als Utility-Objekt-erstellen Sie so viele Objekte wie Sie verknüpft werden soll
Tech
-Glaubst du
html
,css
,js
verbunden sind Objekte an alle-andere? Nein, das sind sie nicht. Nun wollen wir sehen, wie wir das mitconstructor
function- hätten machen können.erstellen Sie so viele Objekte wie Sie verknüpft werden soll
Tech.proptotype
-Einige Überprüfungen (Vermeidung von console.log) -
Wie denken Sie , diese
constructor
-Stil Objects (html
,css
,js
) unterscheiden sich Objekte aus demOLOO
-Stil Code? Tatsächlich dienen sie demselben Zweck. ImOLOO
Stil delegieren ein Objekt anTech
(Delegierung wurde explizit festgelegt), während imconstructor
Stil ein Objekt an delegiert wirdTech.prototype
(Delegierung wurde implizit festgelegt). Letztendlich verknüpfen Sie die drei Objekte, die keine Verknüpfung miteinander haben, mit einem Objekt, indem Sie direktOLOO
-style und indirektconstructor
-style verwenden.Nein,
ObjB
hier ist nicht wie eine Instanz (in der klassischen basierten Sprachen) jede KlasseObjA
. Es sollte gesagt werden, dass dasobjB
Objekt zumObjA
Zeitpunkt der Erstellung an das Objekt delegiert wird. " Wenn Sie den Konstruktor verwendet hätten, hätten Sie dieselbe 'Kopplung' durchgeführt, allerdings indirekt unter Verwendung von.prototype
s.quelle
@Marcus @bholben
Vielleicht können wir so etwas tun.
Natürlich ist das Erstellen eines Point3D-Objekts, das mit dem Prototyp eines Point2D-Objekts verknüpft ist, etwas albern, aber das ist nicht der Punkt (ich wollte mit Ihrem Beispiel übereinstimmen). Sowieso, soweit die Beschwerden gehen:
Die Asymmetrie kann mit Object.setPrototypeOf von ES6 oder mit dem verpönten ,
__proto__ = ...
das ich verwende , behoben werden . Wir können jetzt auch Super für normale Objekte verwenden, wie in zu sehenPoint3D.init()
. Ein anderer Weg wäre, so etwas zu tunobwohl mir die Syntax nicht besonders gefällt.
Wir können immer nur einwickeln
p = Object.create(Point)
und dannp.init()
in einen Konstruktor. zBPoint.create(x,y)
. Mit dem obigen Code können wir einePoint3D
"Instanz" auf folgende Weise erstellen .Ich habe mir gerade diesen Hack ausgedacht, um statische Methoden in OLOO zu emulieren. Ich bin mir nicht sicher, ob es mir gefällt oder nicht. Es erfordert den Aufruf einer speziellen Eigenschaft am Anfang aller "statischen" Methoden. Zum Beispiel habe ich die
Point.create()
Methode statisch gemacht.Alternativ können Sie mit ES6- Symbolen Javascript-Basisklassen sicher erweitern. Sie können sich also Code sparen und die spezielle Eigenschaft für Object.prototype definieren. Beispielsweise,
quelle
@james emanon - Sie beziehen sich also auf Mehrfachvererbung (siehe Seite 75 im Buch "Sie kennen JS nicht: This & Object Prototypes"). Und diesen Mechanismus finden wir zum Beispiel in der Funktion "Erweitern" des Unterstrichs. Die Namen der Objekte, die Sie in Ihrem Beispiel angegeben haben, sind ein bisschen Mischen von Äpfeln, Orangen und Süßigkeiten, aber ich verstehe den Punkt dahinter. Nach meiner Erfahrung wäre dies die OOLO-Version:
Es ist ein einfaches Beispiel, aber der gezeigte Punkt ist, dass wir nur Objekte in einer eher flachen Struktur / Formation miteinander verketten und dennoch die Möglichkeit haben, Methoden und Eigenschaften von mehreren Objekten zu verwenden. Wir erreichen die gleichen Dinge wie beim Klassen- / "Kopieren der Eigenschaften" -Ansatz. Zusammengefasst von Kyle (Seite 114, "this & Object Prototypes"):
Ich verstehe, dass es für Sie natürlicher wäre, alle "übergeordneten" (vorsichtigen :)) Objekte an einem Ort / Funktionsaufruf anzugeben, anstatt die gesamte Kette zu modellieren.
Was es erfordert, ist eine Verschiebung der Denk- und Modellierungsprobleme in unseren Anwendungen entsprechend. Ich gewöhne mich auch daran. Hoffe es hilft und das endgültige Urteil von Kyle selbst wäre großartig. :) :)
quelle
@Marcus, genau wie Sie, ich war scharf auf OLOO und mag auch nicht die Asymmetrie, wie in Ihrem ersten Punkt beschrieben. Ich habe mit einer Abstraktion gespielt, um die Symmetrie zurückzubringen. Sie können eine
link()
Funktion erstellen , die anstelle von verwendet wirdObject.create()
. Bei Verwendung könnte Ihr Code ungefähr so aussehen ...Denken Sie daran, dass
Object.create()
ein zweiter Parameter übergeben werden kann. Hier ist die Verknüpfungsfunktion, die den zweiten Parameter nutzt. Es ermöglicht auch ein wenig benutzerdefinierte Konfiguration ...Natürlich denke ich, dass @Kyle das Abschatten der
init()
Funktion im Point3D-Objekt nicht unterstützen würde . ;-);quelle
Object.assign()
mitObject.create()
dielink()
obige Funktion erheblich vereinfachen können. An seiner Stelle könnten wir dies verwenden :function create(delegate, props) { return Object.assign(Object.create(delegate), props); }
. Oder noch besser, wir können Underscore oder Lodash verwenden, um es wirklich kurz zu machen :_.create(delegate, props)
.Gibt es eine Möglichkeit, mehr als "zwei" Objekte zu OLOO zu machen? Alle Beispiele, die ich habe, bestehen aus dem basierten Beispiel (siehe Beispiel von OP). Nehmen wir an, wir hatten die folgenden Objekte. Wie können wir ein "viertes" Objekt erstellen, das die Attribute der "anderen" drei hat? ala ...
Diese Objekte sind willkürlich und können alle Arten von Verhaltensweisen umfassen. Aber das Wesentliche ist, wir haben 'n' Anzahl von Objekten und unser neues Objekt braucht etwas von allen drei.
anstelle der angegebenen Beispiele ala:
ABER unser neues Objekt = (Knopf, Fahrrad, Schuh) ......
Was ist das Muster, um dies in OLOO in Gang zu bringen?
quelle
Object.assign()
- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… verwenden . Wenn Sie in ES5 schreiben, können Sie Unterstriche_.extend()
oder Lodashs verwenden_.assign()
. Hier ist ein exzellentes Video zur Erklärung ... youtu.be/wfMtDGfHWpA . Wenn Sie kollidierende Eigenschaften haben, gewinnt die letzte - also ist die Reihenfolge wichtig.