3

I have an object declared like this:

my.namespace.FEATURES = {
    FIRST_FEATURE = "first feature",
    SECOND_FEATURE = "second feature"
};

I use my.namespace.my.object to keep track of what kinds of features are available/implemented in my code. Every newly released version will have a modified set of features. A third-party using my minimized code will want to know what they can do in the version they have, so I supply the following function, which is exported, so that they know what they can do.

my.namespace.hasFeature = function(feature) {
    for(var prop in my.namespace.FEATURES) {
        if(my.namespace.FEATURES[prop] == feature) {
            return true;
        }
    }
    return false;
}

The problem is that the properties are getting renamed when I run Closure Compiler.

My question is: what's the best way to keep those properties preserved? I know I can export the property, but it feels kind of dirty for some reason. Is there a Closure best practice to preserve the properties of an object?

boo-urns
  • 10,136
  • 26
  • 71
  • 107

3 Answers3

5

The Closure Compiler has JavaDoc style markup...
Here's the page referencing the markups: Annotating JavaScript for the Closure Compiler
To retain a field name, place /** @expose */ before the field declaration.

my.namespace.FEATURES = {
    /**@expose*/
    FIRST_FEATURE: "first feature",
    /**@expose*/
    SECOND_FEATURE: "second feature"
};

... if you need to retain the namespace, it's the same concept...

/**@expose*/
my = my || {};
/**@expose*/
my.namespace = my.namespace || {};
/**@expose*/
my.namespace.FEATURES = {
    /**@expose*/
    FIRST_FEATURE: "first feature",
    /**@expose*/
    SECOND_FEATURE: "second feature"
};

In addition to retaining the filed name, it also allows you to reference that field using dot-notation later in your code. Using obj["field"], the compiler will lose the reference if you call it with obj.field later instead of having to use strings.

Duncan
  • 1,530
  • 15
  • 20
  • I believe you mean `@export` (not `@expose`). – Karl Nov 11 '14 at 18:45
  • No, I mean `@expose`... this tells the compiler to retain the name. – Duncan Nov 11 '14 at 23:39
  • 1
    Interesting, because `@expose` is not documented at the link you provided. But I now see the accepted answer [here](http://stackoverflow.com/questions/10376025/javascript-closure-compiler-exporting-global-variables) mentions `@expose` as being undocumented. This is useful. Thanks. – Karl Nov 12 '14 at 14:11
  • Ya, after you made your comment, I took another look and noticed it wasn't there. I apologize for that. The link is still useful though. :) – Duncan Nov 13 '14 at 10:59
4

In ADVANCED mode simply quoting the keys tells the compiler not to renamed them:

my.namespace.FEATURES = {
    'FIRST_FEATURE' : "first feature",
    'SECOND_FEATURE' : "second feature"
};

Useful information about property renaming:

https://developers.google.com/closure/compiler/docs/api-tutorial3

https://github.com/google/closure-compiler/wiki/FAQ#some-of-my-properties-are-getting-renamed-but-some-arent-why

daniel
  • 638
  • 4
  • 14
John
  • 5,443
  • 15
  • 21
  • Ah, this is interesting. Will this guarantee that FIRST_FEATURE and SECOND_FEATURE remain properties of the my.namespace.FEATURES object? My other worry is that the namespace will get minimized further into something like: FEATURES$FIRST_FEATURE, which would break my hasFeature function. – boo-urns Mar 12 '12 at 21:06
  • It says nothing about the object that properties are assigned to. To prevent the renaming of the namespace you need to export it. For instance you can do this: window['my']['namespace']['FEATURES'] = { ... This prevents any renaming of the namespace objects. This is simply a direct (or "raw") export. – John Mar 12 '12 at 22:38
  • I realized, I misread your question. Yes, this will prevent them from being collapsed. – John May 03 '12 at 19:07
2

Exporting is there for a reason - as a directive to the Closure library that this is used by outside agents so it should not be renamed. The sections here on exporting explain how you can force Closure to keep a symbol intact (with no renaming). You mostly just need to follow the directions here. There is nothing "dirty" about exporting. It is there for exactly what you need - to tell Closure that this symbol is used by an external agent and cannot be renamed.

The other trigger that can keep Closure from renaming a property is if it is accessed by a string like this:

var f = "FIRST_FEATURE";
my.namespace.FEATURES[f] = "first feature";

In that case Closure sees that your code is using strings to address a property and (since it never messes with string values) it realizes that it can't rename the FIRST_FEATURE property safely.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • A related question: do these methods guarantee that the properties don't get flattened into something like FEATURES$FIRST_FEATURE? – boo-urns Mar 12 '12 at 21:29
  • 1
    @DarrenGreen - Property flattening is an advanced form of property renaming so if there is no renaming, then there can be no flattening. The easiest thing to remember is that Closure never touches strings so if it sees you accessing a property via a string, it knows that it can't do anything to that property. – jfriend00 Mar 12 '12 at 21:31
  • Thanks very much! That's exactly the information I was looking for. – boo-urns Mar 12 '12 at 21:49