-
Star
(154)
You must be signed in to star a gist -
Fork
(24)
You must be signed in to fork a gist
-
-
Save getify/5572383 to your computer and use it in GitHub Desktop.
function Foo(who) { | |
this.me = who; | |
} | |
Foo.prototype.identify = function() { | |
return "I am " + this.me; | |
}; | |
function Bar(who) { | |
Foo.call(this,"Bar:" + who); | |
} | |
Bar.prototype = Object.create(Foo.prototype); | |
Bar.prototype.constructor = Bar; // "fixes" the delegated `constructor` reference | |
Bar.prototype.speak = function() { | |
alert("Hello, " + this.identify() + "."); | |
}; | |
var b1 = new Bar("b1"); | |
var b2 = new Bar("b2"); | |
b1.speak(); // alerts: "Hello, I am Bar:b1." | |
b2.speak(); // alerts: "Hello, I am Bar:b2." | |
// some type introspection | |
b1 instanceof Bar; // true | |
b2 instanceof Bar; // true | |
b1 instanceof Foo; // true | |
b2 instanceof Foo; // true | |
Bar.prototype instanceof Foo; // true | |
Bar.prototype.isPrototypeOf(b1); // true | |
Bar.prototype.isPrototypeOf(b2); // true | |
Foo.prototype.isPrototypeOf(b1); // true | |
Foo.prototype.isPrototypeOf(b2); // true | |
Foo.prototype.isPrototypeOf(Bar.prototype); // true | |
Object.getPrototypeOf(b1) === Bar.prototype; // true | |
Object.getPrototypeOf(b2) === Bar.prototype; // true | |
Object.getPrototypeOf(Bar.prototype) === Foo.prototype; // true |
var Foo = { | |
Foo: function(who) { | |
this.me = who; | |
return this; | |
}, | |
identify: function() { | |
return "I am " + this.me; | |
} | |
}; | |
var Bar = Object.create(Foo); | |
Bar.Bar = function(who) { | |
// "constructors" (aka "initializers") are now in the `[[Prototype]]` chain, | |
// so `this.Foo(..)` works easily w/o any problems of relative-polymorphism | |
// or .call(this,..) awkwardness of the implicit "mixin" pattern | |
this.Foo("Bar:" + who); | |
return this; | |
}; | |
Bar.speak = function() { | |
alert("Hello, " + this.identify() + "."); | |
}; | |
var b1 = Object.create(Bar).Bar("b1"); | |
var b2 = Object.create(Bar).Bar("b2"); | |
b1.speak(); // alerts: "Hello, I am Bar:b1." | |
b2.speak(); // alerts: "Hello, I am Bar:b2." | |
// some type introspection | |
Bar.isPrototypeOf(b1); // true | |
Bar.isPrototypeOf(b2); // true | |
Foo.isPrototypeOf(b1); // true | |
Foo.isPrototypeOf(b2); // true | |
Foo.isPrototypeOf(Bar); // true | |
Object.getPrototypeOf(b1) === Bar; // true | |
Object.getPrototypeOf(b2) === Bar; // true | |
Object.getPrototypeOf(Bar) === Foo; // true |
it won't because it's still the same thing under the hood. It's just syntax sugar. It's still going to be easier to think about object relationships this way.
"Class in JS is not harmless sugar for prototypal OO. Class is a virus that infects everything it touches."
@mrme44 maybe you're assuming that not having classes is a problem that people are trying to fix with patterns like OLOO and that once you have classes there's no reason to use anything else.
No one cares about classes*, OLOO is just a nice way to work with objects taking advantage of the freedom the language gives you.
- I guess someone does care about classes since the keyword is coming to ES6 but it's just syntactic sugar. It'll probably be used by people who can't think beyond classical OOP and when it doesn't work according to their expectations they'll just say js sucks.
I agree with @SoundLogic and @noidexe. The main reason to NOT use classes is that they force you to think in terms of hierarchies (fragile base class problem, gorilla banana problem, duplication by necessity problem). Always favour composition over inheritance, in JS is very easy!
See more here: https://medium.com/javascript-scene/javascript-factory-functions-vs-constructor-functions-vs-classes-2f22ceddf33e
It's a shame that JS has this awkward new Constructor pattern. IMO creating an object using this awful method is pale in comparison to straightforward OLOO approach. 'Damn' Crockford has invented Object.create() too late :).
This is the future.
I really fail to see the advantage of OLOO. Even without ES6, it seems that there's more code, more things to think about and less elegance than 'regular' (ie: ES5) style of inheritance.
For example, how is this:
var b1 = new Bar("b1");
var b2 = new Bar("b2");
worst than this:
var b1 = Object.create(Bar).Bar("b1");
var b2 = Object.create(Bar).Bar("b2");
To me, OLOO achieves the same as 'regular' inheritance in JS but I really fail to see the benefits.
This really makes sense. I think the key to understanding the value here is that the OLOO pattern makes use of delegation (something the JS prototype chain is really well-suited for), not inheritance (something that class-based languages use). The prototype chain does not really encourage an inheritance model. The OLOO pattern hints at a mental model that is much easier to reason about in JS. It's just linked objects; objects that can delegate (methods & variables) to other objects linked further up the chain. (no .prototype augmentation, no messy .constructor prop...). Need to initialize variables like a constructor does? create an init() method on the delegate.
Am I wrong in thinking that the main difference between 'prototype' and OLOO is that in 'prototype, the prototype is duplicated and in OLOO the prototype is linked?
@jfbilodeau :
The main problem when thinking through inheritance pattern while coding in JS is that it is NOT what you are doing when you use the constructor call mecanism (ie Factory function + new) or even when you use the new ES6 keywords like Class
, inherits
...
The reason is that the real inheritance pattern (the one implemented in java or C++ and the one you have in mind when you are reasoning) relies on copying where as the one implemented in js relies on linking.
So, at some point, you're gonna have problems. For starters, once made, "a copy is forever" whereas a link can get cut...
So it is much better to use OLOO because it doesn't "lie" about the true structure you are reliying on. It makes the "thinking" and "making" converge, it connects the abstract and the concrete.
The reason is that the real inheritance pattern ... relies on copying where as the one implemented in js relies on linking
Great explanation of the concept! Way to go!
I prefer composition over inheritance. Carrying all the forest doesn't make sense.
The difference is INHERITANCE is when you design your types around what they are and COMPOSITION is when you design your types around what they do.
I believe through this practice the key takeaway is Behavior Delegation of objects linking to other objects.
OLOO for the win. Also add this to Foo:
[Symbol.hasInstance](instance) {
return this.isPrototypeOf(instance)
}
so this will work fine too:
b1 instanceof Bar; // true
b2 instanceof Bar; // true
b1 instanceof Foo; // true
b2 instanceof Foo; // true
Won't ES6 make your OOLO pattern obsolete. What do you think of OOLO vs the new ES6 syntax for creating classes.