问题
I've just encountered a 'feature' in Javascript regarding pre-increments. In all other languages I've used, it goes like I thought it would. E.g. in C++:
#include <iostream>
int main()
{
int i = 0;
i += ++i;
std::cout << i << std::endl; // Outputs 2.
}
So, ++i
doesn't make copy of the variable, hence the output is 2.
Same in PHP:
<?php
$i = 0;
$i += ++$i;
echo $i; // Outputs 2.
However, in Javascript:
var i = 0;
i += ++i;
console.log(i); // Outputs 1.
So it looks like that in Javascript, it makes copy of i
and doesn't reference the variable. Is this intentional and if yes, why?
回答1:
From EcmaScript standard:
11.4.4 Prefix Increment Operator
The production UnaryExpression : ++ UnaryExpression is evaluated as follows:
- Let expr be the result of evaluating UnaryExpression.
- Throw a SyntaxError exception if the following conditions are all true: �
- Type(expr) is Reference is true
- IsStrictReference(expr) is true
- Type(GetBase(expr)) is Environment Record
- GetReferencedName(expr) is either "eval" or "arguments"
- Let oldValue be ToNumber(GetValue(expr)).
- Let newValue be the result of adding the value 1 to oldValue, using the same rules as for the + operator (see 11.6.3).
- Call PutValue(expr, newValue).
- Return newValue.
and
11.13.2 Compound Assignment ( op= )
The production AssignmentExpression : LeftHandSideExpression AssignmentOperator AssignmentExpression, where AssignmentOperator is @= and @ represents one of the operators indicated above, is evaluated as follows:
- Let lref be the result of evaluating LeftHandSideExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating AssignmentExpression.
- Let rval be GetValue(rref).
- Let r be the result of applying operator @ to lval and rval.
- Throw a SyntaxError exception if the following conditions are all true:
- Type(lref) is Reference is true
- IsStrictReference(lref) is true
- Type(GetBase(lref)) is Environment Record
- GetReferencedName(lref) is either "eval" or "arguments"
- Call PutValue(lref, r)
Thus, var i = 0; i += ++i
is:
i = 0;
lvalue = value(i), which is 0;
rvalue = value(++i), which is: increment i, then value of i (1);
thus, rvalue = 1;
i = lvalue (0) + rvalue (1), which is 1.
Completely according to spec.
However, in C++, this is specifically defined to be undefined behaviour, thus on a different compiler you might also get 1. Or 99. Or it could set your computer on fire. All of those would be standard-compliant compilers. Thus, most people will recommend you only use pre/post-incremented variable once in a statement.
回答2:
I believe this is because javascript see's var i = 0; i += ++i;
as this:
var i = 0;
++i = 1;
i += 1;
It's because the ++i
turns i into 1 before performing +=
, so it's 0 + 1 = 1
In fact, the more I think about it, why would it do anything else?
There are two assignments taking place, and one has to be done before the other. So the two options would be either:
- do ++i first, and make it i = i + 1 where i starts at 0 and equates to 1
or
- do the += first, and make it i = 0 + (++i) which would also equate to 1
来源:https://stackoverflow.com/questions/23930661/pre-increment-in-javascript