I always assumed that <var> += 1 and <var> = <var> + 1 have the same semantics in JS.
Now, this CoffeeScript code compiles to different JavaScript when applied to the global variable e:
a: ->
e = e + 1
b: ->
e += 1
Note that b uses the global variable, whereas a defines a local variable:
({
a: function() {
var e;
return e = e + 1;
},
b: function() {
return e += 1;
}
});
Try it yourself.
Is this a bug or is there a reason why this is so?
I think I would call this a bug or at least an undocumented edge case or ambiguity. I don’t see anything in the docs that explicitly specifies when a new local variable is created in CoffeeScript so it boils down to the usual
sort of thing.
The condition that seems to trigger the creation of a new variable is assignment: it looks like CoffeeScript decides to create a new variable when you try to give it a value. So this:
becomes
with a local
evariable because you are explicitly assigningea value. If you simply refer toein an expression:then CoffeeScript won’t create a new variable because it doesn’t recognize that there’s an assignment to
ein there. CS recognizes an expression but isn’t smart enough to seee +=1as equivalent toe = e + 1.Interestingly enough, CS does recognize a problem when you use an
op=form that is part of CoffeeScript but not JavaScript; for example:yields an error that:
I think making a similar complaint about
e += 1would be sensible and consistent. Or alla op= bexpressions should expand toa = a op band be treated equally.If we look at the CoffeeScript source, we can see what’s going on. If you poke around a bit you’ll find that all the
op=constructs end up going throughAssign#compileNode:so there is special handling for the CoffeeScript-specific
op=conditional constructs as expected. A quick review suggests thata op= bfor non-conditionalop(i.e.ops other than||,&&, and?) pass straight on through to the JavaScript. So what’s going on withcompileCondtional? Well, as expected, it checks that you’re not using undeclared variables:There’s the error message that we see from
-> a ||= 11and a comment noting that you’re not allowed toa ||= bwhenaisn’t defined somewhere.