JS Inheritance Performance (v30)

Revision 30 of this benchmark created by DotNetWise on


Description

Testing widely used inheritance libraries against Fiber.js, ExtJS, TypeScript, DNW FastClass and Native javascript All other libraries are either previous variances of DNW FastClass or are way too weak to compete with the above ones.

Preparation HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js">
</script>
<script src="http://code.jquery.com/jquery-1.9.1.min.js">
</script>
<script src="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/jrclass.js">
</script>
<script src="https://raw.github.com/ded/klass/master/klass.js">
</script>
<script src="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/classy.js">
</script>
<script src="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/ptclass.js"></script>
<script src="https://raw.github.com/documentcloud/underscore/master/underscore.js"></script>
<script src="https://raw.github.com/documentcloud/backbone/master/backbone.js">
</script>
<script src="//ajax.googleapis.com/ajax/libs/mootools/1.3/mootools-yui-compressed.js">
</script>
<script src="//ajax.googleapis.com/ajax/libs/ext-core/3/ext-core.js">
</script>
<script src="http://kiro.me/temp/fiber.js">
</script>
<script>
  //DNW.FastClass - fork me on GitHub https://github.com/dotnetwise/Javascript-FastClass
  Function.prototype.fastClass = function(creator) {
    var base = this.prototype;
    creator.prototype = base;
    var newProto = new creator(base, this);
    newProto.hasOwnProperty('constructor') || (newProto.constructor = function() {
      base.apply(this, arguments);
    });
    newProto.constructor.prototype = newProto;
    return newProto.constructor;
  };
</script>

 <script type="text/javascript">
        var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
        document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
    try {
        var pageTracker = _gat._getTracker("UA-11643574-1");
        pageTracker._trackPageview();
    } catch (err) { }</script>

Setup

(function() {
      //old variations of DNW.FastClass
      var canDefineNonEnumerableProperty = typeof Object.defineProperty === "function";
    
      function __() {};
      Function.prototype.inherit = function(creator, makeConstructorNotEnumerable) {
        /// <summary>Inherits the prototype of the given function. The creator function should return a new Constructor function whose prototype will share this functions's prototype</summary>
        /// <param name="creator" type="Function">function(base, baseConstructor) {<br/>
        ///  return function Derived() {  <br/>
        ///             base.apply(this, arguments); <br/>
        ///     } <br/>
        ///     Notice that there is no .override method call after the function Derrived(){}. This is very important. If you do want to override some methods then use .extend instead
        ///  </param>
        /// <param name="makeConstructorNotEnumerable" type="Boolean">Object.defineProperty is rather slow. If you concern about performance and don't care about 'constructor' being enumerable in for (var i in instance) then let this false. <br/>
        /// Otherwise set it to true and we will redefine the correct constructor i.e. Extendee.prototype.constructor === Extendee that is non-Enumerable
        /// </param>
        __.prototype = this.prototype;
        var Derived = creator.call(this, this.prototype, this);
        Derived.prototype = new __;
        this.__class = Derived.__class = true;
    
        //if we care about Extendee.prototype.constructor === Extendee to be non-Enumerable
        //By default we set the constructor but we don't make it non-enumerable
        if (makeConstructorNotEnumerable && canDefineNonEnumerableProperty) //this is not default as it carries over some performance overhead
        Object.defineProperty(extendeePrototype, 'constructor', {
          enumerable: false,
          value: Derived
        });
        else Derived.prototype.constructor = Derived; //Also fallback for older non-ECMA-5 browsers
        return Derived;
      };
      Function.prototype.extend = function(creator) {
        /// <summary>Extends the prototype of the given function. The creator function should return a new Constructor function whose prototype will share this functions's prototype.</summary>
        /// <param name="creator" type="Function">function(base, baseConstructor) {<br/>
        ///  return function Derived() {  <br/>
        ///             base.apply(this, arguments); <br/>
        ///     }.override(base, {      //custom functions to be added on Derived.prototype <br/>
        ///       method: function() { <br/>
        ///             return base.method.apply(this, arguments); //call the base class' method, assuming is any <br/>
        ///      } <br/>
        ///    }); <br/>
        ///    So it is very important to call the .override at the end. If you simply want to inherit from an object with no overrides then you should call .inherit function instead
        ///  </param>
        var Derived = creator.call(this, this.prototype, this);
    
        this.__class = Derived.__class = true;
    
        return Derived;
      };
    
      Function.prototype.define = function(prototype) {
        /// <summary>Define members on the prototype of the given function with the custom methods and fields specified in the prototype parameter.</summary>
        /// <param name="prototype" type="Plain Object">A custom object with the methods or properties to be added on Extendee.prototype</param>
        var extendeePrototype = this.prototype;
    
        if (prototype) for (var p in prototype)
        extendeePrototype[p] = prototype[p];
        return this;
      }
    
      Function.prototype.override = function(basePrototype, prototype, makeConstructorNotEnumerable) {
        /// <summary>Extends the prototype of the given function with the custom methods and fields specified in the prototype parameter.</summary>
        /// <param name="prototype" type="Plain Object">A custom object with the methods or properties to be added on Extendee.prototype</param>
        __.prototype = basePrototype;
        var extendeePrototype = new __;
        this.prototype = extendeePrototype;
    
        //if we care about Extendee.prototype.constructor === Extendee to be non-Enumerable
        //By default we set the constructor but we don't make it non-enumerable
        if (makeConstructorNotEnumerable && canDefineNonEnumerableProperty) //this is not default as it carries over some performance overhead
        Object.defineProperty(extendeePrototype, 'constructor', {
          enumerable: false,
          value: this
        });
        else extendeePrototype.constructor = this;
    
    
        if (prototype) for (var p in prototype)
        extendeePrototype[p] = prototype[p];
        return this;
      }
    
      Function.prototype.inheritWith = function(creator, makeConstructorNotEnumerable) {
        /// <summary>Extends the prototype of the given function with the custom methods and fields specified in the prototype parameter.</summary>
        /// <param name="prototype" type="Plain Object">A custom object with the methods or properties to be added on Extendee.prototype</param>
        var baseCtor = this;
        var creatorResult = creator.call(this, this.prototype, this) || {};
        var Derrived = creatorResult.constructor ||
        function defaultCtor() {
          baseCtor.apply(this, arguments);
        }; //automatic constructor if ommited
        var derrivedPrototype;
        __.prototype = this.prototype;
        Derrived.prototype = derrivedPrototype = new __;
    
        for (var p in creatorResult)
        derrivedPrototype[p] = creatorResult[p];
    
        //By default we set the constructor but we don't make it non-enumerable
        //if we care about Derrived.prototype.constructor === Derrived to be non-Enumerable we need to use Object.defineProperty
        if (makeConstructorNotEnumerable && canDefineNonEnumerableProperty) //this is not default as it carries over some performance overhead
        Object.defineProperty(derrivedPrototype, 'constructor', {
          enumerable: false,
          value: Derrived
        });
    
        return Derrived;
      };
    
    })();
    
    ///Define ctor A
    
    function AA() {
      function A(val) {
        this.val = val;
      };
    
      A.prototype.method1 = function(x, y, z) {
        this.x = x;
        this.y = y;
        this.x = z;
      };
      return A;
    }
    
    //Creates 500 instances of A, B and C and calls .method1 on them
    
    function RunTests() {
      for (var i = 0; i < 500; i++) {
        var a = new A("a");
        a.method1("x", "y", "z");
        var b = new B("b");
        b.method1("y", "z");
        var c = new C("c");
        c.method1("z");
      }
    }

Test runner

Ready to run.

Testing in
TestOps/sec
John Resig's Class
var A = JRClass.extend({
  init: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var B = A.extend({
  init: function(val) {
    this._super(val);
  },
  method1: function(y, z) {
    this._super('x', y, z);
  }
});
var C = B.extend({
  init: function(val) {
    this._super(val);
  },
  method1: function(z) {
    this._super('y', z);
  }
});
RunTests();
ready
Klass
var A = klass(function(val) {
  this.val = val;
}).methods({
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var B = A.extend(function(val) {}).methods({
  method1: function(y, z) {
    this.supr('x', y, z);
  }
});
var C = B.extend(function(val) {}).methods({
  method1: function(z) {
    this.supr('y', z);
  }
});
RunTests();
ready
Classy
var A = Classy.$extend({
  __init__: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var B = A.$extend({
  __init__: function(val) {
    this.$super(val);
  },
  method1: function(y, z) {
    this.$super('x', y, z);
  }
});
var C = B.$extend({
  __init__: function(val) {
    this.$super(val);
  },
  method1: function(z) {
    this.$super('y', z);
  }
});
RunTests();
ready
Backbone.js
var A = Backbone.Model.extend({
  constructor: function(val) {
    this.val = val;
    Backbone.Model.apply(this, arguments);
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var B = A.extend({
  constructor: function(val) {
    A.apply(this, arguments);
  },
  method1: function(y, z) {
    A.prototype.method1.call(this, 'x', y, z);
  }
});
var C = B.extend({
  constructor: function(val) {
    B.apply(this, arguments);
  },
  method1: function(z) {
    B.prototype.method1.call(this, 'y', z);
  }
});
RunTests();
ready
ExtJS
var A = AA();
var B = function(val) {
    B.superclass.constructor.call(this, val);
    };
Ext.extend(B, A, {
  method1: function(y, z) {
    B.superclass.method1.call(this, 'x', y, z);
  }
});
var C = function(val) {
    C.superclass.constructor.call(this, val);
    };
Ext.extend(C, B, {
  method1: function(z) {
    C.superclass.method1.call(this, 'y', z);
  }
});
RunTests();
ready
PTClass (Prototype.js)
var A = PTClass.create({
  intialize: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});

var B = PTClass.create(A, {
  intialize: function($super, val) {
    $super(val);
  },
  method1: function($super, y, z) {
    $super('x', y, z);
  }
});

var C = PTClass.create(B, {
  intialize: function($super, val) {
    $super(val);
  },
  method1: function($super, z) {
    $super('y', z);
  }
});
RunTests();
ready
Fiber.js
var A = Fiber.extend(function() {
  return {
    init: function(val) {
      this.val = val;
    },
    method1: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  }
});
var B = A.extend(function(base) {
  return {
    init: function(val) {
      base.init.call(this, val);
    },
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    }
  }
});
var C = B.extend(function(base) {
  return {
    init: function(val) {
      base.init.call(this, val);
    },
    method1: function(z) {
      base.method1.call(this, 'y', z);
    }
  }
});
RunTests();
ready
DNW extend
var A = AA();
var B = A.extend(function(base, baseCtor) {
  return function B(val) {
    baseCtor.apply(this, arguments);
  }.override({
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    }
  });
});
var C = B.extend(function(base, baseCtor) {
  return function C(val) {
    baseCtor.apply(this, arguments);
  }.override(base, {
    method1: function(z) {
      base.method1.call(this, 'y', z);
    }
  });
});
RunTests();
ready
DNW extend - fixctor
var A = AA();
var B = A.extend(function(base, baseCtor) {
  return function B(val) {
    baseCtor.apply(this, arguments);
  }.override({
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    }
  }, true);
});
var C = B.extend(function(base, baseCtor) {
  return function C(val) {
    baseCtor.apply(this, arguments);
  }.override(base, {
    method1: function(z) {
      base.method1.call(this, 'y', z);
    }
  }, true);
});
RunTests();
ready
DNW inheritWith
/*var A = function(val) {
    this.val = val;
    }.define({
    method1: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  });*/
var A = AA();
var B = A.inheritWith(function(base, baseCtor) {
  return {
    constructor: function(val) {
      baseCtor.call(this, val);
    },
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    }
  }
});

var C = B.inheritWith(function(base, baseCtor) {
  return {
    constructor: function C(val) {
      baseCtor.call(this, val);
    },
    method1: function(z) {
      base.method1.call(this, 'y', z);
    }
  }
});

RunTests();
ready
TypeScript
var __extends = this.__extends ||
function(d, b) {
  function __() {
    this.constructor = d;
  }
  __.prototype = b.prototype;
  d.prototype = new __();
};
var A = (function() {
  function A(val) {
    this.val = val;
  }
  A.prototype.method1 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  };
  return A;
})();
var B = (function(_super) {
  __extends(B, _super);

  function B(val) {
    _super.call(this, val);
  }
  B.prototype.method1 = function(y, z) {
    _super.prototype.method1.call(this, 'x', y, z);
  };
  return B;
})(A);
var C = (function(_super) {
  __extends(C, _super);

  function C(val) {
    _super.call(this, val);
  }
  C.prototype.method1 = function(z) {
    _super.prototype.method1.call(this, 'y', z);
  };
  return C;
})(B);
RunTests();
ready
Native
// Native
var A = AA();
var B = function(val) {
    A.call(this, val);
    };

function __() {};
__.prototype = A.prototype;
B.prototype = new __();
B.prototype.constructor = B;
B.prototype.method1 = function(y, z) {
  A.prototype.method1.call(this, 'x', y, z);
};

var C = function(val) {
    B.call(this, val);
    };
__.prototype = B.prototype;
C.prototype = new __();
C.prototype.constructor = C;
C.prototype.method1 = function(z) {
  B.prototype.method1.call(this, 'y', z);
};
RunTests();
ready
DNW FastClass
//Fork me on GitHub! https://github.com/dotnetwise/Javascript-FastClass
//creator is the function that returns the custom methods in the new prototype. They need to be 'merged' into 'this'
var A = AA();
var B = A.fastClass(function(base, baseCtor) {
  this.constructor = function(val) {
    baseCtor.call(this, val);
  };
  this.method1 = function(y, z) {
    base.method1.call(this, 'x', y, z);
  }
});
var C = B.fastClass(function(base, baseCtor) {
  this.constructor = function(val) {
    baseCtor.call(this, val);
  };
  this.method1 = function(z) {
    base.method1.call(this, 'y', z);
  }
});
RunTests();
ready

Revisions

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