admin管理员组文章数量:1294341
I always assumed that <var> += 1
and <var> = <var> + 1
have the same semantics in JS.
Now, this CoffeeScript code piles 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 always assumed that <var> += 1
and <var> = <var> + 1
have the same semantics in JS.
Now, this CoffeeScript code piles 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?
- 3 This is not a bug, coffeescript != javascript, there are subtle and disturbing semantic differences like this if you don't fully understand the way that coffeescript interprets the code. I would suggest reading over the coffeescript documentation for a full understanding of why this occurs. – Quintin Robinson Commented Nov 6, 2012 at 19:35
- 1 coffeescript/#lexical_scope – jbabey Commented Nov 6, 2012 at 19:37
-
2
@LightnessRacesinOrbit: Then perhaps you could point out where the different behavior of
f = -> a += b
andf = -> a ||= b
is explicitly documented. – mu is too short Commented Nov 6, 2012 at 20:18 -
2
@muistooshort: That part is implicit. The section I linked to is about declarations, and
+=
has nothing to do with declarations. There's an argument to be made that||=
should do the same thing, I'll grant you that; however, that particular operator is not documented at all so you should not be relying on any particular behaviour other than what Javascript defines explicitly in the ECMA standard. – Lightness Races in Orbit Commented Nov 6, 2012 at 20:20 -
3
@muistooshort: See my answer.
a = a + b
is a construct defined in CoffeeScript and given these "local" semantics.a += b
is not defined in CoffeeScript, takes the Javascript meaning and thus has the different semantics. It's not even terribly surprising -- in what language doesa += b
declare anything new? – Lightness Races in Orbit Commented Nov 6, 2012 at 20:28
3 Answers
Reset to default 10I 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
We do X when the current implementation does X and that happens because the current implementation does it that way.
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:
a = ->
e = e + 1
bees
var a;
a = function() {
var e;
return e = e + 1;
};
with a local e
variable because you are explicitly assigning e
a value. If you simply refer to e
in an expression:
b = ->
e += 1
then CoffeeScript won't create a new variable because it doesn't recognize that there's an assignment to e
in there. CS recognizes an expression but isn't smart enough to see e +=1
as equivalent to e = 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:
c = ->
e ||= 11
yields an error that:
the variable "e" can't be assigned with ||= because it has not been defined
I think making a similar plaint about e += 1
would be sensible and consistent. Or all a op= b
expressions should expand to a = a op b
and 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 through Assign#pileNode
:
pileNode: (o) ->
if isValue = @variable instanceof Value
return @pilePatternMatch o if @variable.isArray() or @variable.isObject()
return @pileSplice o if @variable.isSplice()
return @pileConditional o if @context in ['||=', '&&=', '?=']
#...
so there is special handling for the CoffeeScript-specific op=
conditional constructs as expected. A quick review suggests that a op= b
for non-conditional op
(i.e. op
s other than ||
, &&
, and ?
) pass straight on through to the JavaScript. So what's going on with pileCondtional
? Well, as expected, it checks that you're not using undeclared variables:
pileConditional: (o) ->
[left, right] = @variable.cacheReference o
# Disallow conditional assignment of undefined variables.
if not left.properties.length and left.base instanceof Literal and
left.base.value != "this" and not o.scope.check left.base.value
throw new Error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been defined."
#...
There's the error message that we see from -> a ||= 11
and a ment noting that you're not allowed to a ||= b
when a
isn't defined somewhere.
This can be pieced together from the documentation:
=
: Assignment in Lexical scopeThe CoffeeScript piler takes care to make sure that all of your variables are properly declared within lexical scope — you never need to write
var
yourself.inner
within the function, on the other hand, should not be able to change the value of the external variable of the same name, and therefore has a declaration of its own.The example given in this section is precisely the same as your case.
+=
and||=
This is not a declaration, so the above does not apply. In its absence,
+=
takes on its usual meaning, as does||=
.In fact, since these are not redefined by CoffeeScript, they take their meaning from ECMA-262 — the underlying target language — which yields the results you've observed.
Unfortunately, this "fall-through" doesn't seem to be explicitly documented.
This issue has very recently been discussed on CoffeeScript's Github Issues. It seems the current behaviour of the piler was was agreed upon, or at least discussed, on this previous issue.
Basically, in JavaScript the expressions e = e + 1
and e += 1
are always equivalent, as they never introduce a new variable: they will always add 1
to the (local or global) e
variable, or they will fail if typeof e === 'undefined'
. Now, the expression var e = e + 1
is valid in JavaScript and will declare the e
variable and assign it to the value of adding undefined
and 1
(NaN
, obviously =P), while var e += 1
is syntactically invalid.
In CoffeeScript, e = e + 1
can be a variable declaration in case e
was not declared before, or just an assignment statement if e
is defined in the current scope, while e += 1
never introduces a new variable (a somewhat reasonable behaviour, as it doesn't make sense to increment a previously undeclared variable).
This is the current behaviour as i understand it. I think it's kind of unfortunate that e = e + 1
and e += 1
can mean different things, but i understand that it's a consequence of the bination of implicit variable declarations and JavaScript's scoping rules (this this ment for a, probably quite biased, explanation).
本文标签: javascriptWhy do e1 and ee1 compile differently in CoffeeScriptStack Overflow
版权声明:本文标题:javascript - Why do e += 1 and e = e + 1 compile differently in CoffeeScript? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741597486a2387499.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论