JavaScript Object Oriented Libraries Benchmark (v112)

Revision 112 of this benchmark created on


Description

=== FULL TEST ==

MooTools and Ext Core are removed because they add extra information into native classes. They slow down other libraries.

Ext Core OOP is fast, MooTools OOP is super slow! *My Own custom jsface TODO: - Add YUI

Preparation HTML

<script>
  /*
 * JSFace Object Oriented Programming Library
 * https://github.com/tnhu/jsface
 *
 * Copyright (c) 2009-2012 Tan Nhu
 * Licensed under MIT license (https://github.com/tnhu/jsface/blob/master/LICENSE.txt)
 */
(function(context, OBJECT, NUMBER, LENGTH, toString, version, undefined, oldClass, jsface) {
  "use strict";

  /**
   * Check an object is a map or not. A map is something like { key1: value1, key2: value2 }.
   * @param obj object to be checked
   * @return true if object is a map
   */
  function isMap(obj) { return (obj && typeof obj === OBJECT && !(typeof obj.length === NUMBER && !(obj.propertyIsEnumerable(LENGTH)))); }

  /**
   * Check an object is an array or not. An array is something like [].
   * @param obj object to be checked
   * @return true if object is an array
   */
  function isArray(obj) { return (obj && typeof obj === OBJECT && typeof obj.length === NUMBER && !(obj.propertyIsEnumerable(LENGTH))); }

  /**
   * Check an object is a function or not.
   * @param obj object to be checked
   * @return true if object is an function
   */
  function isFunction(obj) { return (obj && typeof obj === "function"); }

  /**
   * Check an object is a string not.
   * @param obj object to be checked
   * @return true if object is a function
   */
  function isFunction(obj) { return (obj && typeof obj === "function"); }

  /**
   * Check an object is a string not.
   * @param obj object to be checked
   * @return true if object is a string
   */
  function isString(obj) { return toString.apply(obj) === "[object String]"; }

  /**
   * Check an object is a class (not an instance of a class, which is a map) or not.
   * @param obj object to be checked
   * @return true if object is a class
   */
  function isClass(obj) { return isFunction(obj) && (obj.prototype && obj === obj.prototype.constructor); }

  /**
   * Util for extend() to copy a map of { key:value } to an object
   * @param key key
   * @param value value
   * @param ignoredKeys ignored keys
   * @param object object
   * @param iClass true if object is a class
   * @param oPrototype object prototype
   */
  function copier(key, value, ignoredKeys, object, iClass, oPrototype) {
    if ( !ignoredKeys || !ignoredKeys.hasOwnProperty(key)) {
      object[key] = value;
      if (iClass) { oPrototype[key] = value; }                       // class? copy to prototype as well
    }
  }

  /**
   * Extend object from subject, ignore properties in ignoredKeys
   * @param object the child
   * @param subject the parent
   * @param ignoredKeys (optional) keys should not be copied to child
   */
  function extend(object, subject, ignoredKeys) {
    if (isArray(subject)) {
      for (var len = subject.length; --len >= 0;) { extend(object, subject[len], ignoredKeys); }
    } else {
      ignoredKeys = ignoredKeys || { constructor: 1, $super: 1, prototype: 1, $superb: 1 };

      var iClass     = isClass(object),
          isSubClass = isClass(subject),
          oPrototype = object.prototype, supez, key, proto;

      // copy static properties and prototype.* to object
      if (isMap(subject)) { for (key in subject) copier(key, subject[key], ignoredKeys, object, iClass, oPrototype); }

      if (isSubClass) {
        proto = subject.prototype;
        for (key in proto) { copier(key, proto[key], ignoredKeys, object, iClass, oPrototype); }
      }

      // prototype properties
      if (iClass && isSubClass) { extend(oPrototype, subject.prototype, ignoredKeys); }
    }
  }

  
  /**
   * Create a class.
   * @param parent parent class(es)
   * @param api class api
   * @return class
   */
  function Class(parent, api) {
    if ( !api) parent = (api = parent, 0);                                     // !api means there's no parent

    var clazz, constructor, singleton, statics, key, bindTo, len, i = 0, p,
        ignoredKeys = ["constructor", "$singleton", "$statics", "prototype", "$super", "$superp", "main"],
        overload    = Class.overload,
        plugins     = Class.plugins;

    api         = (typeof api === "function" ? api() : api) || {};             // execute api if it's a function
    constructor = api.hasOwnProperty("constructor") ? api.constructor : 0;     // hasOwnProperty is a must, constructor is special
    singleton   = api.$singleton;
    statics     = api.$statics;

    // add plugins' keys into ignoredKeys
    for (key in plugins) { ignoredKeys.push(key); }

    // construct constructor
    clazz  = singleton ? {} : (constructor ? (overload ? overload("constructor", constructor) : constructor) : function(){});

    // determine bindTo: where api should be bound
    bindTo = singleton ? clazz : clazz.prototype;

    // make sure parent is always an array
    parent = !parent || isArray(parent) ? parent : [ parent ];

    // do inherit
    len = parent && parent.length;
    while (i < len) {
      p = parent[i++];
      for (key in p) {
        if (ignoredKeys.indexOf(key) == -1) {
          bindTo[key] = p[key];
          if ( !singleton) { clazz[key] = p[key]; }
        }
      }
      for (key in p.prototype) { if (ignoredKeys.indexOf(key) == -1) { bindTo[key] = p.prototype[key]; } }
    }

    // copy properties from api to bindTo
    
    
    for (key in api) { 
        if (ignoredKeys.indexOf(key) == -1) {
          bindTo[key] = api[key]; 
          
        }
        
    }

    // copy static properties from statics to both clazz and bindTo
    for (key in statics) { clazz[key] = bindTo[key] = statics[key]; }

    // if class is not a singleton, add $super and $superp
    if ( !singleton) {
      p = parent && parent[0] || parent;
      clazz.$super  = p;
      clazz.$superp = p && p.prototype ? p.prototype : p;
    }

    for (key in plugins) { plugins[key](clazz, parent, api); }                 // pass control to plugins
    if (isFunction(api.main)) { api.main.call(clazz, clazz); }                 // execute main()
    return clazz;
  }

  /* Class plugins repository */
  Class.plugins = {};

  /* Initialization */
  jsface = {
    version   : version,
    Class     : Class,
    extend    : extend,
    isMap     : isMap,
    isArray   : isArray,
    isFunction: isFunction,
    isString  : isString,
    isClass   : isClass
  };

  if (typeof module !== "undefined" && module.exports) {                       // NodeJS/CommonJS
    module.exports = jsface;
  } else {
    oldClass          = context.Class;                                         // save current Class namespace
    context.Class     = Class;                                                 // bind Class and jsface to global scope
    context.jsface    = jsface;
    jsface.noConflict = function() { context.Class = oldClass; }               // no conflict
  }
})(this, "object", "number", "length", Object.prototype.toString, "2.1.1");
</script>

<script src="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/my.class.js"</script>
<script src="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/my.class.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/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/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/classy.js"></script>

<script src="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/ptclass.js"</script>
<script src="http://dl.dropbox.com/u/7677927/oop-benchmark/lib/ptclass.js"</script>

Test runner

Ready to run.

Testing in
TestOps/sec
JSFace
var JSFacePerson = jsface.Class({
  constructor: function(name){
    this.name = name;
  },
  setAddress: function(country, city, street) {
    this.country = country;
    this.city = city;
    this.street = street;
  }
});

var JSFaceFrenchGuy = jsface.Class(JSFacePerson, {
  constructor: function(name) {
    JSFaceFrenchGuy.$super.call(this, name);
  },
  setAddress: function(city, street) {
    JSFaceFrenchGuy.$superp.setAddress.call(this, 'France', city, street);
  }
});

var JSFaceParisLover = jsface.Class(JSFaceFrenchGuy, {
  constructor: function(name) {
    JSFaceParisLover.$super.call(this, name);
  },
  setAddress: function(street) {
    JSFaceParisLover.$superp.setAddress.call(this, 'Paris', street);
  }
});

var p1 = new JSFacePerson("John");
p1.setAddress("US", "MT", "CH");

var p2 = new JSFaceFrenchGuy("Leo");
p2.setAddress("MT", "CH");

var p3 = new JSFaceParisLover("Mary");
p3.setAddress("CH");
ready
my.Class
var MyPerson = my.Class({
  constructor: function(name){
    this.name = name;
  },
  setAddress: function(country, city, street) {
    this.country = country;
    this.city = city;
    this.street = street;
  }
});

var MyFrenchGuy = my.Class(MyPerson, {
  constructor: function(name) {
    MyFrenchGuy.Super.call(this, name);
  },
  setAddress: function(city, street) {
    MyFrenchGuy.Super.prototype.setAddress.call(this, 'France', city, street);
  }
});

var MyParisLover = my.Class(MyFrenchGuy, {
  constructor: function(name) {
    MyParisLover.Super.call(this, name);
  },
  setAddress: function(street) {
    MyParisLover.Super.prototype.setAddress.call(this, 'Paris', street);
  }
});

var p4 = new MyPerson("John");
p4.setAddress("US", "MT", "CH");

var p5 = new MyFrenchGuy("Leo");
p5.setAddress("MT", "CH");

var p6 = new MyParisLover("Mary");
p6.setAddress("CH");
ready
John Resig Class
var JRPerson = JRClass.extend({
  init: function(name){
    this.name = name;
  },
  setAddress: function(country, city, street) {
    this.country = country;
    this.city = city;
    this.street = street;
  },
  sayHello: function() {
    console.log('I am ' + this.name + '. My address is ' +
        this.country + ', ' + this.city + ', ' + this.street + '.');
  }
});

