Prototypal inheritance vs Functional inheritance (v7)

Revision 7 of this benchmark created on


Description

This test suite compares performances of pure “prototypal inheritance” and “functional inheritance”. Both patterns have been well explained by Douglas Crockford in his book: JavaScript, The Good Parts.

The benchmark will create several objects built either with prototypal inheritance or functional inheritance, with an inheritance chain of 10 levels, and then will call methods on them.

Setup

// Functional inheritance
    var object1 = function () {
      var that = {
        prop1: 'prop1',
        method1: function () {
          return that.prop1;
        }
      };
      return that;
    };
    
    var object2 = function () {
      var that = object1();
      that.prop2 = 'prop2';
      that.method2 = function () {
        return that.prop2;
      };
      return that;
    };
    
    var object3 = function () {
      var that = object2();
      that.prop3 = 'prop3';
      that.method3 = function () {
        return that.prop3;
      };
      return that;
    };
    
    var object4 = function () {
      var that = object3();
      that.prop4 = 'prop4';
      that.method4 = function () {
        return that.prop4;
      };
      return that;
    };
    
    var object5 = function () {
      var that = object4();
      that.prop5 = 'prop5';
      that.method5 = function () {
        return that.prop5;
      };
      return that;
    };
    
    var object6 = function () {
      var that = object5();
      that.prop6 = 'prop6';
      that.method6 = function () {
        return that.prop6;
      };
      return that;
    };
    
    var object7 = function () {
      var that = object6();
      that.prop7 = 'prop7';
      that.method7 = function () {
        return that.prop7;
      };
      return that;
    };
    
    var object8 = function () {
      var that = object7();
      that.prop8 = 'prop8';
      that.method8 = function () {
        return that.prop8;
      };
      return that;
    };
    
    var object9 = function () {
      var that = object8();
      that.prop9 = 'prop9';
      that.method9 = function () {
        return that.prop9;
      }
      return that;
    };
    
    var object10 = function () {
      var that = object9();
      that.prop10 = 'prop10';
      that.method10 = function () {
        return that.prop10;
      }
      return that;
    };
    
    // Prototypal inheritance
    var inherits = function(Child, Parent) {
      function F() { }
      F.prototype = Parent.prototype;
      Child.prototype = new F;
    };
    
    var Object1 = function () {};
    Object1.prototype.prop1 = 'prop1';
    Object1.prototype.method1 = function () {
      return this.prop1;
    };
    
    var Object2 = function () {
      Object1.call(this);
    };
    inherits(Object2, Object1);
    Object2.prototype.prop2 = 'prop2';
    Object2.prototype.method2 = function () {
      return this.prop2;
    };
    
    var Object3 = function () {
      Object2.call(this);
    };
    inherits(Object3, Object2);
    Object3.prototype.prop3 = 'prop3';
    Object3.prototype.method3 = function () {
      return this.prop3;
    };
    
    var Object4 = function () {
      Object3.call(this);
    };
    inherits(Object4, Object3);
    Object4.prototype.prop4 = 'prop4';
    Object4.prototype.method4 = function () {
      return this.prop4;
    };
    
    var Object5 = function () {
      Object4.call(this);
    };
    inherits(Object5, Object4);
    Object5.prototype.prop5 = 'prop5';
    Object5.prototype.method5 = function () {
      return this.prop5;
    }
    
    var Object6 = function () {
      Object5.call(this);
    };
    inherits(Object6, Object5);
    Object6.prototype.prop6 = 'prop6';
    Object6.prototype.method6 = function () {
      return this.prop6;
    };
    
    var Object7 = function () {
      Object6.call(this);
    };
    inherits(Object7, Object6);
    Object7.prototype.prop7 = 'prop7';
    Object7.prototype.method7 = function () {
      return this.prop7;
    };
    
    var Object8 = function () {
      Object7.call(this);
    };
    inherits(Object8, Object7);
    Object8.prototype.prop8 = 'prop8';
    Object8.prototype.method8 = function () {
      return this.prop8;
    };
    
    var Object9 = function () {
      Object8.call(this);
    };
    inherits(Object9, Object8);
    Object9.prototype.prop9 = 'prop9';
    Object9.prototype.method9 = function () {
      return this.prop9;
    };
    
    var Object10 = function () {
      Object9.call(this);
    };
    inherits(Object10, Object9);
    Object10.prototype.prop10 = 'prop10';
    Object10.prototype.method10 = function () {
      return this.prop10;
    };
    
    // Prototypal inheritance where all functions are bound to this
    var BObject1 = function () {
      this.method1 = this.method1.bind(this);
    };
    BObject1.prototype.prop1 = 'prop1';
    BObject1.prototype.method1 = function () {
      return this.prop1;
    };
    
    var BObject2 = function () {
      BObject1.call(this);
      this.method2 = this.method2.bind(this);
    };
    inherits(BObject2, BObject1);
    BObject2.prototype.prop2 = 'prop2';
    BObject2.prototype.method2 = function () {
      return this.prop2;
    };
    
    var BObject3 = function () {
      BObject2.call(this);
      this.method3 = this.method3.bind(this);
    };
    inherits(BObject3, BObject2);
    BObject3.prototype.prop3 = 'prop3';
    BObject3.prototype.method3 = function () {
      return this.prop3;
    };
    
    var BObject4 = function () {
      BObject3.call(this);
      this.method4 = this.method4.bind(this);
    };
    inherits(BObject4, BObject3);
    BObject4.prototype.prop4 = 'prop4';
    BObject4.prototype.method4 = function () {
      return this.prop4;
    };
    
    var BObject5 = function () {
      BObject4.call(this);
      this.method5 = this.method5.bind(this);
    };
    inherits(BObject5, BObject4);
    BObject5.prototype.prop5 = 'prop5';
    BObject5.prototype.method5 = function () {
      return this.prop5;
    }
    
    var BObject6 = function () {
      BObject5.call(this);
      this.method6 = this.method6.bind(this);
    };
    inherits(BObject6, BObject5);
    BObject6.prototype.prop6 = 'prop6';
    BObject6.prototype.method6 = function () {
      return this.prop6;
    };
    
    var BObject7 = function () {
      BObject6.call(this);
      this.method7 = this.method7.bind(this);
    };
    inherits(BObject7, BObject6);
    BObject7.prototype.prop7 = 'prop7';
    BObject7.prototype.method7 = function () {
      return this.prop7;
    };
    
    var BObject8 = function () {
      BObject7.call(this);
      this.method8 = this.method8.bind(this);
    };
    inherits(BObject8, BObject7);
    BObject8.prototype.prop8 = 'prop8';
    BObject8.prototype.method8 = function () {
      return this.prop8;
    };
    
    var BObject9 = function () {
      BObject8.call(this);
      this.method9 = this.method9.bind(this);
    };
    inherits(BObject9, BObject8);
    BObject9.prototype.prop9 = 'prop9';
    BObject9.prototype.method9 = function () {
      return this.prop9;
    };
    
    var BObject10 = function () {
      BObject9.call(this);
      this.method10 = this.method10.bind(this);
    };
    inherits(BObject10, BObject9);
    BObject10.prototype.prop10 = 'prop10';
    BObject10.prototype.method10 = function () {
      return this.prop10;
    };
    
    
    // Objects cloning
    var Clone = {
      create: function () {
        var object = Object.create(this);
        object.constructor.apply(object, arguments);
        return object;
      },
      extends: function (properties) {
        var Child = Object.create(this);
        for (var p in properties) {
          Child[p] = properties[p];
        }
        return Child;
      },
      constructor: function () {}
    };
    
    var CObject1 = Clone.extends({
      prop1: 'prop1',
      method1: function () {
        return this.prop1;
      }
    });
    
    var CObject2 = CObject1.extends({
      prop2: 'prop2',
      method2: function () {
        return this.prop2;
      }
    });
    
    var CObject3 = CObject2.extends({
      prop3: 'prop3',
      method3: function () {
        return this.prop3;
      }
    });
    
    var CObject4 = CObject3.extends({
      prop4: 'prop4',
      method4: function () {
        return this.prop4;
      }
    });
    
    var CObject5 = CObject4.extends({
      prop5: 'prop5',
      method5: function () {
        return this.prop5;
      }
    });
    
    var CObject6 = CObject5.extends({
      prop6: 'prop6',
      method6: function () {
        return this.prop6;
      }
    });
    
    var CObject7 = CObject6.extends({
      prop7: 'prop7',
      method7: function () {
        return this.prop7;
      }
    });
    
    var CObject8 = CObject7.extends({
      prop8: 'prop8',
      method8: function () {
        return this.prop8;
      }
    });
    
    var CObject9 = CObject8.extends({
      prop9: 'prop9',
      method9: function () {
        return this.prop9;
      }
    });
    
    var CObject10 = CObject9.extends({
      prop10: 'prop10',
      method10: function () {
        return this.prop10;
      }
    });
    
    // No closure!
    var Base = {
        constructor: function () {},
        extend: function (properties) {
            var that = Object.create(this);
            for (var p in properties) {
                that[p] = properties[p];
            }
            return that;
        },
        create: function() {
            var that = {};
            this.constructor.apply(null, Array.prototype.concat.apply([that], arguments));
            return that;
        }
    };
    
    var FObject1 = Base.extend({
      constructor: function (that) {
        that.prop1 = 'prop1';
      },
      function1: function (that) {
        return that.prop1;
      }
    });
    
    var FObject2 = FObject1.extend({
      constructor: function (that) {
        FObject1.constructor(that);
        that.prop2 = 'prop2';
      },
      function2: function (that) {
        return that.prop2;
      }
    });
    
    var FObject3 = FObject2.extend({
      constructor: function (that) {
        FObject2.constructor(that);
        that.prop3 = 'prop3';
      },
      function3: function (that) {
        return that.prop3;
      }
    });
    
    var FObject4 = FObject3.extend({
      constructor: function (that) {
        FObject3.constructor(that);
        that.prop4 = 'prop4';
      },
      function4: function (that) {
        return that.prop4;
      }
    });
    
    var FObject5 = FObject4.extend({
      constructor: function (that) {
        FObject4.constructor(that);
        that.prop5 = 'prop5';
      },
      function5: function (that) {
        return that.prop5;
      }
    });
    
    var FObject6 = FObject5.extend({
      constructor: function (that) {
        FObject5.constructor(that);
        that.prop6 = 'prop6';
      },
      function6: function (that) {
        return that.prop6;
      }
    });
    
    var FObject7 = FObject6.extend({
      constructor: function (that) {
        FObject6.constructor(that);
        that.prop7 = 'prop7';
      },
      function7: function (that) {
        return that.prop7;
      }
    });
    
    var FObject8 = FObject7.extend({
      constructor: function (that) {
        FObject7.constructor(that);
        that.prop8 = 'prop8';
      },
      function8: function (that) {
        return that.prop8;
      }
    });
    
    var FObject9 = FObject8.extend({
      constructor: function (that) {
        FObject8.constructor(that);
        that.prop9 = 'prop9';
      },
      function9: function (that) {
        return that.prop9;
      }
    });
    
    var FObject10 = FObject9.extend({
      constructor: function (that) {
        FObject9.constructor(that);
        that.prop10 = 'prop10';
      },
      function10: function (that) {
        return that.prop10;
      }
    });
    
    
    
    
    // “Python” style
    
          var PObject = {
            extend: function (methods) {
              var parent = this.__methods;
              var arraySlice = Array.prototype.slice;
              var Type = function () {
                for (var m in methods) {
                  this[m] = (function (self, m) {
                    return parent[m] ?
                      function () {
                        var tmp = self._super;
                        self._super = function () {
                          return parent[m].apply(null, [self].concat(arraySlice.call(arguments)))
                        };
                        var ret = methods[m].apply(null, [self].concat(arraySlice.call(arguments)));
                        self._super = tmp;
                        return ret
                      } :
                      function () {
                        return methods[m].apply(null, [self].concat(arraySlice.call(arguments)))
                      }
                  })(this, m)
                }
              };
              var Constructor = function () {
                this.init.apply(null, arguments)
              };
              Constructor.prototype = new Type;
              Constructor.__methods = methods;
              Constructor.extend = arguments.callee;
              return Constructor
            },
            __methods: {}
          };
    
    var PObject1 = PObject.extend({
      init: function (self) {
        self.prop1 = 'prop1'
      },
      function1: function (self) {
        return self.prop1
      }
    });
    
    var PObject2 = PObject1.extend({
      init: function (self) {
        self._super();
        self.prop2 = 'prop2'
      },
      function2: function (self) {
        return self.prop2
      }
    });
    
    var PObject3 = PObject2.extend({
      init: function (self) {
        self._super();
        self.prop3 = 'prop3'
      },
      function3: function (self) {
        return self.prop3
      }
    });
    
    var PObject4 = PObject3.extend({
      init: function (self) {
        self._super();
        self.prop4 = 'prop4'
      },
      function4: function (self) {
        return self.prop4
      }
    });
    
    var PObject5 = PObject4.extend({
      init: function (self) {
        self._super();
        self.prop5 = 'prop5'
      },
      function5: function (self) {
        return self.prop5
      }
    });
    
    var PObject6 = PObject5.extend({
      init: function (self) {
        self._super();
        self.prop6 = 'prop6'
      },
      function6: function (self) {
        return self.prop6
      }
    });
    
    var PObject7 = PObject6.extend({
      init: function (self) {
        self._super();
        self.prop7 = 'prop7'
      },
      function7: function (self) {
        return self.prop7
      }
    });
    
    var PObject8 = PObject7.extend({
      init: function (self) {
        self._super();
        self.prop8 = 'prop8'
      },
      function8: function (self) {
        return self.prop8
      }
    });
    
    var PObject9 = PObject8.extend({
      init: function (self) {
        self._super();
        self.prop9 = 'prop9'
      },
      function9: function (self) {
        return self.prop9
      }
    });
    
    var PObject10 = PObject9.extend({
      init: function (self) {
        self._super();
        self.prop10 = 'prop10'
      },
      function10: function (self) {
        return self.prop10
      }
    });
    
    var ob10 = object10();
    
    var Ob10 = new Object10();
    
    var BOb10 = new BObject10();
    
    var COb10 = CObject10.create();
    
    var FOb10 = FObject10.create();
    
    var POb10 = new PObject10();

