JS Inheritance Performance

JavaScript performance comparison

Revision 62 of this test case created by kogarashisan1

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="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js"></script>
<script src="http://kiro.me/temp/fiber.js"></script>
<script>
///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;
  };
  A.prototype.method2 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.x = z;
  };
  A.prototype.method3 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.x = z;
  };
  return A;
}
</script>
<script>
  //DNW.FastClass - fork me on GitHub https://github.com/dotnetwise/Javascript-FastClass
  (function() {
    var Object_keys = Object.keys;
    var Object_defineProperty = Object.defineProperty;

    var canDefineNonEnumerableProperty = typeof Object_defineProperty === "function";
    var supportsProto = {};
    supportsProto = supportsProto.__proto__ === Object.prototype;
    if (supportsProto) {
      try {
        supportsProto = {};
        supportsProto.__proto__ = {
          Object: 1
        };
        supportsProto = supportsProto.Object === 1; //setting __proto__ in firefox is terribly slow!
      } catch (ex) {
        supportsProto = false;
      }
    }

    function __() {};
    Function.prototype.fastClass = function(creator, makeConstructorNotEnumerable) {
      /// <summary>Inherits the function's prototype to a new function named constructor returned by the creator parameter</summary>
      /// <param name="creator" type="function(base, baseCtor) { }">where base is BaseClass.prototype and baseCtor is BaseClass - aka the function you are calling .fastClass on</param>
      //this == constructor of the base "Class"
      var baseClass = this;
      var base = this.prototype;
      creator = creator ||
      function() {
        this.constructor = function() {
          baseClass.apply(this, arguments);
        }
      };
      creator.prototype = base;

      //creating the derrived class' prototype
      var derrivedProrotype = new creator(base, this);

      //did you forget or not intend to add a constructor? We'll add one for you
      if (!derrivedProrotype.hasOwnProperty("constructor")) derrivedProrotype.constructor = function() {
        baseClass.apply(this, arguments);
      }

      //By default we set the constructor but we don't make it non-enumerable
      //if we care about constructor.prototype.constructor === constructor 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(prototype, 'constructor', {
        enumerable: false,
        value: Derrived
      });

      //setting the derrivedPrototype to constructor's prototype
      derrivedProrotype.constructor.prototype = derrivedProrotype;

      //returning the constructor
      return derrivedProrotype.constructor;
    };

    Function.prototype.inheritWith = function(creator, makeConstructorNotEnumerable) {
      /// <summary>Inherits the function's prototype to a new function named constructor returned by the creator parameter</summary>
      /// <param name="creator" type="function(base, baseCtor) { return { constructor: function() {..}...} }">where base is BaseClass.prototype and baseCtor is BaseClass - aka the function you are calling .inheritWith on</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.inheritWith2 = !supportsProto ? Function.prototype.inheritWith : function(creator, makeConstructorNotEnumerable) {
      /// <summary>Inherits the function's prototype to a new function named constructor returned by the creator parameter</summary>
      /// <param name="creator" type="function(base, baseCtor) { return { constructor: function() {..}...} }">where base is BaseClass.prototype and baseCtor is BaseClass - aka the function you are calling .inheritWith on</param>
      var baseCtor = this;
      var derrivedPrototype = creator.call(this, this.prototype, this) || {};
      var Derrived = derrivedPrototype.constructor ||
      function defaultCtor() {
        baseCtor.apply(this, arguments);
      }; //automatic constructor if ommited
      Derrived.prototype = derrivedPrototype;
      derrivedPrototype.__proto__ = this.prototype;

      //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.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 key in prototype)
        extendeePrototype[key] = prototype[key];
      }
      return this;
    }


    Function.define = function(creator, makeConstructorNotEnumerable) {
      /// <summary>Defines a function named constructor returned by the creator parameter and extends it's protoype with all other functions</summary>
      /// <param name="creator" type="function() { return { constructor: function() {..}...} }"></param>
      var creatorResult = creator.call(this) || {};
      var constructor = creatorResult.constructor ||
      function() {}; //automatic constructor if ommited
      var prototype = constructor.prototype;
      for (var p in creatorResult)
      prototype[p] = creatorResult[p];

      //By default we set the constructor but we don't make it non-enumerable
      //if we care about constructor.prototype.constructor === constructor 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(prototype, 'constructor', {
        enumerable: false,
        value: Derrived
      });

      return constructor;
    };

  })();
</script>
<script>
  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>
  try {
    var pageTracker = _gat._getTracker("UA-11643574-1");
    pageTracker._trackPageview();
  } catch (err) {}
</script>

