JS Inheritance Performance

JavaScript performance comparison

Revision 26 of this test case created by DotNetWise

Preparation code

<script src="http://code.jquery.com/jquery-1.9.1.min.js">
</script>
<script src="https://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.selfExtend = function(creator) {
    var ctor = creator(this.prototype, this);
    ctor.prototype = this.prototype;
    var c = new ctor();
    c.constructor.prototype = c;
    return c.constructor;
  };
</script>
      
<script>
Benchmark.prototype.setup = function() {
  (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 100 instances of A, B and C and calls .method1 on them
  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

<script src="https://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.selfExtend = function(creator) { var ctor = creator(this.prototype, this); ctor.prototype = this.prototype; var c = new ctor(); c.constructor.prototype = c; return c.constructor; }; </script>

Test runner

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

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Native
// Native - although theoretically fastest, most ugly/unusable b/c of too much repetitive code
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…
ExtJS
//Almost fast enough, but you need to embed Ext Core (30KB gzipped and minified is crazy!)
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…
Fiber.js
//Nice syntax, but slowest
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…
TypeScript
//Lucky you if you can do TypeScript, otherwise this is useless (too ugly to manually write)
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
//Faster than Fiber.js but still way behind Native
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
//Same as above, makes 'constructor' not enumerable with Object.defineProperty which makes it even slower!
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
//Also similar with Fiber.js, somewhat faster (especially and firefox) and even better syntax

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 FastClass
//Fork me on GitHub! https://github.com/dotnetwise/Javascript-FastClass
//Almost fast as TypeScript version, even faster than it on some browsers
var A = AA();
var B = A.selfExtend(function(base, baseCtor) {
  return function() { //basically this function returns what's supposed to be below constructor's prototype - hence we're only relying on Javascript prototype inheritance at any given time
    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.

0 Comments