JS Inheritance Performance

JavaScript performance comparison

Revision 23 of this test case created

Info

Testing widely used inheritance libraries against Fiber.js, ExtJS, TypeScript, DNW FastClass 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>
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;
      }
   
      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;
      };
      Function.prototype.selfExtend = function(creator) {
        var ctor = creator(this.prototype, this);
        ctor.prototype = this.prototype;
        var c = new ctor();
        c.constructor.prototype = c;
        return c.constructor;
      };
    })();
   
   
    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;
    }
   
    function RunTests() {
      for (var i = 0; i < 100; 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");
      }
    }
};
</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);
  }
});
RunTests();
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);
  }
});
RunTests();
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);
  }
});
RunTests();
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({
  initalize: function(val) {
    A.apply(this, arguments);
  },
  method1: function(y, z) {
    A.prototype.method1.call(this, 'x', y, z);
  }
});
var C = B.extend({
  initalize: function(val) {
    B.apply(this, arguments);
  },
  method1: function(z) {
    B.prototype.method1.call(this, 'y', z);
  }
});
RunTests();
pending…
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();
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);
  }
});
RunTests();
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 {
    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();
pending…
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();
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);
RunTests();
pending…
DotNetWise 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();
pending…
DotNetWise extend - fix constructor
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();
pending…
DotNetWise inheritWith
var A = function(val) {
    this.val = val;
    }.define({
    method1: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  });
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);
    }
  }
});

for (var i = 0; i < 100; 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");
}
pending…
DNW SelfCall
var A = function(val) {
    this.val = val;
    }.define({
    method1: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  });
var B = (function(base) {
  function ctor() {
    this.constructor = function(val) {
      base.constructor.call(this, val);
    };
    this.method1 = function(y, z) {
      base.method1.call(this, 'x', y, z);
    }
  };
  ctor.prototype = base;
  var c = new ctor();
  c.constructor.prototype = c;
  return c.constructor;
})(A.prototype);

var C = (function(base) {
  function ctor() {
    this.constructor = function(val) {
      base.constructor.call(this, val);
    };
    this.method1 = function(z) {
      base.method1.call(this, 'y', z);
    }
  };
  ctor.prototype = base;
  var c = new ctor();
  c.constructor.prototype = c;
  return c.constructor;
})(B.prototype);
RunTests();
pending…
DNW FastClass
//Fork me on GitHub! https://github.com/dotnetwise/Javascript-FastClass
var A = AA();
var B = A.selfExtend(function(base, baseCtor) {
  return function() {
    this.constructor = function(val) {
      baseCtor.call(this, val);
    };
    this.method1 = function(y, z) {
      base.method1.call(this, 'x', y, z);
    }
  };
});
var C = B.selfExtend(function(base, baseCtor) {
  return function ctor() {
    this.constructor = function(val) {
      baseCtor.call(this, val);
    };
    this.method1 = function(z) {
      base.method1.call(this, 'y', z);
    }
  };
});
RunTests();
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