Javascript scoping makes my head hurt

Written by Piers Cawley on , updated

Who came up with the javascript scoping rules? What were they smoking. Here’s some Noddy Perl that demonstrates what I’m on about: my @subs; for my $i (0..4) { push @subs, sub { $i } } print $subs[0]->(); # => 0; Here’s the equivalent (or what I thought should be the equivalent) in Javscript: var subs = []; for (var i in [0,1,2,3,4]) { subs[i] = function () { return i; } } alert subs0 // => 4 What’s going on?

Who came up with the javascript scoping rules? What were they smoking. Here’s some Noddy Perl that demonstrates what I’m on about:

my @subs; 
for my $i (0..4) {
  push @subs, sub { $i }
}

print $subs[0]->(); # => 0;
Here's the equivalent (or what I thought should be the equivalent) in Javscript:
var subs = [];
for (var i in [0,1,2,3,4]) {
  subs[i] = function () {
    return i;
  }
}
alert subs[0]() // => 4
What's going on? In Perl, $i is scoped to the `for` block. Essentially, each time through the loop, a new variable is created, so the generated closures all refer to different $is. In Javascript, `i` is scoped to the `for` loop's containing function. Each of the generated closures refer to the same `i`. Which means that, to get the same effect as the perl code, you must write:
var subs = [];
for (var shared_i in [0,1,2,3,4]) {
  (function (i) {
    subs[i] = function () {
      return i;
    };
  })(shared_i);
}
subs[0]() // => 0
h3. Dodgy Ruby scoping

I had initially planned to write the example “How it should work” code in Ruby, but it turns out that Ruby’s for has the same problem:

subs = [];
for i in 0..4
  subs << lambda { i }
end
subs[0].call # => 4
Which is one reason why sensible Ruby programmers don't use `for`. If I were writing the snippet in 'real' Ruby, I'd write:
subs = (0..4).collect { |i|
  lambda { i }
}
subs[0].call # => 0
h3. My conclusion

Javascript is weird. Okay, so you already know this. In so many ways it’s a lovely language, but it does have some annoyingly odd corners.

  • 0 likes
  • 0 reposts
  • 0 replies
  • 0 mentions