Search code examples
javascriptprototypejs

Replacement for Prototype.js Class system


We have a set of classes created that depend on Prototype's Class implementation (and some Object.extend).

The problem is prototype is creating trouble when integrating with the rest of our applications (even with "noconflict" adapters and the such).

Does anybody know of a compatible Class implementation that does not mess with the global scope? Or has anybody been able to "extract" Prototype's to use it alone?


Solution

  • I wrote one a couple of years back (I should go revisit it, and give it a proper name) because I didn't like Prototype's handling of calling methods on the "superclass", which involves creating a function every time an overridden method is called (yes, really). It's very similar to Prototype's except for how you make supercalls; you can readily drop it in and search for super in your code and change it up. My implementation also makes it a bit easier to use named functions rather than anonymous ones, which is useful for many reasons, not least because it helps your tools help you. It also makes private "class" methods trivial. Details below.

    But you don't have to use mine. There are other options that will require slightly more work to migrate your code to, but probably not a lot more:

    My issue with both of them is that they use function decompilation (so does Prototype's Class stuff), and function decompilation (e.g., calling toString on a function) has never been standardized and does not work on some mobile browsers. Resig's mechanism continues to work if function decompilation doesn't work, but it adds overhead to every method in that case (rather than only ones that make supercalls). My mechanism doesn't use function decompilation at all, adds no overhead to method calls, and even makes supercalls highly-efficient.

    If you use my mechanism and your Prototype code looks like this:

    var SuperThingy = Class.create({
        foo: function(arg) {
            console.log("SuperThingy: " + arg);
            this._pseudoPrivate();
        },
        _pseudoPrivate: function() {
            console.log("I'm not really private.");
        }
    });
    var Thingy = Class.create(SuperThingy, {
        foo: function(super, arg) {
            console.log("Thingy: " + arg);
            super(arg);
        }
    });
    

    You can make minimal changes:

    var SuperThingy = Helper.makeClass({
        foo: function(arg) {
            console.log("SuperThingy: " + arg);
            this._pseudoPrivate();
        },
        _pseudoPrivate: function() {
            console.log("I'm not really private.");
        }
    });
    var Thingy = Helper.makeClass(SuperThingy, {
        foo: function(arg) {
            console.log("Thingy: " + arg);
            this.callSuper(arguments, arg);
        }
    });
    

    ...or you can make slightly larger changes and get the benefit of a speed increase (callSuper uses arguments.callee, which is slow), properly-named functions (for debugging and such), and truly private functions:

    var SuperThingy = Helper.makeClass(function() {
        function SuperThingy_foo(arg) {
            console.log("SuperThingy: " + arg);
            trulyPrivate.call(this);
        }
    
        function trulyPrivate() {
            console.log("I'm truly private.");
        }
    
        return {foo: SuperThingy_foo};
    });
    var Thingy = Helper.makeClass(SuperThingy, function() {
        function Thingy_foo(arg) {
            console.log("Thingy: " + arg);
            foo.$super.call(this, arg);
        }
    
        return {foo: Thingy_foo};
    });