var JRFrenchGuy = JRPerson.extend({
  init: function(name) {
    this._super(name);
  },
  setAddress: function(city, street) {
    this._super('France', city, street);
  }
});

var JRParisLover = JRFrenchGuy.extend({
  init: function(name) {
    this._super(name);
  },
  setAddress: function(street) {
    this._super('Paris', street);
  }
});

var p7 = new JRPerson("John");
p7.setAddress("US", "MT", "CH");

var p8 = new JRFrenchGuy("Leo");
p8.setAddress("MT", "CH");

var p9 = new JRParisLover("Mary");
p9.setAddress("CH");
ready
Klass
var EnderPerson = klass(function(name) {
  this.name = name;
})
.methods({
  setAddress: function(country, city, street) {
    this.country = country;
    this.city = city;
    this.street = street;
  }
});

var EnderFrenchGuy = EnderPerson.extend(function(name) {
})
.methods({
  setAddress: function(city, street) {
    this.supr('France', city, street);
  }
});

var EnderParisLover = EnderFrenchGuy.extend(function(name) {
})
.methods({
  setAddress: function(street) {
    this.supr('Paris', street);
  }
});

var p10 = new EnderPerson("John");
p10.setAddress("US", "MT", "CH");

var p11 = new EnderFrenchGuy("Leo");
p11.setAddress("MT", "CH");

var p12 = new EnderParisLover("Mary");
p12.setAddress("CH");
ready
Classy
var ClassyPerson = Classy.$extend({
  __init__: function(name){
    this.name = name;
  },
  setAddress: function(country, city, street) {
    this.country = country;
    this.city = city;
    this.street = street;
  }
});

var ClassyFrenchGuy = ClassyPerson.$extend({
  __init__: function(name) {
    this.$super(name);
  },
  setAddress: function(city, street) {
    this.$super('France', city, street);
  }
});

var ClassyParisLover = ClassyFrenchGuy.$extend({
  __init__: function(name) {
    this.$super(name);
  },
  setAddress: function(street) {
    this.$super('Paris', street);
  }
});

var p13 = new ClassyPerson("John");
p13.setAddress("US", "MT", "CH");

var p14 = new ClassyFrenchGuy("Leo");
p14.setAddress("MT", "CH");

var p15 = new ClassyParisLover("Mary");
p15.setAddress("CH");
ready
PTClass
var PTClassPerson = PTClass.create({
  initialize: function(name){
    this.name = name;
  },
  setAddress: function(country, city, street) {
    this.country = country;
    this.city = city;
    this.street = street;
  }
});

var PTClassFrenchGuy = PTClass.create(PTClassPerson, {
  initialize: function($super, name) {
    $super(name);
  },
  setAddress: function($super, city, street) {
    $super('France', city, street);
  }
});

var PTClassParisLover = PTClass.create(PTClassFrenchGuy, {
  initialize: function($super, name) {
    $super(name);
  },
  setAddress: function($super, street) {
    $super('Paris', street);
  }
});

var p16 = new PTClassPerson("John");
p16.setAddress("US", "MT", "CH");

var p17 = new PTClassFrenchGuy("Leo");
p17.setAddress("MT", "CH");

var p18 = new PTClassParisLover("Mary");
p18.setAddress("CH");
ready
CoffeeScript
var CSFrenchGuy, CSParisLover, CSPerson, p19, p20, p21,
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };

CSPerson = (function() {

  CSPerson.name = 'CSPerson';

  function CSPerson(name) {
    this.name = name;
  }

  CSPerson.prototype.setAddress = function(country, city, street) {
    this.country = country;
    this.city = city;
    return this.street = street;
  };

  return CSPerson;

})();

CSFrenchGuy = (function(_super) {

  __extends(CSFrenchGuy, _super);

  CSFrenchGuy.name = 'CSFrenchGuy';

  function CSFrenchGuy(name) {
    this.name = name;
    CSFrenchGuy.__super__.constructor.call(this, name);
  }

  CSFrenchGuy.prototype.setAddress = function(city, street) {
    return CSFrenchGuy.__super__.setAddress.call(this, 'France', city, street);
  };

  return CSFrenchGuy;

})(CSPerson);

CSParisLover = (function(_super) {

  __extends(CSParisLover, _super);

  CSParisLover.name = 'CSParisLover';

  function CSParisLover(name) {
    this.name = name;
    CSParisLover.__super__.constructor.call(this, name);
  }

  CSParisLover.prototype.setAddress = function(street) {
    return CSParisLover.__super__.setAddress.call(this, 'Paris', street);
  };

  return CSParisLover;

})(CSFrenchGuy);

p19 = new CSPerson("John");

p19.setAddress("US", "MT", "CH");

p20 = new CSFrenchGuy("Leo");

p20.setAddress("MT", "CH");

p21 = new CSParisLover("Mary");

p21.setAddress("CH");
ready

Revisions

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