Skip to content

Instantly share code, notes, and snippets.

@getify
Last active January 7, 2024 11:58
Star You must be signed in to star a gist
Save getify/5572383 to your computer and use it in GitHub Desktop.
OLOO (objects linked to other objects) pattern explored (with comparison to the prototype style of the same code)
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
@polotek
Copy link

polotek commented May 15, 2013

My ctor library does almost exactly this. It keeps the constructor pattern but focuses on just creating objects. https://github.com/polotek/ctor

I like the idea in this that Bar is an object that you create from. But the constructor is still an important concept. And the way you suggest tacking it on in the second example seems confusing and forced. Curious to hear what you think of ctor.

@getify
Copy link
Author

getify commented Aug 24, 2014

FYI: the whole Foo.Foo and Bar.Bar stuff is totally unnecessary cruft. I usually just call them Foo.init and Bar.init.

@Samjin
Copy link

Samjin commented Jun 8, 2015

I saw your benchmark between oo vs oloo, it seems like the result says oloo is almost 40% slower in some cases. Do you still prefer to use OLOO instead of OO?
http://jsperf.com/performance-oovsoloo

@IPWright83
Copy link

@Samjin I'm not sure that it's going to really make much difference? Compare the # of operations running, these are huge numbers so the actual real affect on the performance of your JavaScript execution is likely to be small compared to the rest of the content on the page.

@mattiaerre
Copy link

@getify as a .Net developer that loves JavaScript as well I genuinely believe that this pattern is really cool. It simply doesn't try to force the concept of class into a language that doesn't have it. On the other hand it does enforce the concept of prototype chain. I am definitely trying to use it from now on instead of the prototype pattern. Thank you so much for sharing this.

@danielmeneses
Copy link

Hi there,

i really like the OLOO approach. I've wrote a tinny polyfill to improve the experience of the OLOO pattern, well at least i think it does and i like to work this way ;). I think this approach allows better readability of the code, it also adds a reference of the parent object for each object created (this.parent) and allows methods to have the same name. Here it goes:

// oloo pattern - my tinny polyfill
Object.prototype.oloo = function(o1, o2){
    if( o1 && o2 && typeof o1 === "object" && typeof o2 === "object"){
        // create new object
        var o0 = Object.create(o1);
        // copy all props to the brand new obj
        for( var key in o2 ){
            if(o2.hasOwnProperty(key)){
                o0[key] = o2[key];
            }
        }
        // set parent reference
        o0.parent = o1;
        return o0;
    }
    return null;
}

var Object1 = {
    name: null,
    init: function(name){
        this.name = name;
        console.log(this.name+" - main");
    }
};

var Object2 = Object.oloo( Object1, {
    middleName: null,
    init: function(name, middelName){
        this.parent.init(name);
        this.middelName = middelName;
        console.log(this.middelName+" - 1");
    }
});

var Object3 = Object.oloo( Object2, {
    lastname: null,
    init: function(name, middleName, lastname){
        this.parent.init(name, middleName);
        this.lastname = lastname;
        console.log( this.lastname +" - 2");
    },
    getFullName: function(){
        return this.name +" "+ this.middelName +" "+ this.lastname;
    }
});
Object3.init("Daniel", "Teles", "Meneses");
console.log( Object3.getFullName() );

@mrme44
Copy link

mrme44 commented Aug 12, 2015

Won't ES6 make your OOLO pattern obsolete. What do you think of OOLO vs the new ES6 syntax for creating classes.

@ivanjuras
Copy link

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.

@SoundLogic
Copy link

"Class in JS is not harmless sugar for prototypal OO. Class is a virus that infects everything it touches."

@noidexe
Copy link

noidexe commented Jan 18, 2016

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

@matteoantoci
Copy link

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

@dagolinuxoid
Copy link

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

@carsonpowers
Copy link

This is the future.

@jfbilodeau
Copy link

jfbilodeau commented Jun 26, 2017

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.

@chumlabs
Copy link

chumlabs commented Jun 26, 2017

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.

@jfbilodeau
Copy link

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?

@GodefroyClair
Copy link

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

@GeoDoo
Copy link

GeoDoo commented Dec 12, 2017

@GodefroyClair

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!

@tnguven
Copy link

tnguven commented Feb 19, 2018

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.

@blachawk
Copy link

blachawk commented Mar 1, 2018

I believe through this practice the key takeaway is Behavior Delegation of objects linking to other objects.

@BenceSzalai
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment