JS Inheritance Performance

JavaScript performance comparison

Revision 18 of this test case created by DotNetWise

Info

Testing widely used inheritance libraries against Fiber.js and native javascript

Preparation code

<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="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/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="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js">
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.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>
  Function.prototype.define = function(name, methods, proto) {
    var ctor = this;
    if (proto) {
      var base = ctor.prototype = new proto();
      ctor.prototype.constructor = ctor;
    }

    var p = ctor.prototype;
    p[name] = p;
    p.ctor = ctor;
    p.m = methods.m;
    //alert(p.m);
    return ctor;
  }
</script>
<script>
Benchmark.prototype.setup = function() {
    (function () {
        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;
        }
    })();
   
};
</script>

Preparation code output

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/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);
  }
});

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("y");
pending…
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);
  }
});

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("y");
pending…
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);
  }
});

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");
pending…
Backbone.js
var A = Backbone.Model.extend({
  initalize: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});

var B = A.extend({
  method1: function(y, z) {
    A.prototype.method1.call(this, 'x', y, z);
  }
});

var C = B.extend({
  method1: function(z) {
    B.prototype.method1.call(this, 'y', z);
  }
});


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("y");
pending…
MooTools
var A = new Class({
  initialize: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});

var B = new Class({
  Extends: A,
  initialize: function(val) {
    this.parent(val);
  },
  method1: function(y, z) {
    this.parent('x', y, z);
  }
});

var C = new Class({
  Extends: B,
  initialize: function(val) {
    this.parent(val);
  },
  method1: function(z) {
    this.parent('y', z);
  }
});

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");
pending…
ExtJS
var A = function(val) {
    this.val = val;
    }
A.prototype.method1 = function(x, y, z) {
  this.x = x;
  this.y = y;
  this.z = z;
};

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

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");
pending…
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);
  }
});

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("c");
pending…
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 {
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    }
  }
});

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

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");
pending…
Native
// Native
var A = function(val) {
    if (!val) {
      this.val = val;
    }
}
A.prototype.method1 = function(x, y, z) {
  this.x = x;
  this.y = y;
  this.x = z;
}

var B = function(val) {
    A.call(this, val);
    }
B.prototype = new A();
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);
    }
C.prototype = new B();
C.prototype.constructor = C;
C.prototype.method1 = function(z) {
  B.prototype.method1.call(this, 'y', z);
};

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");
pending…
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);
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");
pending…
DotNetWise extend
var A = function(val) {
    if (val) {
      this.val = val;
    }
    }.define({
    method1: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  });
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);
    }
  });
});

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");
pending…
DotNetWise extend - fix constructor
var A = function(val) {
    if (val) {
      this.val = val;
    }
    }.define({
    method1: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  });
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);
});

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");
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment