Wednesday, July 11, 2012

Who's var is it, anyway?


Consider the following snippet of JavaScript code:
var cells = document.getElementsByTagName('td');

for(var i=0; i<cells.length; i++){
  var cell = cells[i];
  cell.addEventListener('click', function(){
    cell.style.backgroundColor = '#00F'; // which 'cell' ?
  }, false);
}
Does the click listener function do what it’s supposed to? Why or why not?

Closures and Scoping and Vars – Oh, my!

If you’ve gotten this far, it’s probably safe to say you’re aware of the fundamentals of the var keyword. It’s also likely that its use has been hammered into your consciousness as a “best practice” coding habit, and that’s a Good Thing™ (polluting the globe and the global namespace are equally heinous).

So, you follow this advice faithfully, and always declare your variables before you use them:
function foo(arg) {
  var a = 0;

  if( arg ) {
    var b = ‘arg: ‘ + arg;
    console.log( b );
  }
}

Here’s the caveat: unlike other C-style languages, JavaScript variables are NOT block-scoped; they are only global- or function-scoped. Those curly braces provide no protection for your poor vars.

What does this mean? I can update the previous code like this:
function foo(arg) {
  var a = 0;

  if( arg ) {
    var b = ‘arg: ‘ + arg;
    console.log( b );
  }

  console.log( ‘still b: ‘ + b );
}

One more thing…

In JavaScript, variables can be declared after they are first used. How is this possible, you (hopefully) ask? Because of a funny thing that the language parser does, called var hoisting.
foo = ‘bar’;
var foo;

// is implicitly understood as:

var foo;
foo = ‘bar’;
Because of this, it’s a good idea to declare variables at the top of functions. In our original example, the language understands it like this:
var i, cell;
var cells = document.getElementsByTagName('td');

for(i=0; i<cells.length; i++){
  cell = cells[i];
  cell.addEventListener('click', function(){
    cell.style.backgroundColor = '#00F';
  }, false);
}
Note that because the compiler moved the var to the top of the scope (and we don’t have block-scoping in JavaScript), our click handlers are all closing on the same cell instance, which will have the value of the last table cell element at the end of the for loop, so our click handlers will all only effect the last table cell in the document!


Reference: https://developer.mozilla.org/en/JavaScript/Reference/Statements/var

No comments: