4

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.

S. Imp
  • 2,833
  • 11
  • 24
  • Do you have the same error if you use the following syntax: `function myclass(p1, p2) {`? – trincot Mar 01 '18 at 17:38
  • @trincot yes the error is the same, as can easily [verify yourself](https://closure-compiler.appspot.com/). – S. Imp Mar 01 '18 at 18:49

1 Answers1

3

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.

Randy Casburn
  • 13,840
  • 1
  • 16
  • 31
  • if you had tried your own suggestion, you'd see that it doesn't work. The output? **Your code compiled to down to 0 bytes. Perhaps you should export some functions?** – S. Imp Mar 01 '18 at 18:51
  • 1
    This is the correct answer. If you use ADVANCED mode there is a separate issue - the constructor is never used therefore it is removed as dead code. – Chad Killingsworth Mar 01 '18 at 22:44
  • @S.Imp - Um - I used your partial snippet to demonstrate what the docs say. But I'll defer to Chad KillingsWorth's valuable addition on the issue. (note to self - don't try to help ungrateful people). – Randy Casburn Mar 02 '18 at 02:50
  • @S.Imp - I'll also provide _another_ link to he docs in case you missed the first reference: https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler – Randy Casburn Mar 02 '18 at 02:51
  • @randy-casburn I tried the exact code in your post where it says "so with your example code try this" and the output was zero bytes. The suggested code in your post is NOT the correct answer. If the correct answer is in the docs, and you want to edit your response, I'd be happy to upvote it. – S. Imp Mar 02 '18 at 03:06
  • @RandyCasburn thank you for your persistence and your edit. That seems like a really nasty bug/feature of this compiler that you have to call a constructor to export it. I'm so disappointed! For the record, I tried to add @\export and @\public declarations in the comment and still obtained zero byte output. – S. Imp Mar 02 '18 at 03:13
  • @RandyCasburn I've upvoted your response. In the interest of clarity, if you'll move that edit to the top of the answer and more clearly indicate the very regrettable 'dead code' problem, i'll mark it as the answer. I also personally think you should remove the *Closure compiler is great tool* bit. I have decided to install uglifier on my workstation and it's working quite nicely. – S. Imp Mar 02 '18 at 03:26
  • @ChadKillingsworth thanks for your comment. such a bummer it does that! – S. Imp Mar 02 '18 at 03:27
  • @S.Imp - please check **Edit 3** – Randy Casburn Mar 02 '18 at 03:28
  • @S.Imp - changes made per your request. – Randy Casburn Mar 02 '18 at 04:15
  • @RandyCasburn thank you for the extra effort. I have marked your response as the answer. – S. Imp Mar 02 '18 at 05:43
  • See https://closuretools.blogspot.com/2012/09/which-compilation-level-is-right-for-me.html for the differences in compilation modes. – Chad Killingsworth Mar 02 '18 at 12:25