JS Inheritance Performance (v34)

Revision 34 of this benchmark created by DotNetWise on


Description

Testing widely used inheritance libraries against TypeScript (as a reference point),

  • Measuring Declaring classes performance (without benchmarking the code for the libraries themselves).
  • For testing Using classes performance please see this: http://jsperf.com/js-inheritance-performance/35

DNW .fastClass, DNW .inheritWith and Native. All other libraries are way too weak to compete with these. http://jsperf.com/js-inheritance-performance/36

MooTools and Ext Core are removed because they add extra information into native classes. They slow down other libraries. Ext Core OOP is fast (about 15-25% slower than native), MooTools OOP is super slow!

Fiber js is about 50% or even slower -> http://jsperf.com/js-inheritance-performance/33

Preparation HTML

<script src="http://code.jquery.com/jquery-1.9.1.min.js"></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 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

///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;
    }
    
    //Creates 1 instances of A, B and C and calls .method1, .method2 and .method3 on them
    
    function RunTests() {
        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");
    }

Test runner

Ready to run.

Testing in
TestOps/sec
DNW inheritWith
//Fork me on GitHub! https://github.com/dotnetwise/Javascript-FastClass
//creator is the function that returns the custom methods in the new prototype. 
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);
    },
    method2: function(y, z) {
      base.method2.call(this, 'x', y, z);
    },
    method3: function(y, z) {
      base.method3.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);
    },
    method2: function(z) {
      base.method2.call(this, 'y', z);
    },  
    method3: function(z) {
      base.method3.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;
  };
  A.prototype.method2 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  };
  A.prototype.method3 = 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);
  };
  B.prototype.method2 = function(y, z) {
    _super.prototype.method2.call(this, 'x', y, z);
  };
  B.prototype.method3 = function(y, z) {
    _super.prototype.method3.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);
  };
  C.prototype.method2 = function(z) {
    _super.prototype.method2.call(this, 'y', z);
  };
  C.prototype.method3 = function(z) {
    _super.prototype.method3.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);
};
B.prototype.method2 = function(y, z) {
  A.prototype.method2.call(this, 'x', y, z);
};
B.prototype.method3 = function(y, z) {
  A.prototype.method3.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);
};
C.prototype.method2 = function(z) {
  B.prototype.method2.call(this, 'y', z);
};
C.prototype.method3 = function(z) {
  B.prototype.method3.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. 
// sets `function(base, baseCtor).prototype = base` before calling the function. We append new prototype members to this, one by one
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);
  };
  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 C = 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);
  };
});
RunTests();
ready
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 A = AA();
var B = 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 C = 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);
    }
  }
});
RunTests();
ready

Revisions

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