Editing super-implementations 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) 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) 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) var F = function () {}; function createClass (name, superclass, callback) { var theClass; // create the function constructor for the class theClass = function () { // verify class is always instantiated with 'new' keyword if (!(this instanceof theClass)) { throw new Error('[' + name + '] Please use "new" keyword to instantiate class'); } this.init.apply(this, arguments); }; // Setup display name and superclass theClass.displayName = theClass._name = name; // store class in window window[name] = theClass; // inherit from the superclass unless we're processing the base class if (superclass) { F.prototype = superclass.prototype; theClass.superclass = superclass; theClass.prototype = new F(); theClass.prototype.constructor = theClass; } superclass = theClass.superclass ? theClass.superclass.prototype : {}; // invoke callback to "implement" class and process it afterwards callback(theClass); // process instance (prototype) methods of the class for (var method in theClass.prototype) { if (!theClass.prototype.hasOwnProperty(method)) { return; } processMethod(theClass, method); // if method uses .callSuper, use a closure to keep reference of the corresponding superclass' method if ((typeof superclass[method] === 'function') && /this\.callSuper3\(/.test(theClass.prototype[method])) { theClass.prototype[method] = overrideCallSuper(theClass.prototype[method], superclass[method]); } } } function processMethod (theClass, methodName) { var parent = theClass.prototype; var prop = Object.getOwnPropertyDescriptor(parent, methodName).value; // only process functions if (typeof prop !== 'function') { return; } prop._class = theClass; prop._name = methodName; if (!prop.displayName) { prop.displayName = theClass.displayName + '#' + methodName + '()'; } } function overrideCallSuper (method, superMethod) { return function () { // locally store existing callSuper to avoid overwriting it if it already exists var callSuper = this.callSuper3; var results; // override the callSuper method with the actual's superclass method // and call the actual method this.callSuper3 = superMethod; results = method.apply(this, arguments); // restore callSuper and return results this.callSuper3 = callSuper; return results; }; } createClass('Person', null, function () { Person.prototype.init = function (firstName, lastName) { this.firstName = firstName; this.lastName = lastName; }; Person.prototype.name = function (msg) { return '1. ' + this.firstName + ' ' + this.lastName; }; Person.prototype.name2 = function (msg) { return '2. ' + this.firstName + ' ' + this.lastName; }; Person.prototype.name3 = function (msg) { return '3. ' + this.firstName + ' ' + this.lastName; }; Person.prototype.callSuper = function () { // figure out what function called us var caller = Person.prototype.callSuper.caller; var parent = caller._class.superclass.prototype; var method_name = caller._name; return parent[method_name].apply(this, arguments); }; Person.prototype.callSuper2 = function (methodName) { var args = Array.prototype.slice.call(arguments); return this.constructor.superclass.prototype[methodName].apply(this, args.slice(1)); }; }); createClass('Employee', Person, function () { Employee.prototype.init = function (firstName, lastName, title) { this.firstName = firstName; this.lastName = lastName; this.title = title; }; Employee.prototype.name = function (msg) { return this.callSuper() + ' (Employed)'; }; Employee.prototype.name2 = function (msg) { return this.callSuper2('name2') + ' (Employed)'; }; Employee.prototype.name3 = function (msg) { return this.callSuper3() + ' (Employed)'; }; }); var john = new Employee('John', 'Smith', 'Assistant'); 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 john.name(); Test 2 Title Async (check if this is an asynchronous test) Code john.name2(); Test 3 Title Async (check if this is an asynchronous test) Code john.name3();