Wzór OLOO Kyle'a Simpsona a wzór prototypowy

109

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."
shmuli
źródło
2
Czy możesz przynajmniej podać link do opisu wzoru, o który pytasz. Jeszcze lepiej byłoby pokazać w swoim pytaniu przykładowy kod.
jfriend00
4
Getify jest czasami na Stackoverflow. Napisałem mu na Twitterze to pytanie :)
Pointy

Odpowiedzi:

155

co dokładnie wprowadza jego wzór?

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:

function Foo() {}
Foo.prototype.y = 11;

function Bar() {}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.z = 31;

var x = new Bar();
x.y + x.z;  // 42

Formularz OLOO:

var FooObj = { y: 11 };

var BarObj = Object.create(FooObj);
BarObj.z = 31;

var x = Object.create(BarObj);
x.y + x.z;  // 42

W obu fragmentach xobiekt jest [[Prototype]]połączony z obiektem ( Bar.prototypelub BarObj), który z kolei jest połączony z trzecim obiektem ( Foo.prototypelub FooObj).

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 x1przez x1000, itp.) Jest identyczna w przypadku fragmentów. Wykonywanie delegacji ( x.yi x.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 / newmechanizmó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 classvs OLOO.

Kyle Simpson
źródło
2
Znalazłem naprawdę interesującą twoją odpowiedź i pomysł OLOO opisany w twoich książkach, chciałbym poznać twoją opinię na to pytanie: stackoverflow.com/questions/40395762/… Szczególnie jeśli uważasz, że ta implementacja jest poprawna i jak rozwiązać problem związany z dostępem członek prywatny. Z góry dziękuję za poświęcony czas i gratuluję wydania najnowszej książki.
GibboK
Ale Object.create(...)jest wielokrotnie wolniejszy niż new. jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…
Pier
3
@Pier wydajność nie jest tak naprawdę dużym problemem. naprawiono zepsuty link do posta na blogu dotyczący sprawdzania poprawności działania tworzenia obiektów, który wyjaśnia, jak właściwie o tym myśleć.
Kyle Simpson
2
A jQuery jest wolniejszy niż DOM API, prawda? Ale to bieżący rok, stary - wolę pisać elegancko i prosto, niż martwić się optymalizacją. Jeśli później będę musiał przeprowadzić mikro-optymalizację, będę się tym martwić, gdy nadejdzie czas.
Eirik Birkeland
6
Chciałbym dodać, że teraz, nieco ponad rok później, Object.create () jest mocno zoptymalizowany w chrome i że jsperf to pokazuje - jest to obecnie jedna z najszybszych opcji. To dokładnie pokazuje, dlaczego nie powinieneś zajmować się takimi mikro-optymalizacjami, a zamiast tego po prostu pisać algorytmicznie poprawny kod.
Kyle Baker
25

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 ( superwywoł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:

W delegowaniu zachowań unikamy, jeśli to w ogóle możliwe, nazywania rzeczy takimi samymi na różnych poziomach [[Prototype]]łańcucha.

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ć resourceobiekt z save()funkcją, która wysyła wersję JSON obiektu do serwera, ale możesz również mieć clientResourceobiekt, 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 specialResourceobiektu, 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 nazwie save, która przesłania podstawową save()funkcjonalność resourceprzedmiot dwa łącza w dół łańcucha prototypów:

var resource = {
  save: function () { 
    console.log('Saving');
  }
};

var clientResource = Object.create(resource);

clientResource.stripAndSave = function () {
  // Do something else, then delegate
  console.log('Stripping unwanted properties');
  this.save();
};

var specialResource = Object.create( clientResource );

specialResource.timeStampedSave = function () {
  // Set the timestamp of the last save
  this.save = Date.now();
  this.stripAndSave();
};

a = Object.create(clientResource);
b = Object.create(specialResource);

a.stripAndSave();    // "Stripping unwanted properties" & "Saving".
b.timeStampedSave(); // Error!

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 initmetoda - 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 ( lastSavedbyłoby znacznie lepsze, ale to tylko przykład).

Ed Hinchliffe
źródło
23
Zgadzam się, że możliwość kolizji nazw jest wadą ... ale w rzeczywistości jest to wada samego [[Prototype]]systemu, a nie konkretnie OLOO.
Kyle Simpson
Być może należało o tym wspomnieć w książce?
wypożycza
Nie jestem pewien, czy to naprawdę rozwiązanie problemu, który opisuje @Ed Hinchliffe, ponieważ po prostu przenosi save () do swojej własnej przestrzeni nazw, ale działa codepen.io/tforward/pen/govEPr?editors=1010
Tristan Forward
Myślę, że @ ed-hinchliffe miał na myśli b.timeStampedSave();zamiast a.timeStampedSave();ostatniego wiersza fragmentu kodu.
amangpt777
1
@ tristan-forward dziękuję za wciągnięcie w to Ricka i Morty'ego!
Eric Bishard
13

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” ):

function Point(x,y) {
    this.x = x;
    this.y = y;
};

function Point3D(x,y,z) {
    Point.call(this, x,y);
    this.z = z;
};

Point3D.prototype = Object.create(Point.prototype);

Natomiast we wzorze OLOO różne formy składniowe używane do definiowania bazy i obiektów pochodnych:

var Point = {
    init  : function(x,y) {
        this.x = x;
        this.y = y;
    }
};


var Point3D = Object.create(Point);
Point3D.init = function(x,y,z) {
    Point.init.call(this, x, y);
    this.z = z;
};

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:

  1. połączenie Object.create
  2. 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):

     var p2a = Object.create(Point);
    
     p2a.init(1,1);