<!-- Lava.ClassManager -->
<script src="https://dl.dropboxusercontent.com/u/3554716/jsperf/lava-class-manager.js"></script>
<script>
Lava.ClassManager.registerRootNamespace('global', window);
Lava.ClassManager.define(
'global.LavaA',
{
	val: null,
	x: null,
	y: null,
	z: null,
	init: function(val) {
		this.val = val;
	},
	method1: function(x, y, z) {
		this.x = x;
		this.y = y;
		this.z = z;
	},
	method2: function(x, y, z) {
		this.x = x;
		this.y = y;
		this.z = z;
	},
	method3: function(x, y, z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
});

Lava.ClassManager.define(
'global.LavaB',
{
	Extends: "global.LavaA",

	init: function(val) {
		this.LavaA$init(val);
	},
	method1: function(y, z) {
		this.LavaA$method1('x', y, z);
	},
	method2: function(y, z) {
		this.LavaA$method2('x', y, z);
	},
	method3: function(y, z) {
		this.LavaA$method3('x', y, z);
	}
});

Lava.ClassManager.define(
'global.LavaC',
{
	Extends: "global.LavaB",

	init: function(val) {
		this.LavaB$init(val);
	},
	method1: function(z) {
		this.LavaB$method1('y', z);
	},
	method2: function(z) {
		this.LavaB$method2('y', z);
	},
	method3: function(z) {
		this.LavaB$method3('y', z);
	}
});
</script>

<script>
// Fiber
var FiberA = Fiber.extend(function() {
  return {
    init: function(val) {
      this.val = val;
    },
    method1: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    },
    method2: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    },
    method3: function(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  }
});
var FiberB = FiberA.extend(function(base) {
  return {
    init: function(val) {
      base.init.call(this, val);
    },
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    },
    method2: function(y, z) {
      base.method2.call(this, 'x', y, z);
    },
    method3: function(y, z) {
      base.method3.call(this, 'x', y, z);
    }
  }
});
var FiberC = FiberB.extend(function(base) {
  return {
    init: function(val) {
      base.init.call(this, val);
    },
    method1: function(z) {
      base.method1.call(this, 'y', z);
    },
    method2: function(z) {
      base.method2.call(this, 'y', z);
    },
    method3: function(z) {
      base.method3.call(this, 'y', z);
    }
  }
});
</script>

<script>
// PTClass
var PTClassA = PTClass.create({
  intialize: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method2: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method3: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});

var PTClassB = PTClass.create(PTClassA, {
  intialize: function($super, val) {
    $super(val);
  },
  method1: function($super, y, z) {
    $super('x', y, z);
  },
  method2: function($super, y, z) {
    $super('x', y, z);
  },
  method3: function($super, y, z) {
    $super('x', y, z);
  }
});

var PTClassC = PTClass.create(PTClassB, {
  intialize: function($super, val) {
    $super(val);
  },
  method1: function($super, z) {
    $super('y', z);
  },
  method2: function($super, z) {
    $super('y', z);
  },
  method3: function($super, z) {
    $super('y', z);
  }
});
</script>

<script>
// backbone
var BackboneA = 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;
  },
  method2: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method3: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var BackboneB = BackboneA.extend({
  constructor: function(val) {
    BackboneA.apply(this, arguments);
  },
  method1: function(y, z) {
    BackboneA.prototype.method1.call(this, 'x', y, z);
  },
  method2: function(y, z) {
    BackboneA.prototype.method2.call(this, 'x', y, z);
  },
  method3: function(y, z) {
    BackboneA.prototype.method3.call(this, 'x', y, z);
  }
});
var BackboneC = BackboneB.extend({
  constructor: function(val) {
    BackboneB.apply(this, arguments);
  },
  method1: function(z) {
    BackboneB.prototype.method1.call(this, 'y', z);
  },
  method2: function(z) {
    BackboneB.prototype.method2.call(this, 'y', z);
  },
  method3: function(z) {
    BackboneB.prototype.method3.call(this, 'y', z);
  }
});
</script>

