I think that the most intrusive one is semicolon insertion. This is perfectly valid code that does not do what it looks like:
return
{
a: "foo"
};
That's quite different from: return {
a: "foo"
};I think that the most intrusive one is semicolon insertion. This is perfectly valid code that does not do what it looks like:
return
{
a: "foo"
};
That's quite different from: return {
a: "foo"
}; var closures = [];
for (var i = 0; i < 10; i++)
{
var j = i;
closures[i] = function() { alert(j); }
}
closures[0](); // outputs 9, WTF?
How to fix it: var closures = [];
for (var i = 0; i < 10; i++) (function()
{
var j = i*i;
closures[i] = function() { alert(j); }
})(i)
closures[0](); // outputs 0 as it shouldJust pass in j when you call it and you can have lexical scope easily.
But if you changed that, a TON of code would be impossible, so I'm very happy with it working that way.
Function scope is the entire point of closures. If it was lexical scope it would not be a closure, it would just be a regular function.
Many more languages with closures have lexical scope than function scope. Lexical scope is the entire point of closures in those languages, indeed everywhere except JavaScript.
Just pass in j when you call it and you can have lexical scope easily.
I don't know j when I call the closure. Give a code example.
But if you changed that, a TON of code would be impossible, so I'm very happy with it working that way.
I can't imagine any code that would become impossible, or even the slightest bit more difficult, if function scope were replaced with lexical. Indeed all code using function scope could be mechanically translated into a (hypothetical) lexically-scoped-JavaScript by moving every var declaration in every if/for/while/etc block up to the toplevel of the containing function. If you disagree, give a code example.
var closures = [];
for (var i = 0; i < 10; i++)
{
var j = i;
closures[i] = (function (k) { return function() { alert(k); })(j);
}
closures[0](); // outputs 0
The issue is that the var here is passed by reference. Ie, all 10 closures refer to the SAME "i" variable. So, when i is incremented, they see that it's been incremented.Outputing 9 is actually correct in the first case. If you want to trap the current value, you need to copy the value to a new reference, ie, by making it the argument of a function with a new scope.
The issue is that the var here is passed by reference. Ie, all 10 closures refer to the SAME "i" variable. So, when i is incremented, they see that it's been incremented.
No, closures refer to the j variable that doesn't get incremented, only assigned to. The assignment is by value - no vars get "passed by reference", whatever this means. The actual issue is that all invocations of the loop body alias the same var j, even though it's declared inside the block.
I usually wrap the whole loop body if it contains multiple variables and multiple closures closing over them - it's easier on the brain than fixing every closure creation spot. Kinda sad because JavaScript is almost the perfect language for my mind.
You say that, and yet, you seem not to understand it. Perhaps you know how it works, but have not thought through the implications of it working any differently?
No, closures refer to the j variable that doesn't get incremented, only assigned to. ... The actual issue is that all invocations of the loop body alias the same var j, even though it's declared inside the block.
You're doing it wrong. Javascript doesn't have block-level scope. It only has functional scope.
In the process of parsing an execution context, all "var" statements (and named function declaration statements, which are a special sort of "var") are handled first, and only once. A var inside a loop still only creates the variable once, scoped to the function.
So, your code sample is actually equivalent to:
var closures = [], j = undefined;
for (var i = 0; i < 10; i++) {
j = i;
closures[i] = function() { alert(j); }
}
closures[0](); // outputs 9, duh ;)
You have one "j" variable that is shared by all 10 closures. When you set "j" in loop iteration 9, the closure created in iteration 0 faithfully sees that change.Closures tracking changes to variables in the parent function is not a wart. Consider this code:
var thing = (function () {
var privateData = "foo";
return {
set : function (x) { privateData = x; },
get : function () { return privateData; }
};
})();
If closures "trapped" outer values at their current state, then no matter how you called thing.set(), it would always return "foo" from thing.get(). Also, it would be much more expensive to keep track of completely independent contexts. (You could argue that these side effects make js a less pure functional language, and you'd be correct; another, less clear, argument could be made that this makes javascript less good, and I'd disagree.)However, your complaint seems not to be about closures or shared activation objects per se, but rather about the lack of block-level scope. Since "j" is created inside a set of braces, you seem to suggest that there ought to be multiple "j" variables created, which are each forgotten at the end of the loop.
That's how C++ does it. But javascript is not C++. Javascript is javascript. And only functions create scope in javascript.
IMO, this is not a wart. Doing otherwise would mean that every "while", "for", "switch", "if", and "else" would have to create an activation object, which would get quite expensive and complicated.
If you find it confusing that a var inside a loop is only created once, then take Crockford's advice, and always put all "var" statements at the start of a function. Personally, I disagree with him on this point, because I find it more useful to "var" variables near their use so that moving code around doesn't accidentally result in leakage to the global scope.
I like your reasoning for moving "var" further down where it's used in a function.
Bingo, reread my first comment.
If closures "trapped" outer values at their current state, then no matter how you called thing.set(), it would always return "foo" from thing.get().
No I don't want hyper-static environments, this would preclude the very useful encoding of objects with mutable state as closures.
Since "j" is created inside a set of braces, you seem to suggest that there ought to be multiple "j" variables created... every "while", "for", "switch", "if", and "else" would have to create an activation object, which would get quite expensive and complicated.
Do you think Ocaml heap-allocates a frame for every scope entered? How is it about as fast as C++, then? :-) Activation frames would need to be created for only those blocks that directly construct closures, findable by simple static analysis. Almost all other languages with closures do precisely this.
As a rule, the introduction of lexical scope doesn't make languages perceptibly slower. For example, the historical transition of Lisps from dynamic to lexical scope was accompanied by fears very similar to yours, and Scheme played a major role in convincing people that lexical scope worked fast. Modern Scheme compilers are quite clever at eliminating unneeded activation frames and closures, sometimes even converting closures into inline code. It's a well-researched topic. (See http://www.paulgraham.com/thist.html, a great retrospective by one of the best implementers... search for "tied up" for an amusing paragraph about exactly this. The year was 1982, I was busy being born.)
And to reiterate my point from another comment, all code that relies on function scope could be converted to a hypothetical lexically-scoped-JavaScript by a trivial transformation: move every var declaration to the toplevel of the containing function (which coincides nicely with Crockford's advice). This demonstrates that function scope is not more useful than lexical in any practical sense.
In short, I know how JavaScript works, I know how it could work differently, and I consider the different way superior both in theory and in practice.
No, closures refer to the j variable that doesn't get incremented, only assigned to.
"Doesn't get incremented, only assigned to." I don't know what that means. How is "i++;j=i;" any different, really, than "i++;j++"?
The assignment is by value - no vars get "passed by reference", whatever this means.
"Passed by reference" ==> all the closures reference the same "j" variable. The phrase "pass by reference" is, of course, a bit of a mixed metaphor, since it's not really "passed" as an argument to a function, but rather trapped by a closure. Same difference.
The actual issue is that all invocations of the loop body alias the same var j, even though it's declared inside the block.
"Even though"? There are no blocks in javascript; only functions create scope. That's what's so awesomely simple about this language. Eh, to each his own, I guess.
Lexical scope could be introduced in the new additions to ES3.1, but I'm pretty sure it won't be. A javascript engine would be crazy to try to shoehorn it into the language now, since it would, in fact, break a lot of code in the wild.
Time to man up. :)
Modern Scheme compilers are quite clever at eliminating unneeded activation frames and closures, sometimes even converting closures into inline code.
Isn't that roughly what tracemonkey does?
Yes. Nice to see implementations are growing up.
I guess it just seems much more elegant and simple to me to have only one kind of scope in the language... That's what's so awesomely simple about this language.
Block scope can be viewed as only one kind of scope. :-) If you try to define a language formally, function scope and lexical scope will be about equivalent on the simplicity scale: scopes will just be tied to code blocks instead of functions. (The scope nesting machinery is needed for closures anyway.) Implementation simplicity is also about equivalent: yes, blocks have to allocate activation frames only if they're needed, but same applies to functions once you get serious about speed.
The preference depends on your coding style. I find it intuitive to declare each variable in the outermost {} block that uses it, and not higher up; this intuition exactly corresponds to the formal concept of block scope. Many people naturally think like me, hence the huge number of Web articles complaining about the JS loop scope gotcha, calling it "weird", "unintuitive" and all manner of names. But as you say, to each his own.
A javascript engine would be crazy to try to shoehorn it into the language now, since it would, in fact, break a lot of code in the wild.
Yes. :-(
Woho! I just won a religious war.
proc foo {x} {
# do stuff
}
but if you really want to get around this you can abuse the continuation character proc foo {x} \
{
# do stuff
}The weird thing is just how the interpreter handles this:
return;
{
a: "foo";
}
That's a null return, a block (not that JS supports block-level scope, so blocks are pointless), a labeled line (line-labels aren't actually used for anything, since there's no GOTO), and on that labeled line, there's a string expression that does nothing.So, this is really like 4 different warts working together, but semicolon insertion is by far the most insidious.
Also, liberal use of parens can help prevent semicolon insertion bugs.