Sunday, May 13, 2012

Dot vs square bracket notations in Closure

In javascript you can use square brackets and dot notation almost interchangeably:

a['property'] === a.property


Which is great and you can do things like:

function createGet(property, fn) {
  A['get'+property] = fn(A[property]);
}

so you can then put getters on an object (in this case A). e.g:

A.one = 1;
A.two = 2;
var sayNumber = function(num) {return "number is: " + num;};
createGet('one', sayNumber);
createGet('two', sayNumber);
A.getOne(); // "number is : 1"
A.getTwo(); // "number is : 2"

There are some other great uses as well such as the delegateEvents in backbone.js

But unfortunately in closure we can't do this because after compilation:


a['property'] !== a.property


This is because string literals will not be compiled, but properties with dot notation will be renamed. Fear not however because we don't actually need to mix these two and it does help if you think about these not being the same.

If you have a look at plastronjs you'll notice that setting a schema will look like:

var schema = {
  'prop1' : {
    get: function ...
    set: function ...
  },
  'prop2' : {
    get: function ...
    set: function ...
  }
};

so the properties have strings and the get and set don't. Closure will rename strings but not properties. The reason they are like this is that property will be called on using model.set() and model.get() that take a string as the first argument and those can either be sent or received through a sync. So basically prop1 and prop2 can be EXTERNAL. get and set are only ever used inside our program so they are INTERNAL.

so how about the options object that gets passed to a model? it takes:

'attr', 'sync', 'schema' etc. Why are those in quotes? Well the fact is that I've allowed you to just pass through ordinary properties at the top level, so who is to say when those are compiled (sat attr -> a) that you haven't defined 'a' as a property? I'm allowing you to mix external and internals so to be safe I have to use the quotes. In doing that I then have to refer to the options object for object['attr'] instead of object.attr so we're losing a little compiled space but making things much easier to pass through attributes.

Just to make things easier I've also given you a special method in mvc.Model which will allow you to get around typing the string every time you need an attribute. What we can do is bind a function to that attribute to make things easier. Say we have an attribute 'star' that is either true or false. If I reuse it a lot, instead of doing model.get('star') I'd like something a bit easier. mvc.Model#getBinder to the rescue. Now you can do this:

var star = model.getBinder('star');
star(); // true
star(false);
star(); // false

The getBinder method is simple and looks like this:

mvc.Model.prototype.getBinder = function(key) {
  return goog.bind(function(val) {
    if (goog.isDef(val)) {
      this.set(key, val);
    } else {
      return this.get(key);
    }
  }, this);
};

It will allow you to get and set through a function whose name can be compiled and also makes it easy to call.

So what is the moral of the story? Well there are three:

  1. If a property is visible externally or is mixed with external properties that use square brackets
  2. If a property is internal only to the project then use dot
  3. If you're using a lot of square brackets think about binding it to a function

Keep these in mind when you start and after a bit of practice you won't miss being able to mix notations, and it will certainly help you keep down differences between the compiled and uncompiled versions of your code.

1 comment:

  1. Hiya! I am curious if you have a lot of visitors of this weblog?

    ReplyDelete