Natomiast we wzorcu Prototype używasz operatora standardowego new:

var p2a = new Point(1,1);

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 funkcja squarew poniższym kodzie:

Point.square = function(x) {return x*x;};

Point.prototype.length = function() {
    return Math.sqrt(Point.square(this.x)+Point.square(this.y));
};

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:

var Point = {
    init  : function(x,y) {
        this.x = x;
        this.y = y;
    },
    square: function(x) {return x*x;},
    length: function() {return Math.sqrt(Point.square(this.x)+Point.square(this.y));}
};
Marcus Junius Brutus
źródło
2
W pierwszym przykładzie kodu nie ma literałów. Prawdopodobnie nadużywasz terminu „dosłownego”, nadając mu inne znaczenie. Tylko mówię ...
Ivan Kleshnin
2
Jeśli chodzi o drugi punkt, autor argumentuje, że „lepszym” oddzieleniem obaw jest oddzielenie tworzenia i inicjalizacji, i cytuje, że mogą istnieć rzadkie przypadki użycia, w których może się to sprawdzić (na przykład pula obiektów). Uważam, że argument jest strasznie słaby.
wypożycza
2
Jeśli chodzi o drugi punkt, w OLOO możesz tworzyć obiekty w jednym czasie i czekać na inicjalizację, podczas gdy w przypadku konstruktora musisz zainicjować podczas tworzenia, więc Kyle uważa to za korzyść.
taco
5

„Pomyślałem, że to sprawia, że ​​każdy obiekt jest zależny od drugiego”

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 przez constructorwywołania.

var foo= {},
    bar= Object.create(foo),
    baz= Object.create(bar);


console.log(Object.getPrototypeOf(foo)) //Object.prototype

console.log(Object.getPrototypeOf(bar)) //foo

console.log(Object.getPrototypeOf(baz)) //bar

A teraz pomyśl przez chwilę, o czym myślisz foo baribaz jako zależne od siebie, nawzajem?

Zróbmy teraz ten sam constructorkod stylu-

function Foo() {}

function Bar() {}

function Baz() {}

Bar.prototype= Object.create(Foo);
Baz.prototype= Object.create(Bar);

var foo= new Foo(),
    bar= new Bar().
    baz= new Baz();

console.log(Object.getPrototypeOf(foo)) //Foo.prototype
console.log(Object.getPrototypeOf(Foo.prototype)) //Object.prototype

console.log(Object.getPrototypeOf(bar)) //Bar.prototype
console.log(Object.getPrototypeOf(Bar.prototype)) //Foo.prototype

console.log(Object.getPrototypeOf(baz)) //Baz.prototype
console.log(Object.getPrototypeOf(Baz.prototype)) //Bar.prototype

Jedyna różnica b / w drugi i były kodu jest, że w tym ostatnim foo, bar, bazbbjects są połączone ze sobą, drugi przez dowolnych obiektów ich constructorfunkcji ( Foo.prototype, Bar.prototype, Baz.prototype), ale w pierwszej z nich ( OLOOstylu) są bezpośrednio połączone. Oba sposoby jesteś po prostu łączące foo, bar, bazze 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ć.

var anotherObj= {};
Object.setPrototypeOf(foo, anotherObj);

Więc wszystkie są od siebie niezależne.

„Miałem nadzieję, OLOOże rozwiążę problem, w którym każdy obiekt nic nie wie o drugim”.

