Czy wzorzec „OLOO (obiekty łączące się z innymi obiektami)” Kyle'a Simpsona różni się w jakikolwiek sposób od wzorca projektowego prototypu? Oprócz ukucia tego za pomocą czegoś, co konkretnie wskazuje na „łączenie” (zachowanie prototypów) i wyjaśnienia, że nie ma tu „kopiowania” (zachowanie klas), co dokładnie wprowadza ten wzorzec?
Oto przykład wzoru Kyle'a z jego książki „You Don't Know JS: 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
źródło
źródło
Odpowiedzi:
OLOO obejmuje łańcuch prototypów w takiej postaci, w jakiej jest, bez konieczności nakładania na inne (mylące IMO) semantyki, aby uzyskać powiązanie.
Tak więc te dwa fragmenty mają DOKŁADNY ten sam wynik, ale osiągają go inaczej.
Formularz konstruktora:
Formularz OLOO:
W obu fragmentach
x
obiekt jest[[Prototype]]
połączony z obiektem (Bar.prototype
lubBarObj
), który z kolei jest połączony z trzecim obiektem (Foo.prototype
lubFooObj
).Relacje i delegowanie są identyczne między fragmentami. Wykorzystanie pamięci jest identyczne między fragmentami. Możliwość tworzenia wielu „dzieci” (czyli wielu obiektów, takich jak
x1
przezx1000
, itp.) Jest identyczna w przypadku fragmentów. Wykonywanie delegacji (x.y
ix.z
) jest identyczne między fragmentami. Wydajność tworzenia obiektów jest wolniejsza w przypadku OLOO, ale sprawdza to pod kątem rozsądku pokazuje, że wolniejsza wydajność naprawdę nie stanowi problemu.Twierdzę, że OLOO oferuje to, że znacznie łatwiej jest po prostu wyrazić obiekty i bezpośrednio je połączyć, niż pośrednio połączyć je za pomocą konstruktora /
new
mechanizmów. Ten ostatni udaje, że dotyczy klas, ale tak naprawdę jest po prostu okropną składnią do wyrażania delegacji ( uwaga dodatkowa: tak samo jest z ES6class
składnią !).OLOO właśnie wycina pośrednika.
Oto kolejny porównanie z
class
vs OLOO.źródło
Object.create(...)
jest wielokrotnie wolniejszy niżnew
. jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…Przeczytałem książkę Kyle'a i wydała mi się bardzo pouczająca, zwłaszcza szczegóły dotyczące tego, jak to zrobić
this
jest związana.Plusy:
Dla mnie jest kilka zalet OLOO:
1. Prostota
OLOO polega na
Object.create()
utworzeniu nowego obiektu, który jest[[prototype]]
połączony z innym obiektem. Nie musisz rozumieć, że funkcje mają rozszerzenieprototype
właściwość lub martwić się o jakiekolwiek potencjalne pułapki związane z ich modyfikacją.2. Czystsza składnia
Jest to dyskusyjne, ale uważam, że składnia OLOO jest (w wielu przypadkach) schludniejsza i bardziej zwięzła niż „standardowe” podejście javascriptowe, szczególnie jeśli chodzi o polimorfizm (
super
wywołania w stylu-style).Cons:
Myślę, że jest jeden wątpliwy element projektu (taki, który faktycznie przyczynia się do punktu 2 powyżej), a jest to związane z cieniowaniem:
Pomysł polega na tym, że obiekty mają swoje własne, bardziej szczegółowe funkcje, które następnie wewnętrznie delegują do funkcji niżej w łańcuchu. Na przykład możesz mieć
resource
obiekt zsave()
funkcją, która wysyła wersję JSON obiektu do serwera, ale możesz również miećclientResource
obiekt, który mastripAndSave()
funkcję, która najpierw usuwa właściwości, które nie powinny być wysyłane na serwer .Potencjalny problem polega na tym, że jeśli ktoś inny podejdzie i zdecyduje się na stworzenie
specialResource
obiektu, nie w pełni świadomy całego łańcucha prototypów, może rozsądnie * zdecydować o zapisaniu znacznika czasu dla ostatniego zapisu w ramach właściwości o nazwiesave
, która przesłania podstawowąsave()
funkcjonalnośćresource
przedmiot dwa łącza w dół łańcucha prototypów:Jest to szczególnie wymyślony przykład, ale chodzi o to, że konkretnie nie przesłanianie innych właściwości może prowadzić do niezręcznych sytuacji i intensywnego używania tezaurusa!
Być może lepszą ilustracją tego byłaby
init
metoda - szczególnie przejmująca, ponieważ OOLO omija funkcje typu konstruktora. Ponieważ każdy powiązany obiekt prawdopodobnie będzie potrzebował takiej funkcji, odpowiednie nazwanie ich może być żmudnym ćwiczeniem, a wyjątkowość może utrudniać zapamiętanie, którego użyć.* Właściwie nie jest to szczególnie rozsądne (
lastSaved
byłoby znacznie lepsze, ale to tylko przykład).źródło
[[Prototype]]
systemu, a nie konkretnie OLOO.b.timeStampedSave();
zamiasta.timeStampedSave();
ostatniego wiersza fragmentu kodu.Dyskusja w „You Don't Know JS: this & Object Prototypes” oraz prezentacja OLOO jest prowokująca do myślenia i wiele się nauczyłem przeglądając tę książkę. Zalety wzoru OLOO są dobrze opisane w innych odpowiedziach; jednak mam do niego następujące skargi dotyczące zwierząt domowych (lub brakuje mi czegoś, co uniemożliwia mi skuteczne zastosowanie):
1
Gdy „klasa” „dziedziczy„ inną „klasę” w klasycznym wzorcu, obie funkcje mogą mieć podobną składnię ( „deklaracja funkcji” lub „instrukcja funkcji” ):
Natomiast we wzorze OLOO różne formy składniowe używane do definiowania bazy i obiektów pochodnych:
Jak widać w powyższym przykładzie, obiekt bazowy można zdefiniować za pomocą notacji literału obiektowego, podczas gdy tej samej notacji nie można użyć dla obiektu pochodnego. Wkurza mnie ta asymetria.
2
We wzorcu OLOO tworzenie obiektu przebiega w dwóch krokach:
Object.create
wywołaj jakąś niestandardową, niestandardową metodę inicjalizacji obiektu (o czym musisz pamiętać, ponieważ może się różnić w zależności od obiektu):
Natomiast we wzorcu Prototype używasz operatora standardowego
new
:3
W klasycznym wzorcu mogę tworzyć „statyczne” funkcje narzędziowe, które nie mają zastosowania bezpośrednio do „chwili”, przypisując je bezpośrednio do funkcji „klasy” (w przeciwieństwie do jej
.prototype
). Np. Jak funkcjasquare
w poniższym kodzie:W przeciwieństwie do tego, we wzorcu OLOO wszystkie „statyczne” funkcje są dostępne (za pośrednictwem łańcucha [[prototyp]]) również na instancjach obiektów:
źródło
Jak wyjaśnia Kyle, kiedy dwa obiekty są
[[Prototype]]
połączone, w rzeczywistości nie są one od siebie zależne; zamiast tego są indywidualnym przedmiotem. Łączysz jeden obiekt z drugim za pomocą[[Prototype]]
powiązania, które możesz zmienić w dowolnym momencie. Jeśli weźmiesz dwa[[Prototype]]
połączone obiekty utworzone w stylu OLOO jako zależne od siebie, powinieneś również pomyśleć to samo o tych utworzonych przezconstructor
wywołania.A teraz pomyśl przez chwilę, o czym myślisz
foo
bar
ibaz
jako zależne od siebie, nawzajem?Zróbmy teraz ten sam
constructor
kod stylu-Jedyna różnica b / w drugi i były kodu jest, że w tym ostatnim
foo
,bar
,baz
bbjects są połączone ze sobą, drugi przez dowolnych obiektów ichconstructor
funkcji (Foo.prototype
,Bar.prototype
,Baz.prototype
), ale w pierwszej z nich (OLOO
stylu) są bezpośrednio połączone. Oba sposoby jesteś po prostu łączącefoo
,bar
,baz
ze sobą, bezpośrednio w dawnym jednym i pośrednio w tym ostatnim. Ale w obu przypadkach obiekty są od siebie niezależne, ponieważ w rzeczywistości nie jest to instancja żadnej klasy, która po utworzeniu instancji nie może dziedziczyć po innej klasie. Zawsze możesz zmienić obiekt, który obiekt powinien również delegować.Więc wszystkie są od siebie niezależne.
Tak, to rzeczywiście możliwe-
Użyjmy
Tech
jako obiektu użytkowego-utwórz tyle obiektów, ile chcesz, aby były połączone
Tech
-Czy uważasz
html
,css
,js
obiekty są połączone ze sobą, inne? Nie, nie są. Zobaczmy teraz, jak mogliśmy to zrobić za pomocąconstructor
funkcji-utwórz tyle obiektów, ile chcesz, aby były połączone
Tech.proptotype
-Trochę sprawdzania (unikanie console.log) -
Jak myślisz, jak te
constructor
-Style Objects (html
,css
,js
) Przedmioty różnią się odOLOO
kodu -Style? W rzeczywistości służą temu samemu celowi. WOLOO
styluTech
delegacja jednego obiektu do (delegacja została ustawiona jawnie), podczas gdy delegacjaconstructor
jednego obiektu w styluTech.prototype
(delegacja została ustawiona niejawnie). Ostatecznie łączysz te trzy obiekty, nie mając żadnego połączenia między sobą, z jednym obiektem, bezpośrednio używającOLOO
stylu -styl, pośrednioconstructor
-styl.Nie,
ObjB
tutaj nie jest przykładem (w językach klasycznych) żadnej klasyObjA
. Można by powiedzieć, żeobjB
obiekt jest delegowany naObjA
obiekt w czasie jego tworzenia . ” Gdybyś użył konstruktora, zrobiłbyś to samo„ sprzęganie ”, chociaż pośrednio, używając.prototype
s.źródło
@Marcus @bholben
Może możemy zrobić coś takiego.
Oczywiście tworzenie obiektu Point3D, który łączy się z prototypem obiektu Point2D jest trochę głupie, ale to nie ma znaczenia (chciałem być spójny z twoim przykładem). W każdym razie, jeśli chodzi o skargi:
Asymetrię można naprawić za pomocą Object.setPrototypeOf z ES6 lub bardziej dezaprobatą
__proto__ = ...
tego, którego używam. Możemy teraz również używać super na zwykłych obiektach, jak widać naPoint3D.init()
. Innym sposobem byłoby zrobienie czegoś takiegochociaż nie podoba mi się szczególnie składnia.
Zawsze możemy po prostu zawinąć,
p = Object.create(Point)
a następniep.init()
umieścić w konstruktorze. npPoint.create(x,y)
. Korzystając z powyższego kodu, możemy utworzyćPoint3D
„wystąpienie” w następujący sposób.Właśnie wymyśliłem ten hack, aby emulować statyczne metody w OLOO. Nie jestem pewien, czy mi się to podoba, czy nie. Wymaga wywołania specjalnej właściwości na początku każdej „statycznej” metody. Na przykład uczyniłem
Point.create()
metodę statyczną.Alternatywnie, z ES6 Symbole można bezpiecznie rozszerzyć klas bazowych javascript. Możesz więc zapisać sobie kod i zdefiniować specjalną właściwość w Object.prototype. Na przykład,
źródło
@james emanon - masz na myśli dziedziczenie wielokrotne (omówione na stronie 75 w książce „You Don't Know JS: this & Object Prototypes”). I ten mechanizm możemy znaleźć na przykład w funkcji „rozszerzania” podkreślenia. Nazwy przedmiotów, które podałeś w swoim przykładzie, to trochę mieszanie jabłek, pomarańczy i cukierków, ale rozumiem, o co chodzi. Z mojego doświadczenia wynika, że byłaby to wersja OOLO:
Jest to prosty przykład, ale pokazany punkt jest taki, że po prostu łączymy obiekty razem w dość płaską strukturę / formację i nadal mamy możliwość korzystania z metod i właściwości z wielu obiektów. Osiągamy to samo, co przy podejściu class / "kopiowanie właściwości". Podsumowane przez Kyle'a (strona 114, „This & Object Prototypes”):
Rozumiem, że bardziej naturalnym sposobem byłoby umieszczenie wszystkich obiektów „nadrzędnych” (ostrożnie :)) w jednym wywołaniu miejsca / funkcji, a raczej modelowanie całego łańcucha.
Wymaga to zmiany myślenia i modelowania problemów w naszych aplikacjach zgodnie z tym. Ja też się do tego przyzwyczajam. Mam nadzieję, że to pomoże i ostateczny werdykt samego Kyle'a byłby świetny. :)
źródło
@Marcus, podobnie jak Ty, lubiłem OLOO i nie lubię asymetrii opisanej w pierwszym punkcie. Bawiłem się abstrakcją, aby przywrócić symetrię. Możesz stworzyć
link()
funkcję, która będzie używana zamiastObject.create()
. Gdy zostanie użyty, Twój kod może wyglądać mniej więcej tak ...Pamiętaj, że
Object.create()
ma drugi parametr, który można przekazać. Oto funkcja łączenia, która wykorzystuje drugi parametr. Pozwala także na trochę niestandardowej konfiguracji ...Oczywiście myślę, że @Kyle nie poparłby shadowania
init()
funkcji w obiekcie Point3D. ;-)źródło
Object.assign()
zObject.create()
, możemy znacznie uprościćlink()
powyższą funkcję. W jego miejsce, moglibyśmy użyć tego:function create(delegate, props) { return Object.assign(Object.create(delegate), props); }
. Albo jeszcze lepiej, możemy użyć Underscore lub Lodash aby uczynić go naprawdę zwięzły:_.create(delegate, props)
.Czy istnieje sposób na więcej niż „dwa” obiekty OLOO… wszystkie przykłady, które składają się z przykładu bazowego (patrz przykład OP). Powiedzmy, że mieliśmy następujące obiekty, jak możemy stworzyć „czwarty” obiekt, który ma atrybuty „pozostałych” trzech? ala ...
obiekty te są arbitralne i mogą obejmować wszelkiego rodzaju zachowania. Ale sedno jest takie, że mamy „n” obiektów, a nasz nowy obiekt potrzebuje czegoś ze wszystkich trzech.
zamiast podanych przykładów ala:
ALE, nasz newObject = (przycisk, rower, but) ......
Jaki jest wzorzec, aby to osiągnąć w OLOO?
źródło
Object.assign()
- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Jeśli piszesz w ES5, możesz użyć podkreślenia_.extend()
lub Lodash_.assign()
. Oto doskonały film wyjaśniający ... youtu.be/wfMtDGfHWpA . Jeśli masz jakieś kolidujące właściwości, wygrywa ten ostatni - więc porządek ma znaczenie.