The different types can roughly be divided into:
Patterns that work with constructors
Patterns that work with objects
You can also classify the patterns based on whether they:
Use the prototype
Copy properties
Do both (copy properties of the prototype)
| Method | Name | Example | Classification | Notes |
|---|---|---|---|---|
| 1 | Prototype chaining (pseudo-classical) | Child.prototype = new Parent();
| Works with constructors Uses the prototype chain | The default mechanism described in the ECMA standard. Tip: move all properties/methods that are meant to be reused to the prototype, and add the non-reusable as own properties |
| 2 | Inherit only the prototype | Child.prototype = Parent.prototype;
| Works with constructors Copies the prototype (no prototype chain, all share the same prototype object) | More efficient since no new instances are created just for the sake of inheritance. Prototype chain lookup during runtime is fast, since there's no chain. Drawback: children can modify parents' functionality |
| 3 | Temporary constructor | function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
| Works with constructors Uses the prototype chain | Unlike #1, it only inherits properties of the prototype. Own properties (created with this inside the constructor) are not inherited. Used in YUI and Ext.js libraries Provides convenient access to the parent (through uber) Inheritance |
| 4 | Copying the prototype properties | function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
| Works with constructors Copies properties Uses the prototype chain | All properties of the parent prototype become properties of the child prototype No need to create a new object only for inheritance Shorter prototype chains |
| 5 | Copy all properties (shallow copy) | function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
| Works with objects Copies properties | Very simple Used in Firebug, earlier jQuery and Prototype.js versions Also known as shallow copy Doesn't use prototypes |
| 6 | Deep copy | //same as above, but recurse into objects
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if(typeof p[i] === 'object') {
c[i] = p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
| Works with objects Copies properties | Same as #5, but copies the objects by value Used in more recent versions of jQuery |
| 7 | Prototypal inheritance | function object(o){
function F() {};
F.prototype = o;
return new F();
}
| Works with objects Uses the prototype chain | No pseudo-classes; objects inherit from objects Leverages the benefits of the prototype |
| 8 | Extend and augment | function objectPlus(o, stuff) {
var n;
function F() {};
F.prototype = o;
n = new F();
n.uber = o;
for (var i in stuff) {
n[i] = stuff[i];
}
return n;
}
| Works with objects Uses the prototype chain Copies properties | Mix of prototypal inheritance (#7) and copying properties (#5) One function call to inherit and extend at the same time |
| 9 | Multiple inheritance | function multi() {
var n = {}, stuff, j = 0, len = arguments.length;
for (j = 0; j < len; j++) {
stuff = arguments[j];
for (var i in stuff) {
n[i] = stuff[i];
}
}
return n;
}
| Works with objects Copies properties | A mixin-style implementation Copies all the properties of all the parent objects in the order of appearance |
| 10 | Parasitic inheritance | function parasite(victim) {
var that = object(victim);
that.more = 1;
return that;
}
//example:
var twoD = {
name: '2D shape',
dimensions: 2
}
function triangle(s, h) {
var that = parasite(twoD);
that.name = 'Triangle';
that.getArea = function() { return this.side * this.height / 2; };
that.side = s;
that.height = h;
return that;
}
var t = triangle(5, 10);
t.dimensions
var t2 = new triangle(5,5);
t2.getArea();
t2.name
| Works with objects Uses the prototype chain | Constructor-like function, creates objects Copies an object; augments and returns the copy |
| 11 | Borrowing constructors | function Child() {
Parent.apply(this, arguments);
}
| Works with constructors | Inherits only own properties Can be combined with #1 to inherit prototype too Easy way to deal with the issues when a child inherits a property that is an object (and therefore passed by reference) |
| 12 | Borrow a constructor and copy the prototype | function Child() {
Parent.apply(this, arguments);
}
extend2(Child, Parent);
| Works with constructors Uses the prototype chain Copies properties | Combination of #11 and #4 Allows you to inherit both own properties and prototype properties without calling the parent constructor twice. |
Given so many options, you are probably wondering which is the right one? That depends on your style and preferences, your project, task, and team.
Are you more comfortable thinking in terms of classes?
Then pick one of the methods that work with constructors. Are you going to need just one or a few instances of your "class"? Then choose an object-based pattern.
Are these the only ways of implementing inheritance?
No.
You can chose a pattern from the table above or you can mix them, or you can think of your own.
The important thing is to understand and be comfortable with objects, prototypes, and constructors; the rest is easy.
--end of extract from book Object Oriented JavaScript ----
It gives fresh perspective on the subject. Is it practical? No that sure, especially after you read, JavaScript Design Patterns, chapter on Inheritance. Is it helpful for better understanding, yes, still I will have to reread or revisit it from time to time.
No comments:
Post a Comment