Tak, to rzeczywiście możliwe-

Użyjmy Techjako obiektu użytkowego-

 var Tech= {
     tag: "technology",
     setName= function(name) {
              this.name= name;
}
}

utwórz tyle obiektów, ile chcesz, aby były połączone Tech-

var html= Object.create(Tech),
     css= Object.create(Tech),
     js= Object.create(Tech);

Some checking (avoiding console.log)- 

    html.isPrototypeOf(css); //false
    html.isPrototypeOf(js); //false

    css.isPrototypeOf(html); //false
    css.isPrototypeOf(js); //false

    js.isPrototypeOf(html); //false
    js.isPrototypwOf(css); //false

    Tech.isPrototypeOf(html); //true
    Tech.isPrototypeOf(css); //true
    Tech.isPrototypeOf(js); //true

Czy uważasz html, css, jsobiekty są połączone ze sobą, inne? Nie, nie są. Zobaczmy teraz, jak mogliśmy to zrobić za pomocą constructorfunkcji-

function Tech() { }

Tech.prototype.tag= "technology";

Tech.prototype.setName=  function(name) {
              this.name= name;
}

utwórz tyle obiektów, ile chcesz, aby były połączone Tech.proptotype-

var html= new Tech(),
     css= new Tech(),
      js= new Tech();

Trochę sprawdzania (unikanie console.log) -

html.isPrototypeOf(css); //false
html.isPrototypeOf(js); //false

css.isPrototypeOf(html); //false
css.isPrototypeOf(js); //false

js.isPrototypeOf(html); //false
js.isPrototypeOf(css); //false

Tech.prototype.isPrototypeOf(html); //true
Tech.prototype.isPrototypeOf(css); //true
Tech.prototype.isPrototypeOf(js); //true

Jak myślisz, jak te constructor-Style Objects ( html, css, js) Przedmioty różnią się od OLOOkodu -Style? W rzeczywistości służą temu samemu celowi. W OLOOstylu Techdelegacja jednego obiektu do (delegacja została ustawiona jawnie), podczas gdy delegacja constructorjednego obiektu w stylu Tech.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ąc OLOOstylu -styl, pośrednio constructor-styl.

„Tak jak jest, ObjB musi zostać utworzony z ObjA .. Object.create (ObjB) etc”

Nie, ObjBtutaj nie jest przykładem (w językach klasycznych) żadnej klasy ObjA. Można by powiedzieć, że objBobiekt jest delegowany na ObjAobiekt w czasie jego tworzenia . Gdybyś użył konstruktora, zrobiłbyś to samo„ sprzęganie ”, chociaż pośrednio, używając .prototypes.

Abhishek Sachan
źródło
3

@Marcus @bholben