<script>
// classy
var ClassyA = Classy.$extend({
  __init__: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method2: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method3: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var ClassyB = ClassyA.$extend({
  __init__: function(val) {
    this.$super(val);
  },
  method1: function(y, z) {
    this.$super('x', y, z);
  },
  method2: function(y, z) {
    this.$super('x', y, z);
  },
  method3: function(y, z) {
    this.$super('x', y, z);
  }
});
var ClassyC = ClassyB.$extend({
  __init__: function(val) {
    this.$super(val);
  },
  method1: function(z) {
    this.$super('y', z);
  },
  method2: function(z) {
    this.$super('y', z);
  },
  method3: function(z) {
    this.$super('y', z);
  }
});
</script>
<script>
var KlassA = klass(function(val) {
  this.val = val;
}).methods({
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method2: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method3: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var KlassB = KlassA.extend(function(val) {}).methods({
  method1: function(y, z) {
    this.supr('x', y, z);
  },
  method2: function(y, z) {
    this.supr('x', y, z);
  },
  method3: function(y, z) {
    this.supr('x', y, z);
  }
});
var KlassC = KlassB.extend(function(val) {}).methods({
  method1: function(z) {
    this.supr('y', z);
  },
  method2: function(z) {
    this.supr('y', z);
  },
  method3: function(z) {
    this.supr('y', z);
  }
});
</script>

<script>
var JRClassA = JRClass.extend({
  init: function(val) {
    this.val = val;
  },
  method1: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method2: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  },
  method3: function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
});
var JRClassB = JRClassA.extend({
  init: function(val) {
    this._super(val);
  },
  method1: function(y, z) {
    this._super('x', y, z);
  },
  method2: function(y, z) {
    this._super('x', y, z);
  },
  method3: function(y, z) {
    this._super('x', y, z);
  }
});
var JRClassC = JRClassB.extend({
  init: function(val) {
    this._super(val);
  },
  method1: function(z) {
    this._super('y', z);
  },
  method2: function(z) {
    this._super('y', z);
  },
  method3: function(z) {
    this._super('y', z);
  }
});
</script>
<script>
// DNW InheritWith2
//Fork me on GitHub! https://github.com/dotnetwise/Javascript-FastClass
//creator is the function that returns the custom methods in the new prototype.
// Sets the returned object {}.__proto__ = base. Not all the browsers support this (see IE<=10) For that matter we'll fallback to inheritWith on IE! So IE tests for inheritWith2 are irrelvant.
var DNW_IW2_A = AA();
var DNW_IW2_B = DNW_IW2_A.inheritWith2(function(base, baseCtor) {
  return {
    constructor: function(val) {
      baseCtor.call(this, val);
    },
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    },
    method2: function(y, z) {
      base.method2.call(this, 'x', y, z);
    },
    method3: function(y, z) {
      base.method3.call(this, 'x', y, z);
    }
  }
});

var DNW_IW2_C = DNW_IW2_B.inheritWith2(function(base, baseCtor) {
  return {
    constructor: function C(val) {
      baseCtor.call(this, val);
    },
    method1: function(z) {
      base.method1.call(this, 'y', z);
    },
    method2: function(z) {
      base.method2.call(this, 'y', z);
    },
    method3: function(z) {
      base.method3.call(this, 'y', z);
    }
  }
});
</script>
<script>
// 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. 
// sets `function(base, baseCtor).prototype = base` before calling the function. We append new prototype members to this, one by one
var DNW_FC_A = AA();
var DNW_FC_B = DNW_FC_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);
  };
  this.method2 = function(y, z) {
    base.method2.call(this, 'x', y, z);
  };
  this.method3 = function(y, z) {
    base.method3.call(this, 'x', y, z);
  };
});
var DNW_FC_C = DNW_FC_B.fastClass(function(base, baseCtor) {
  this.constructor = function(val) {
    baseCtor.call(this, val);
  };
  this.method1 = function(z) {
    base.method1.call(this, 'y', z);
  };
  this.method2 = function(z) {
    base.method2.call(this, 'y', z);
  };
  this.method3 = function(z) {
    base.method3.call(this, 'y', z);
  };
});
</script>
<script>
// Native
var Native_A = AA();
var Native_B = function(val) {
    Native_A.call(this, val);
    };

function __() {};
__.prototype = Native_A.prototype;
Native_B.prototype = new __();
Native_B.prototype.constructor = Native_B;
Native_B.prototype.method1 = function(y, z) {
  Native_A.prototype.method1.call(this, 'x', y, z);
};
Native_B.prototype.method2 = function(y, z) {
  Native_A.prototype.method2.call(this, 'x', y, z);
};
Native_B.prototype.method3 = function(y, z) {
  Native_A.prototype.method3.call(this, 'x', y, z);
};
var Native_C = function(val) {
    Native_B.call(this, val);
    };
