Editing JavaScript Object Oriented Libraries Benchmark This edit will create a new revision. Your details (optional) Name Email (won’t be displayed; might be used for Gravatar) URL Test case details Title * Published (uncheck if you want to fiddle around before making the page public) Description (in case you feel further explanation is needed)(Markdown syntax is allowed) === 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 Are you a spammer? (just answer the question) Preparation code Preparation code HTML (this will be inserted in the <body> of a valid HTML5 document in standards mode) (useful when testing DOM operations or including libraries) <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> Include JavaScript libraries as follows: <script src="//cdn.ext/library.js"></script> Define setup for all tests (variables, functions, arrays or other objects that will be used in the tests) (runs before each clocked test loop, outside of the timed code region) (e.g. define local test variables, reset global variables, clear canvas, etc.) (see FAQ) Define teardown for all tests (runs after each clocked test loop, outside of the timed code region) (see FAQ) Code snippets to compare Test 1 Title Async (check if this is an asynchronous test) Code 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"); Test 2 Title Async (check if this is an asynchronous test) Code 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"); Test 3 Title Async (check if this is an asynchronous test) Code 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"); Test 4 Title Async (check if this is an asynchronous test) Code 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"); Test 5 Title Async (check if this is an asynchronous test) Code 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"); Test 6 Title Async (check if this is an asynchronous test) Code 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"); Test 7 Title Async (check if this is an asynchronous test) Code 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");