Perfection kills

Exploring Javascript by example

Archives Posts

Event delegation will save the world.

February 19th, 2009 by kangax

If you ever wanted to know how many event handlers your Prototype.js-based application has, but were afraid to count, this tiny bookmarklet will gladly do that for you!

Accidentally, the snippet demonstrates an absolutely wicked use of Prototype’s sacred technique of inject over Hash.

$H(Event.cache).inject(0, function(m, p) {
  m += $H(p.value).values().flatten().size();
  return m;
});

Enjoy.

Filed under delegation, inject having 13 Comments »

Archives Posts

Namespacing made easy

January 30th, 2008 by kangax

Enumerable#inject is one scary method. It took me a while to understand the beauty of this functional programming paradigm. Just in case you were ever wondering about its real life usage – here’s one of them.

We all know that using namespaces is generally a wise thing to do. You don’t have to create a multi level object structure, but nesting your code under a pseudo-namespace is what makes maintenance, upgrades and migrations somewhat easier and error-prone.

Few popular javascript libraries implement some sort of a utility function that automatically creates nested objects. What it means is that instead of writing it manually, you could let the helper handle it for you:

 
// creating nested structure manually is painful
var com = {
  thinkweb2: {
    projects: {
      _prototype: 'a deeply nested property...'
    }
  }
}
 
com.thinkweb2.projects._prototype // => 'a deeply nested property...'

Well, it appears that inject can handle such task in only 3 lines of code:

 
'com.thinkweb2.projects.prototype'.split('.').inject(window, function(parent, child) {
  var o = parent[child] = { }; return o;
})
 
Object.inspect(com.thinkweb2.projects.prototype) // => "[object Object]"

Not bad – a completely generic namespacing snippet.

What’s happening here is not a black magic. Inject accepts initial object as a first argument (in this case – window object) and iterator function as the second argument. It then calls iterator function on each item from the collection (in our case it’s simply a string split into an array). Iterator function receives accumulated result as a first argument and current value as a second. Iterator performs an action (creates a nested object) and returns accumulated result (the newly created parent object) to be used in following iterations.

Alternatively, we could wrap this all nicely and define as a String.prototype method:

 
String.prototype.namespace = function(separator){
  this.split(separator || '.').inject(window, function(parent, child) {
    var o = parent[child] = { }; return o;
  })
}
 
foo.bar.baz.quux = 'Nothing special...'; // => ERROR: foo is not defined
 
// Default separator is '.'
'foo.bar.baz.quux'.namespace();
 
foo.bar.baz.quux = 'Nothing special...'; // => "Nothing special..."
 
// Or using a custom one
'MY_AWESOME_APPLICATION::util::DOM::dimensions'.namespace('::');
 
'dimensions' in MY_AWESOME_APPLICATION.util.DOM; // => true

Note how we use “separator” argument (or defaulting to ‘.’) making the function somewhat more flexible.

P.S.
Heavy namespacing could lead to unnecessary complexity.
Lack of it – to buggy behavior.
In the end, the only thing that matters is what works best for you.

Enjoy!

Filed under inject having 17 Comments »