Search code examples
javascriptgoogle-closure

Generation of getters and setters in Javascript compatible with Closure Compiler


I'm writing a library that I hope to be compatible with Closure Compiler in Advanced mode. Most objects in the library maintain an internal object of attributes that are frequently part of the API, which leads to my source files being filled with lots and lots of functions like this.

/*
 * Get name.
 */
Layer.prototype.getName = function() {
    return this.attrs.name;
}
/*
 * Set name.
 */
Layer.prototype.setName = function(name) {
    this.attrs.name = name;
}

I can think of a billion ways to optimize this to declutter my code a bit. One example: KineticJS, as per this related question, does something a bit like this:

Global.addGettersSetters = function(obj, property) {
    obj['get'+property] = function() { return this.attrs[property] }
    obj['set'+property] = function(val) { this.attrs[property] = val }
}

// Later that day, in our original object, we have:

Global.addGettersSetters(Layer, 'name');

My understanding is that this is a no-no with Closure Compiler--the names won't be shortened and the functions won't be optimized because I'm specifying the properties of Layer as strings.

So, is there a way for me to fully and properly define the interface without cluttering up my code? Something in the Closure Library I've overlooked, perhaps?

An alternative solution: is there a way to do C#-style properties in modern JS? In a way Closure Compiler finds permissible? I have the luxury of targeting Webkit and only Webkit with this library, so stuff that's not yet fully implemented is fine.


Solution

  • You cannot dynamically add a function which could then be compiled (and minified/obfuscated) by the Closure Compiler because that dynamic "addGettersSetters" function would only be used at runtime, so the compiler has no knowledge of what it could be creating. The downside of using the compiler is a lot of duplicate pre-compiled code, but the benefit is that the majority of the places where your getters and setters are used will either be minified or just changed to inline references to the variables.

    Also, by putting in explicit getters/setters and properly annotating them with JsDoc annotations:

     /*
     * Set name.
     * @param {string} name
     */
    Layer.prototype.setName = function(name) {
        this.attrs.name = name;
    }
    

    you can add some level of type safety to your code to ensure you get a warning during compilation if someone calls "setName(5)".

    Otherwise I would follow Chris's suggestion and look into JS getters / setters (other reference here). I have not used these with the closure compiler though so I cannot vouch for them.