Class Pattern Comparisons - Full

JavaScript performance comparison

Revision 3 of this test case created by Donald Atkinson

Info

Introduction

The ability to have classic object-oriented scoping has been a dividing topic amongst the JS/ES community. In order to accomplish various forms of OO implementation, a variety of libraries often employ complex methods and subclassing structures. While several patterns come close to mimcking the behavior, even these widely used JS class building libraries utilize substantially different tricks and cannot truly mimick classic OO scoping.

The Closed Modular Class Prototype is a single hybrid pattern that provides classic object oriented scoping and most classic inheritance features. This series of tests benchmarks as many aspects as can reasonably be performed. While the entire body of tests will be applied to this test, it may be desirable to take each test independently:

Closed Modular Class Prototype

This pattern is a hybrid pattern between the Constructor, Closure/Module, and Prototypal patterns. Each aspect of the code is used in the exact same way, but in substantially different places in order to achieve OO-scoping. Except in the case of object private members, the below is all of the code needed.

In the case of older browsers, the es5-shim for Function.prototype.Array can substantially reduce additional cross-browser boilerplate code.

The Basic Pattern

(function(Class) {
  /* Static Members and Methods Here */
    Class.create = function(){
      // This is actually a standard constructor.
    };

  /* Class Prototype Here */
    Class.prototype.class = Class;

  /* Return the Class for attachment */
    return Class;
}(function(){
     return this.class.create.call(this, arguments);
 }));

Class Creation

Class creation is simple and the same pattern may be utilized defined normally with a variable, attached to an object, or in an object expression, and even nested another closure/module with no modification.

var ClassName = (Class Pattern);
Object = {
   ClassName: (Class Pattern);
}
Namespace.ClassName = (Class Pattern);

Object Instantiation

Use the new keyword:

object = new ClassName(args);

Also allows for static creation

ClassName.create(args);

Preparation code

<!-- Shim from es5-shim: Function.prototype.apply() -->
<script>(function(){var d=!1,b=Function.prototype.apply;try{d=isNaN.apply(null,{})}catch(e){}d||(Function.prototype.apply=function(c,a){return a?"object"!==typeof a||a instanceof Array||!("length"in a)?b.call(this,c,a):b.call(this,c,Array.from(a)):b.call(this,c)})})();</script>

<script>
var ProtoObject = function(cfg) {
    var dataPrivate = 0;
    this.dataPublic = 0;
};
ProtoObject.staticMethod = function(){
   return 0;
};
ProtoObject.prototype = {
    dummy: 0,
    doSomething: function(value) {
        return value + 1;
    },
    runStaticMethod: function() {
        return ProtoObject.staticMethod();
    },
    dummyFn1: function() {
        return 0;
    },
    dummyFn2: function() {
        return 0;
    },
    dummyFn3: function() {
        return 0;
    },
    dummyFn3: function() {
        return 0;
    },
};

var ConstructedObject = function(cfg) {
    var dataPrivate = 0;

    this.dummy = 0;
    this.dataPublic = 0;

    this.doSomething = function(value) {
         return value + 1;
    };
    this.runStaticMethod = function() {
         return ConstructedObject.staticMethod();
    };
    this.dummyFn1 = function() {
        return 0;
    };
    this.dummyFn2 = function() {
        return 0;
    };
    this.dummyFn3 = function() {
        return 0;
    };
    this.dummyFn4 = function() {
        return 0;
    };
};
ConstructedObject.staticMethod = function(){
    return 0;
};

var ClassModuleObject = (function(Class) {
    var syncPrivateAccess = false;

    function staticMethodPrivate() {
            return 0;
    }
    function exposedStaticMethod() {
            return 0;
    }

    Class.staticMethod = function() {
        return 0;
    };
    Class.exposedStaticMethod = exposedStaticMethod;
   
    Class.prototype.class = Class;
    Class.prototype.doSomething = function(value) {
        return value + 1;
    };
    Class.prototype.runStaticMethod1 = function() {
        return Class.staticMethod();
    };
    Class.prototype.runStaticMethod2 = function() {
        return Class.exposedStaticMethod();
    };
    Class.prototype.runPrivateStaticMethod = function() {
        return staticMethodPrivate();
    };

    Class.prototype.getPrivateData = function() {
        var result;
        syncPrivateAccess = true;
        result = this.read();
        syncPrivateAccess = false;
        return result;
    }

    Class.create = function(cfg) {
        var dataPrivate = 50;
        function read() {
            if (syncPrivateAccess) return dataPrivate;
        }

        if (this instanceof Class) {
            this.dataPublic = 0;
            this.read = read;
        }
        else {
            return new Class(cfg);
        }
    };

// Return the Actual Class
    return Class;
}(function(cfg){
    return this.class.create.apply(this, arguments);
}));

var objTest1 = new ConstructedObject(),
    objTest2 = new ProtoObject(),
    objTest3 = new ClassModuleObject();
</script>

Preparation code output

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
new Constructor()
new ConstructedObject({});
pending…
new Prototypal()
new ProtoObject({});
pending…
new ClassModule()
new ClassModuleObject({});
pending…
Static Creation
ClassModuleObject.create({});
pending…
Constructor Static Method
objTest1.runStaticMethod();
pending…
Proto Static Method
objTest2.runStaticMethod();
pending…
Module Public Static (Method 1)
objTest3.runStaticMethod1();
pending…
Module Public Static (Method 2)
objTest3.runStaticMethod2();
pending…
(Module Only) Private Static Method
objTest3.runPrivateStaticMethod();
pending…
Public Member Access
var testVal1 = objTest2.dataPublic;
pending…
(Module Only) Private Member Access
var testVal2 = objTest3.getPrivateData();
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. Here’s a list of current revisions for this page:

0 comments

Add a comment