- Classical pattern
Wzorzec ten ma imitować dziedziczenie klas z języków obiektowych, stąd nazwa. Istnieje kilka sposobów jego implementacji. Poniżej przykład, w którym wykorzystuje się obiekt prototype. Zasada działania jest prosta. Jeżeli przy wywołaniu funkcji nie ma jej w konstruktorze danego obiektu, jej sygnatura poszukiwana jest w referencji __proto__ wskazującej na obiekt prototype. Podobnie rzecz ma się z properties.
function Parent1(n){ this.name = n || 'Parent2'; } Parent1.prototype.say = function(){ console.log('My name is: ' + this.name); }; function Child1(n){ Parent1.apply(this,arguments); } Child1.prototype = new Parent1(); var c1 = new Child1('Child1'); console.log(c1.name); c1.say(); delete c1.name; c1.say();
Konsola:
Child1 My name is: Child1 My name is: Parent1
- Klass
Ideą tego wzorca jest emulacja klas z języków obiektowych, a więc doprowadzenie do takiego API jak w poniższym przykładzie.
var car1 = new car('simple car'); car1.printName(); var car2 = new bmw('bmw car'); car2.printName();
Definicję funkcji konstruktorów wprowadza się za pomocą specjalnej funkcji klass. Przyjmuje ona funkcję konstruktora dla parenta oraz listę properties (jako obiekt), charakterystycznych dla nowego obiektu.
var car = klass(null, { __construct : function(name){ console.log('constructing car...'); this.name = name; }, printName : function(){ console.log(this.name); } }); var bmw = klass(car, { __construct : function(){ console.log('constructing bmw'); }, printName : function(){ console.log('bmw printer'); bmw.uber.printName.call(this); } });
Cała "magia" zawarta jest w funkcji klass:
var klass = function(Parent, props){ var Child, F, i; Child = function(){ if(Child.uber && Child.uber.hasOwnProperty("__construct")){ Child.uber.__construct.apply(this, arguments); } if(Child.prototype.hasOwnProperty("__construct")){ Child.prototype.__construct.apply(this, arguments); } }; Parent = Parent || Object; F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.uber = Parent.prototype; Child.prototype.constructor = Child; for(i in props){ if (props.hasOwnProperty(i)){ Child.prototype[i] = props[i]; } } return Child; };
Przy użyciu pomocniczego obiektu funkcji F(), obiekt Child przejmuje prototype swojego parenta podanego jako argument funkcji. Obiekty uber i prototype służą do wywoływania konstruktorów będących wyżej w hierarchii dziedziczenia w momencie konstrukcji obiektu. Ostatecznie kopiowane są również properties obiektu parent.
- Prototypical inheritance
Jest to prostszy od powyższych i często wystarczający wzorzec, w którym mechanizm dziedziczenia polega na przekazaniu prototypu rodzica do prototypu dziecka. Warto zwrócić uwagę na to jak w obiekcie dziedziczącym zachowuje się funkcja definiowana w prototype, a jak ta z obiektu parenta.
function object(parent){ var F = function(){}; F.prototype = parent; return new F(); } function Parent3(){ this.name = 'Parent3'; var that = this; this.print = function(){ console.log(that.name); }; } Parent3.prototype.printName = function(){ console.log(this.name); }; var newparent = { name: "ParentName" }; var child = object(newparent); console.log(child.name); //errors //child.printName(); //child.print(); var parent3 = new Parent3('Parent3'); var child3 = object(parent3); child3.name = "Child3"; child3.printName(); child3.print();
Konsola wypisze dwa różne wyniki, ponieważ jedynie funkcja z prototypu będzie wywołana z property z obiektu dziedziczącego. Ponadto obiekt z którego chcemy dziedziczyć musi być tworzony przez operator new.
ParentName Child3 Parent3
- Mixin
Jest to specjalny wzorzec umożliwiający dziedziczenie z kilku prostych obiektów w jeden. Tworzy się specjalną funkcję mix, do której przekazywane są jako argumenty obiekty, które chcemy złączyć w całość.
function mix(){ var arg, prop, child = {}; for (arg = 0; arg < arguments.length; arg++) { for(prop in arguments[arg]){ if(arguments[arg].hasOwnProperty(prop)){ child[prop] = arguments[arg][prop]; } } } return child; } var obj = mix({i : 1, one : "one"}, {j : 1, two : "two"}, {j : 2, three : "three"}); console.dir(obj);
W przypadku dwóch identycznie nazywających się properties w obiektach, wybrany zostanie ostatni.
{ i: 1, one: 'one', j: 1, two: 'two', three: 'three' }
Brak komentarzy:
Prześlij komentarz