Javascript scoping makes my head hurt 13
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]() // => 4What’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]() // => 0Dodgy 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 # => 4Which 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 # => 0My 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.

Here’s how I would have done this sort of thing in Javascript:
You’re right, though: Javascript’s scoping is very often contrary to expectations.
iin the list comprehension scope rather than keeping the reference alive with the lambda, it acts as you want it to: A bit of a cheat, but a short way to spell it.@Brendan: I’ve always disliked that style – what’s the point of manually unpicking a perfectly serviceable closure?
@anon: Ah, one more reason to maintain my dislike for Python.
In JavaScript 1.7 you get “let” which does what you’re looking for. Of course that doesn’t help you in the majority of browsers, but it is a recognized problem.
Yeah, scoping is insufficiently thought out in Javascript and downright braindead in Python. At least in Javascript, you can fix this particular example by reformulating it in the same way as the reformulated Ruby example:
var subs = [0,1,2,3,4].map( function(i) { return function() { i }; } );Of course I’d write this in Perl just like you did in “real Ruby”:
my @subs = map { my $i = $_; sub { $i } } 0 .. 4;And gosh, here I was annoyed by the verbosity of having to write “
sub” in front of every block snippet. Welcome to Javascript which makes you write “function()”!Heh. Javascript continues in its mission to turn people onto Lisp (but with extra syntax) eh? Are any of the various Javascript implementations doing tail call optimization yet? It’d certainly make continuation passing style a good deal easier to manage.
Aristotle: And no way to alias @functioneither. Giles Bowkett came up with a neat ruby hack:Which I find rather delightful.
Ah, then you’ll enjoy
lambda. :-)Pssh, Ruby doesn’t require a third party library:
http://www.oreillynet.com/ruby/blog/2007/10/fun_with_unicode_1.html
Daniel: You obviously haven’t looked at
lambda.pmvery closely. It’s not substantially longer than the ruby implementation, but it has some very neat tricks for lexically scoping the λ.Perl’s conventions for module inclusion and pragma-like modules knock Ruby’s into a cocked hat, frankly.
The loop counter is declared outside the for{} block, so to me it makes sense that it could be scoped to the containing block. Perl’s the odd one out here. I like the way it’s odd, but it’s still odd.
And, digging into the dusty recesses of my memory, isn’t that the same as how a for-loop counter would be scoped in C?
Who cares how it’s scoped in C? Frankly, the difference in scope only really becomes important/noticeable when your language gets closures. The slight weirdness of the Perl 5 scoping’s been addressed in Perl 6: It’s now:
Where
-> $whatever { ... }is the new way of declaring parameterized anonymous blocks. I’m sure it won’t be long before someone writes a library to let them spell that asλ $foo { ... }