Search code examples
javascriptprototypegoogle-closure-compilermodule-pattern

Javascript Module pattern, Prototype and Google Closure


I am having trouble getting this code structure to survive obfuscation with the Google Closure Compiler. Here's some sample code:

var MyModule = (function()
{   
    function myModule()
    {
        // Constructor
    }

    function moduleFoo(url)
    {
        // Method
    }

    function moduleBar()
    {
        // Method
    }

    myModule.prototype = {
        constructor: myModule,
        foo: moduleFoo,
        bar: moduleBar
    };

    return myModule;

})();

Elsewhere in my code I need be able to write things like the following:

var myMod = new MyModule();
myMod.foo();
myMod.bar();

However the compiler is renaming everything (as expected). How can I make the prototype that I have defined available elsewhere in my code after obfuscation? I have tried exporting as follows:

// In place of the prototype object above
myModule.prototype['constructor'] = myModule;
myModule.prototype['foo'] = moduleFoo;
myModule.prototype['bar'] = moduleBar;

window['myModule'] = myModule;

But things seem to break down either when the prototype methods are called or when their corresponding closures are executed.

Any help is appreciated.


Solution

  • This exact pattern does not work well with Closure-compiler using ADVANCED_OPTIMIZATIONS. Instead, you will need to slightly refactor your code:

    /** @constructor */
    function MyModule()
    {
        // Constructor
    }
    
    (function() {
        function moduleFoo(url)
        {
            // Problem using "this" keyword. Will require @this annotation.
        }
    
        MyModule.prototype = {
            foo: moduleFoo
        };
    
        MyModule.prototype.bar =  function() {
            // "this" keyword works fine.
        };
    })();
    

    Or like:

    /** @const */
    var MyNamespace = {};
    
    (function() {
        /** @constructor */
        MyNamespace.MyModule = function() {};
    
        MyNamespace.MyModule.prototype = {
            constructor: function() {},
            foo: function(url) {},
            bar: function() {}
        };
    })();
    

    With either of the above methods your exports should work correctly.

    Note: The second option will only work with a compiler built from the latest source as it involves a bug that was just fixed last week.