JS Hacks: Accessing Variables in a Hidden Scope

Modular application structure is great for loosely coupling code to make it more reusable and more maintainable, always has been, always will be.

However, encapsulation has typically flown in the face of cross-cutting concerns like analytics and logging, which for any production web app is absolutely essential. There are a few JavaScript AOP type libraries out there (based on a quick Google search) but in JavaScript we don’t have a class definition lookup like in ActionScript or the forceful reflection APIs that allow us to bust open private scopes like in Ruby.

The idea here is we’d like to be able to track events that happen in our application, along with the associated detailed information, while littering our application code with metrics crap as little as possible. For example, I’d much rather see this:

Analytics.track('signUpComplete');

than:

Analytics.track('signUpComplete', { username: username, browser: 'Chrome 11', donkeys: isItADonkey() });

the point being that I don’t want my application code to care about what the metrics code needs. One solution is to create and pass an accessor function that captures the scope you want to pull additional information from and pass that accessor function to the external module that needs the additional access.

In your application code:

var accessor = function(name){ try{ return eval(name); }catch(e){ /* log error */ return null; } };
Analytics.track('signUpComplete', accessor);

and in your other module’s code:

var eventsSpec = {
     'signUpComplete': {
          username: 'path.to.username'
          , browser: 'path.to.browser'
          , donkeys: 'isItADonkey()'
     }
};

var track = function(eventName, accessor){
    var capturedProperties = {};
    for(var key in eventsSpec[eventName]){
        capturedProperties[key] = accessor(eventsSpec[eventName][key]);
    }
    // fire off call to google analytics or whatever
};

While this means that your metrics code is tightly coupled to your application code and the way it is structured there isn’t really any way around that, the data lives where it lives and you need to get it. On the other hand, your application code is not tightly coupled to your metrics code, which is the goal, sweet!

Copy/paste the lines below into your browser’s console to see a full featured example of accessing properties, property chains and functions in the various closures:

var globalScopeObject = {
     prop: 'dot accessor in outer block scope'
};

var globalFunction = function() {
     return 'global function call';
};

var wrapped = function(){
     var outerBlockScope = 'this is outer block scope';
     var outerBlockScopeObject = {
          prop: 'dot accessor in outer block scope'
     };

     var outerBlockFunction = function() {
          return 'outer block function call';
     };

     return function(name){
          var innerBlockScope = 'this is inner block scope';
          var innerBlockScopeObject = {
               prop: 'dot accessor in inner block scope'
          };
          var innerBlockFunction = function() {
               return 'inner block function call';
          };

          try{
               return eval(name);
          }catch(e){
               if(typeof console != 'undefined'){
                    console.log(e);
               }
               return null;
          };
     };
};

var test = wrapped();

console.log(test('globalScope'));
console.log(test('outerBlockScope'));
console.log(test('innerBlockScope'));
console.log(test('globalScopeObject.prop'));
console.log(test('outerBlockScopeObject.prop'));
console.log(test('innerBlockScopeObject.prop'));
console.log(test('globalFunction()'));
console.log(test('outerBlockFunction()'));
console.log(test('innerBlockFunction()'));

Try out it and let me know!

1 comment

Leave a Reply

Your email address will not be published. Required fields are marked *

*

code

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>