Może możemy zrobić coś takiego.

    const Point = {

        statics(m) { if (this !== Point) { throw Error(m); }},

        create (x, y) {
            this.statics();
            var P = Object.create(Point);
            P.init(x, y);
            return P;
        },

        init(x=0, y=0) {
            this.x = x;
            this.y = y;
        }
    };


    const Point3D = {

        __proto__: Point,

        statics(m) { if (this !== Point3D) { throw Error(m); }},

        create (x, y, z) {
            this.statics();
            var P = Object.create(Point3D);
            P.init(x, y, z);
            return P;
        },

        init (x=0, y=0, z=0) {
            super.init(x, y);
            this.z = z;
        }
    }; 

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:

  1. 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ć na Point3D.init(). Innym sposobem byłoby zrobienie czegoś takiego

    const Point3D = Object.assign(Object.create(Point), {  
        ...  
    }   

    chociaż nie podoba mi się szczególnie składnia.


  1. Zawsze możemy po prostu zawinąć, p = Object.create(Point)a następnie p.init()umieścić w konstruktorze. np Point.create(x,y). Korzystając z powyższego kodu, możemy utworzyć Point3D„wystąpienie” w następujący sposób.

    var b = Point3D.create(1,2,3);
    console.log(b);                         // { x:1, y:2, z:3 }
    console.log(Point.isPrototypeOf(b));    // true
    console.log(Point3D.isPrototypeOf(b))   // true

  1. 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ą.

        var p = Point.create(1,2);
        var q = p.create(4,1);          // Error!  

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,

    const extendedJS = {};  

    ( function(extension) {

        const statics = Symbol('static');

        Object.defineProperty(Object.prototype, statics, {
            writable: true,
            enumerable: false,
            configurable: true,
            value(obj, message) {
                if (this !== obj)
                    throw Error(message);
            }
        });

        Object.assign(extension, {statics});

    })(extendedJS);


    const Point = {
        create (x, y) {
            this[extendedJS.statics](Point);
            ...

Andrzeja Szymczaka
źródło
2

@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:

var ObjA = {
  setA: function(a) {
    this.a = a;
  },
  outputA: function() {
    console.log("Invoking outputA - A: ", this.a);
  }
};

// 'ObjB' links/delegates to 'ObjA'
var ObjB = Object.create( ObjA );

ObjB.setB = function(b) {
   this.b = b;
}

ObjB.setA_B = function(a, b) {
    this.setA( a ); // This is obvious. 'setA' is not found in 'ObjB' so by prototype chain it's found in 'ObjA'
    this.setB( b );
    console.log("Invoking setA_B - A: ", this.a, " B: ", this.b);
};

// 'ObjC' links/delegates to 'ObjB'
var ObjC = Object.create( ObjB );

ObjC.setC = function(c) {
    this.c = c;  
};

ObjC.setA_C = function(a, c) {
    this.setA( a ); // Invoking 'setA' that is clearly not in ObjC shows that prototype chaining goes through ObjB all the way to the ObjA
    this.setC( c );
    console.log("Invoking setA_C - A: ", this.a, " C: ", this.c);
};

ObjC.setA_B_C = function(a, b, c){
    this.setA( a ); // Invoking 'setA' that is clearly not in ObjC nor ObjB shows that prototype chaining got all the way to the ObjA
    this.setB( b );
    this.setC( c );
    console.log("Invoking setA_B_C - A: ", this.a, " B: ", this.b, " C: ", this.c);
};

ObjA.setA("A1");
ObjA.outputA(); // Invoking outputA - A:  A1

ObjB.setA_B("A2", "B1"); // Invoking setA_B - A:  A2  B:  B1

ObjC.setA_C("A3", "C1"); // Invoking setA_C - A:  A3  C:  C1
ObjC.setA_B_C("A4", "B2", "C1"); // Invoking setA_B_C - A:  A4  B:  B2  C:  C1

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”):

Innymi słowy, rzeczywisty mechanizm, istota tego, co jest ważne dla funkcjonalności, którą możemy wykorzystać w JavaScript, polega na łączeniu obiektów z innymi obiektami .

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. :)

NenadPavlov
źródło
Tak - dzięki - ale miałem nadzieję odejść od tej metodologii, ponieważ sposób, w jaki ją masz i sposób, w jaki to zrobiłem, sprawia, że ​​każdy przedmiot jest zależny od drugiego. Miałem nadzieję, że OLOO rozwiąże problem, w którym każdy obiekt nic nie wie o drugim. Tak jak jest, obiekt objB musi zostać utworzony z ObjA .. Object.create (ObjB) itd., Który jest zbyt powiązany. jakieś pomysły?
james emanon
-1

@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 zamiast Object.create(). Gdy zostanie użyty, Twój kod może wyglądać mniej więcej tak ...

var Point = {
    init  : function(x,y) {
        this.x = x;
        this.y = y;
    }
};


var Point3D = link(Point, {
    init: function(x,y,z) {
        Point.init.call(this, x, y);
        this.z = z;
    }
});

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 ...

function link(delegate, props, propsConfig) {
  props = props || {};
  propsConfig = propsConfig || {};

  var obj = {};
  Object.keys(props).forEach(function (key) {
    obj[key] = {
      value: props[key],
      enumerable: propsConfig.isEnumerable || true,
      writable: propsConfig.isWritable || true,
      configurable: propsConfig.isConfigurable || true
    };
  });

  return Object.create(delegate, obj);
}

Oczywiście myślę, że @Kyle nie poparłby shadowania init()funkcji w obiekcie Point3D. ;-)

bholben
źródło
Patrząc wstecz, myślę, że łącząc Object.assign()z Object.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).
bholben
-1

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 ...

var Button = {
     init: function(name, cost) {
       this.buttonName = name;
       this.buttonCost = cost;
     }
}

var Shoe = {
     speed: 100
}

var Bike = {
     range: '4 miles'
}

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:

var newObj = Object.create(oneSingularObject);
    newObj.whatever..

ALE, nasz newObject = (przycisk, rower, but) ......

Jaki jest wzorzec, aby to osiągnąć w OLOO?

James Emanon
źródło
1
To brzmi jak „preferuj kompozycję zamiast dziedziczenia” - świetna strategia. W ES6 możesz użyć 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.
bholben