2

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.

Community
  • 1
  • 1
iameli
  • 773
  • 1
  • 6
  • 18

4 Answers4

3

If the getters/setters are public anyway, then you need them to not be renamed in the minified js. That means having them use strings for names is fine - they won't be minified but that's what you wanted.

Yes, modern JS has getters/setters.

Community
  • 1
  • 1
Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
  • I should clarify that I'm intending for this library to be compiled together with the programs that use it, I'm not just trying to define a public API. Ideally most of the getters and setters would be removed except for the few that the program uses. – iameli Apr 23 '13 at 01:07
1

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.

ne8il
  • 2,427
  • 1
  • 20
  • 18
0

Sorry, I don't get the ne8il answer and why it was marked as the correct one.

You can do what you want by just adding .prototype between obj and [ like this:

function addGettersSetters(obj, property) {
    // You can also add this if you don't want to declare attrs = {} each time
    // if (!("attrs" in obj.prototype)) obj.prototype.attrs = {};
    obj.prototype['get'+property] = function() { return this.attrs[property] }
    obj.prototype['set'+property] = function(val) { this.attrs[property] = val }
}

And also writing the property name with capital letter. Then you can use it like this:

var Layer = function() { this.attrs = {}; };
// Or just: 'var Layer = function(){};' if you use the line commented above

addGettersSetters(Layer, 'Name');

var layer = new Layer();
layer.setName("John");
alert(layer.getName()); // "John"
axelbrz
  • 783
  • 1
  • 7
  • 16
0

Not a complete answer of the original question, just adding some info.

You can see how various JavaScript OOP frameworks handle getters/setters e.g. here: jsPerf.com - JavaScript Object Oriented Libraries Benchmark with getters and setters

is there a way for me to fully and properly define the interface without cluttering up my code?

Tan Nhu (original author of the benchmark) created his own OOP library jsface which is available at: https://github.com/tnhu/jsface

I like it, I use it for exactly this reason

EDIT: how are the getters/setters generator solved in TypeScript is mentioned e.g. in SO article get and set in TypeScript

For more complete list of other frameworks and their way of encoding getters/setters you can check List of languages that compile to JS · jashkenas/coffeescript Wiki · GitHub

Community
  • 1
  • 1
xmojmr
  • 8,073
  • 5
  • 31
  • 54