Despite the title, this is mostly about Coffeescript. I read a post a while ago about how an author’s Javascript has improved because he was using Coffeescript. I can’t currently find the post, but I found many similar sentiments and I would also agree with the thought. Recently I was working on a somewhat larger project for me in Coffeescript and it actually help me discover a new pattern to program in.

My preferred way to program in Javascript is to use objects heavily, using object literals and prototypical inheritance. I think trying to make it look like a pseudo classically inheritance language is fine, but I really like expressing my code in an object literal instead of a constructor function and calling the “new” operator on it to create an object. Coffeescript does have some nice stuff to make this prettier, but I still prefer to avoid the “new” operator.

Here’s a quick example of how I might choose to write something simple in JS.

var person = {
  name: "Keller",
  greet: function() {
    console.log("Hello " + this.name + "!");
  }
};
person.greet(); // Hello Keller!

I’ve frequently used the what I’ve seen called the “reveal” Javascript pattern for organizing Javascript and hiding “private” attributes and methods. It looks something like this:

var person = (function() {
  var name;

  var greet = function() {
    console.log("Hello " + name + "!");
  };

  var set_name = function(new_name) {
    name = new_name;
    update_db(new_name);
  };

  var update_db = function(new_name) {
    //ajax call to db
  };

  return {
    greet: function() {
      greet();
    },
    set_name: function(new_name) {
      set_name(new_name);
    }
  }
})();

person.set_name("Keller");
person.greet(); // Hello Keller!
person.name; // undefined
person.update_db('evil'); //error "person has no method 'update_db'"

If you haven’t seen this before, what’s happening is person is being set to a self-calling function. The function immediately calls itself and returns an object with 2 methods. These methods access functions in the self-calling function. This method is nice because the variables and functions defined in the function lose their scope outside of the function, but the object returned by it has access to them, so you sorta can have private attributes and methods in an object.

Here’s what it looks like though in CoffeeScript:

person = (() ->
  name = ''

  greet = () ->
    console.log("Hello #{name}!")

  set_name = (new_name) ->
    name = new_name
    update_db(new_name)

  update_db = (new_name) ->
    #ajax call to db

  {
    greet: () ->
      greet()
    set_name: (new_name) ->
      set_name(new_name)
  }
)()

person.set_name("Keller")
person.greet() # Hello Keller!

So, that’s not bad looking. It’s arguable not any worse looking than the Javascript equivalent. But I feel like one of the great things about Coffeescript is that it makes your JS beautiful. There were a couple of things that I thought were a little ugly. First was the person = (() -> line. I like how CS does functions, but in this case, it looks a little confusing to me. The next thing that was a little ugly, was the name = '' line. I need to declare it outside of the functions that use it, but there really is no way (correct me if I’m wrong), to simply declare a variable in CS. I have to set or use it. And that can get really ugly if you have a lot of variables like this. In JS I can do something like var name, age, sex, state, country; all in one line. CS makes that longer and more tedious. One last thing that kind of bugged me is the “private” variables and methods aren’t really object attributes and methods at all. That’s ok with me in JS, but in CS, it means I can’t use the handy @ shortcut for this. :(

However, I discovered a different way to do something very similar that is much prettier in Coffeescript. I’ll show it in Javascript first.

(function() {
  var person = {
    greet: function() {
      console.log("Hello " + this.name + "!");
    },
    set_name: function(name) {
      this.name = name;
      this.update_db(name);
    },
    update_db: function(name) {
      //ajax call to db
    }
  }

  this.person = {
    greet: function() {
      person.greet();
    },
    set_name: function(name) {
      person.set_name(name);
    }
  }
}).call(this);

person.set_name("Keller");
person.greet(); // Hello Keller!

This works essentially the same way, except rather than returning an object from inside a closure and setting it to variable outside of the self-executing function’s scope, it sets the object to the an object that already exists outside the function, which in this case is this. One thing that may be confusing is the this that this.person is referring to is the same this that is being used in the last line’s .call(this) which is the window object. All other uses of this are for the person object.

So here’s the nice part about how Coffeescript works. By default, it wraps everything in a self-executing function to protect the variables scope. So you don’t need to write that part of it. Here’s what it looks like in Coffeescript.

person =
  greet: () ->
    console.log "Hello #{@name}!"
  set_name: (name) ->
    @name = name;
    @update_db(name);
  update_db: (name) ->
    #ajax call to db

@person =
  greet: () ->
    person.greet();
  set_name: (name) ->
    person.set_name(name);

In another file or in the HTML:

person.set_name "Keller"
person.greet() # Hello Keller!

Beautiful, right?! Maybe it looks a little funny because it looks like I’m creating two versions of the person object, and that’s kind of true. One is a private version, and the other is a public version. The public version shouldn’t really do anything but expose parts of the private version of the object. It’s a lot like the “reveal” pattern. This is what I ended up using as my design pattern recently and I think it worked and looked great.

One last thing. I did “discover” this on my own. But, it’s really not that much different than what is out there, and I’m pretty sure others have discovered and used this before too. I’m probably not that original. But it was fun to think of something new (to me).



Published

12 December 2012

Tags

comments powered by Disqus