Kyle Simpson's OLOO Pattern vs Prototype Design Pattern

what exactly does his pattern introduce?

OLOO embraces the prototype chain as-is, without needing to layer on other (IMO confusing) semantics to get the linkage.

So, these two snippets have the EXACT same outcome, but get there differently.

Constructor Form:

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

OLOO Form:

var FooObj = { y: 11 };

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

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

In both snippets, an x object is [[Prototype]]-linked to an object (Bar.prototype or BarObj), which in turn is linked to third object (Foo.prototype or FooObj).

The relationships and delegation are identical between the snippets. The memory usage is identical between the snippets. The ability to create many "children" (aka, many objects like x1 through x1000, etc) is identical between the snippets. The performance of the delegation (x.y and x.z) is identical between the snippets. The object creation performance is slower with OLOO, but sanity checking that reveals that the slower performance is really not an issue.

What I argue OLOO offers is that it's much simpler to just express the objects and directly link them, than to indirectly link them through the constructor/new mechanisms. The latter pretends to be about classes but really is just a terrible syntax for expressing delegation (side note: so is ES6 class syntax!).

OLOO is just cutting out the middle-man.

Here's another comparison of class vs OLOO.


I read Kyle's book, and I found it really informative, particularly the detail about how this is bound.

Pros:

For me, there a couple of big pros of OLOO:

1. Simplicity

OLOO relies on Object.create() to create a new object which is [[prototype]]-linked to another object. You don't have to understand that functions have a prototype property or worry about any of the potential related pitfalls that come from its modification.

2. Cleaner syntax

This is arguable, but I feel the OLOO syntax is (in many cases) neater and more concise than the 'standard' javascript approach, particularly when it comes to polymorphism (super-style calls).

Cons:

I think there is one questionable bit of design (one that actually contributes to point 2 above), and that is to do with shadowing:

In behaviour delegation, we avoid if at all possible naming things the same at different levels of the [[Prototype]] chain.

The idea behind this is that objects have their own more specific functions which then internally delegate to functions lower down the chain. For example, you might have a resource object with a save() function on it which sends a JSON version of the object to the server, but you may also have a clientResource object which has a stripAndSave() function, which first removes properties that shouldn't be sent to the server.

The potential problem is: if someone else comes along and decides to make a specialResource object, not fully aware of the whole prototype chain, they might reasonably* decide to save a timestamp for the last save under a property called save, which shadows the base save() functionality on the resource object two links down the prototype chain:

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!

This is a particularly contrived example, but the point is that specifically not shadowing other properties can lead to some awkward situations and heavy use of a thesaurus!

Perhaps a better illustration of this would be an init method - particularly poignant as OOLO sidesteps constructor type functions. Since every related object will likely need such a function, it may be a tedious exercise to name them appropriately, and the uniqueness may make it difficult to remember which to use.

*Actually it's not particularly reasonable (lastSaved would be much better, but it's just an example.)


The discussion in "You Don't Know JS: this & Object Prototypes" and the presentation of the OLOO is thought-provoking and I have learned a ton going through the book. The merits of the OLOO pattern are well-described in the other answers; however, I have the following pet complaints with it (or am missing something that prevents me from applying it effectively):

1

When a "class" "inherits" another "class" in the classical pattern, the two function can be declared similar syntax ("function declaration" or "function statement"):

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);

In contrast, in the OLOO pattern, different syntactical forms used to define the base and the derived objects:

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;
};

As you can see in the example above the base object can be defined using object literal notation, whereas the same notation can't be used for the derived object. This asymmetry bugs me.

2

In the OLOO pattern, creating an object is two steps:

  1. call Object.create
  2. call some custom, non standard method to initialize the object (which you have to remember since it may vary from one object to the next):

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

In contrast, in the Prototype pattern you use the standard operator new:

var p2a = new Point(1,1);

3

In the classical pattern I can create "static" utility functions that don't apply directly to an "instant" by assigning them directly to the "class" function (as opposed to its .prototype). E.g. like function square in the below code:

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

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

In contrast, in the OLOO pattern any "static" functions are available (via the [[prototype]] chain) on the object instances too:

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));}
};

"I figured to do it makes each obj dependent on the other"

As Kyle explains when two objects are [[Prototype]] linked, they aren't really dependent on each other; instead they are individual object. You're linking one object to the other with a [[Prototype]] linkage which you can change anytime you wish. If you take two [[Prototype]] linked objects created through OLOO style as being dependent on each other, you should also think the same about the ones created through constructor calls.

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

Now think for a second do you think of foo bar and baz as being dependent on each-other?

Now let's do the same this constructor style code-

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

The only difference b/w the latter and the former code is that in the latter one foo, bar, baz bbjects are linked to each-other through arbitrary objects of their constructor function (Foo.prototype, Bar.prototype, Baz.prototype) but in the former one (OLOO style) they are linked directly. Both ways you're just linking foo, bar, baz with each other, directly in the former one and indirectly in the latter one. But, in both the cases the objects are independent of each-other because it isn't really like an instance of any class which once instantiated, can't be made to inherit from some other class. You can always change which object an object should delegate too.

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

So they're all independent of each-other.

" I was hoping OLOO would solve the issue in which each object knows nothing about the other."

Yes that's indeed possible-

Let's use Tech as an utility object-

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

create as many objects as you wish linked to 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

Do you think html, css, js objects are connected to each-other? No, they aren't. Now let's see how we could've done that with constructor function-

function Tech() { }

Tech.prototype.tag= "technology";

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

create as many objects as you wish linked to Tech.proptotype-

var html= new Tech(),
     css= new Tech(),
      js= new 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.isPrototypeOf(css); //false

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

How do you think these constructor-style Objects (html , css, js) Objects differ from the OLOO-style code? In fact they serve the same purpose. In OLOO-style one objects delegate to Tech(delegation was set explicitly) while in constructor-style one objects delegate to Tech.prototype(delegation was set implicitly). Ultimately you end up linking the three objects, having no linkage with each-other, to one object, directly using OLOO-style, indirectly using constructor-style.

"As is, ObjB has to be created from ObjA.. Object.create(ObjB) etc"

No, ObjB here is not like an instance (in classical-based languages) of any class ObjA. It sould be said like objB object is made delegate to ObjA object at it's creation time". If you used constructor, you would have done the same 'coupling', although indirectly by making use of .prototypes.


@Marcus @bholben

Perhaps we can do something like this.

    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;
        }
    }; 

Of course, creating a Point3D object that links to the prototype of a Point2D object is kind of silly, but that's beside the point (I wanted to be consistent with your example). Anyways, as far as the complaints go:

  1. The asymmetry can be fixed with ES6's Object.setPrototypeOf or the more frowned upon __proto__ = ... that I use. We can also use super on regular objects now too, as seen in Point3D.init(). Another way would be to do something like

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

    though I don't particularly like the syntax.


  1. We can always just wrap p = Object.create(Point) and then p.init() into a constructor. e.g. Point.create(x,y). Using the code above we can create a Point3D "instance" in the following manner.

    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. I just came up with this hack to emulate static methods in OLOO. I'm not sure if I like it or not. It requires calling a special property at the top of any "static" methods. For example, I've made the Point.create() method static.

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

Alternatively, with ES6 Symbols you can safely extend Javascript base classes. So you could save yourself some code and define the special property on Object.prototype. For example,

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