Search code examples
javascriptgoogle-closure-compileruglifier

old-school JavaScript classes and advanced JS minification issue


I've got a fairly elaborate class which uses the old-style way of creating classes. Rather than using the class keywords, classes are defined using the function keyword. To create public properties, you assign this.publicProperty. My code works quite well.

However, when I try to use the Closure Compiler (a JS minifier tool) in advanced mode, I get bazillions of errors, starting with one about 'dangerous' references to this. Here's a trivial example which illustrates my problem. I try to compress this trivial JS code:

var myclass = function(p1, p2) {
    this.publicProperty = null;
};

The Compiled Code output is empty and there's a warning:

JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 2 character 2
this.publicProperty = null;
^

Why is this 'dangerous' ?

I've seen other posts here on SO which clearly show that my code seems OK. Why does the Closure minifier complain about this?

As a secondary question, I'd like to know if anyone can suggest an effective browser-based JS minifier that will compress your JS down to a single line of code. I've tried Uglifier and Closure and javascript-minifier am unable to get minified JS output that's just a single line of code.


Solution

  • Summarizing the edits and discussion below: Closure compiler relies on annotations as instructions. So annotating the constructor function (per below) is a start. Next, it is also necessary to realize that any code that is not used by any of the other code you are compiling at the same time will be treated as dead code and will be removed by the compiler. This doesn't work well for custom APIs such as yours. So the final fix (also below), is to record a reference to the function on the window object using array type property access notation ( [] ).

    Edit I missed an important detail. deferring to Chad's comment below, since you are using advanced mode, Chad says: If you use ADVANCED mode there is a separate issue - the constructor is never used therefore it is removed as dead code. So please make sure you constructor is addressed within your code.

    Edit 3 To fix the issue Chad references (his name is splattered all over the bug reports!), the next step is to add your function to the global scope. It appears it resides in the global anyway, so this is not big issue (unless you correct me).

    window['myClass'] = myClass;

    ====

    To solve your problem, you must annotate your code correctly. It sounds as if that may not be the case. Here is a relevant snippet from the docs:

    @constructor

    Marks a function as a constructor. The compiler requires a @constructor annotation for any function that is used with the new keyword. @constructor should be omitted from EcmaScript class constructor methods and goog.defineClass constructor methods.

    With this code sample:

    /**
     * A rectangle.
     * @constructor
     */
    function GM_Rect() {
      ...
    }
    

    So with your example code try this:

        /**
         * @constructor
         */
        var myclass = function(p1, p2) {
            this.publicProperty = null;
        };
    

    That should resolve the issue. You may have to use a named function rather than a function expression.