__.prototype = Native_B.prototype;
Native_C.prototype = new __();
Native_C.prototype.constructor = Native_C;
Native_C.prototype.method1 = function(z) {
  Native_B.prototype.method1.call(this, 'y', z);
};
Native_C.prototype.method2 = function(z) {
  Native_B.prototype.method2.call(this, 'y', z);
};
Native_C.prototype.method3 = function(z) {
  Native_B.prototype.method3.call(this, 'y', z);
};
</script>
<script>
// TypeScript
var __extends = this.__extends ||
function(d, b) {
  function __() {
    this.constructor = d;
  }
  __.prototype = b.prototype;
  d.prototype = new __();
};
var TS_A = (function() {
  function TS_A(val) {
    this.val = val;
  }
  TS_A.prototype.method1 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  };
  TS_A.prototype.method2 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  };
  TS_A.prototype.method3 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  };
  return TS_A;
})();
var TS_B = (function(_super) {
  __extends(TS_B, _super);

  function TS_B(val) {
    _super.call(this, val);
  }
  TS_B.prototype.method1 = function(y, z) {
    _super.prototype.method1.call(this, 'x', y, z);
  };
  TS_B.prototype.method2 = function(y, z) {
    _super.prototype.method2.call(this, 'x', y, z);
  };
  TS_B.prototype.method3 = function(y, z) {
    _super.prototype.method3.call(this, 'x', y, z);
  };
  return TS_B;
})(TS_A);
var TS_C = (function(_super) {
  __extends(TS_C, _super);

  function TS_C(val) {
    _super.call(this, val);
  }
  TS_C.prototype.method1 = function(z) {
    _super.prototype.method1.call(this, 'y', z);
  };
  TS_C.prototype.method2 = function(z) {
    _super.prototype.method2.call(this, 'y', z);
  };
  TS_C.prototype.method3 = function(z) {
    _super.prototype.method3.call(this, 'y', z);
  };
  return TS_C;
})(TS_B);
</script>
<script>
// DNW IW
//Fork me on GitHub! https://github.com/dotnetwise/Javascript-FastClass
//creator is the function that returns the custom methods in the new prototype. 
var DNW_IW_A = AA();
var DNW_IW_B = DNW_IW_A.inheritWith(function(base, baseCtor) {
  return {
    constructor: function(val) {
      baseCtor.call(this, val);
    },
    method1: function(y, z) {
      base.method1.call(this, 'x', y, z);
    },
    method2: function(y, z) {
      base.method2.call(this, 'x', y, z);
    },
    method3: function(y, z) {
      base.method3.call(this, 'x', y, z);
    }
  }
});

var DNW_IW_C = DNW_IW_B.inheritWith(function(base, baseCtor) {
  return {
    constructor: function C(val) {
      baseCtor.call(this, val);
    },
    method1: function(z) {
      base.method1.call(this, 'y', z);
    },
    method2: function(z) {
      base.method2.call(this, 'y', z);
    },
    method3: function(z) {
      base.method3.call(this, 'y', z);
    }
  }
});
</script>
      
<script>
Benchmark.prototype.setup = function() {
  
  //Creates 500 instances of A, B and C and calls .method1, .method2 and .method3 on them
  
  function RunTests() {
    for (i = 500; i--;) {
      var a = new A("a");
      a.method1("x", "y", "z");
      a.method2("x", "y", "z");
      a.method3("x", "y", "z");
      var b = new B("b");
      b.method1("y", "z");
      b.method2("y", "z");
      b.method3("y", "z");
      var c = new C("c");
      c.method1("z");
      c.method2("z");
      c.method3("z");
    }
  }

};
</script>

Preparation code output

<!-- Lava.ClassManager -->

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
DNW inheritWith
A = DNW_IW_A;
B = DNW_IW_B;
C = DNW_IW_C;
RunTests();
pending…
TypeScript
A = TS_A;
B = TS_B;
C = TS_C;
RunTests();
pending…
Native
A = Native_A;
B = Native_B;
C = Native_C;
RunTests();
pending…
DNW FastClass
A = DNW_FC_A;
B = DNW_FC_B;
C = DNW_FC_C;
RunTests();
pending…
DNW inheritWith2
A = DNW_IW2_A;
B = DNW_IW2_B;
C = DNW_IW2_C;
RunTests();
pending…
John Resig's Class
A = JRClassA;
B = JRClassB;
C = JRClassC;
RunTests();
pending…
Klass
A = KlassA;
B = KlassB;
C = KlassC;
RunTests();
pending…
Classy
A = ClassyA;
B = ClassyB;
C = ClassyC;
RunTests();
pending…
Backbone.js
A = BackboneA;
B = BackboneB;
C = BackboneC;
RunTests();
pending…
PTClass (Prototype.js)
A = PTClassA;
B = PTClassB;
C = PTClassC;
RunTests();
pending…
Fiber.js
A = FiberA;
B = FiberB;
C = FiberC;
RunTests();
pending…
Lava.ClassManager
A = LavaA;
B = LavaB;
C = LavaC;
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