Test runner

Ready to run.

Testing in
TestOps/sec
object10()
object10();
ready
new Object10()
new Object10;
ready
obj10.methods()
ob10.method1();
ob10.method2();
ob10.method3();
ob10.method4();
ob10.method5();
ob10.method6();
ob10.method7();
ob10.method8();
ob10.method9();
ob10.method10();
ready
Ob10.methods()
Ob10.method1();
Ob10.method2();
Ob10.method3();
Ob10.method4();
Ob10.method5();
Ob10.method6();
Ob10.method7();
Ob10.method8();
Ob10.method9();
Ob10.method10();
ready
new BObject10()
new BObject10();
ready
BOb10.methods()
BOb10.method1();
BOb10.method2();
BOb10.method3();
BOb10.method4();
BOb10.method5();
BOb10.method6();
BOb10.method7();
BOb10.method8();
BOb10.method9();
BOb10.method10();
ready
CObject10.create();
CObject10.create();
ready
COb10.methods()
COb10.method1();
COb10.method2();
COb10.method3();
COb10.method4();
COb10.method5();
COb10.method6();
COb10.method7();
COb10.method8();
COb10.method9();
COb10.method10();
ready
FObject10.create()
FObject10.create();
ready
FObject10 methods
FObject10.function1(FOb10);
FObject10.function2(FOb10);
FObject10.function3(FOb10);
FObject10.function4(FOb10);
FObject10.function5(FOb10);
FObject10.function6(FOb10);
FObject10.function7(FOb10);
FObject10.function8(FOb10);
FObject10.function9(FOb10);
FObject10.function10(FOb10);
ready
new PObject10
new PObject10();
ready
PObject10.methods
POb10.function1();
POb10.function2();
POb10.function3();
POb10.function4();
POb10.function5();
POb10.function6();
POb10.function7();
POb10.function8();
POb10.function9();
POb10.function10();
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.