Search code examples
javascriptpre-increment

Pre increment in Javascript


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?


Solution

  • From EcmaScript standard:

    11.4.4 Prefix Increment Operator

    The production UnaryExpression : ++ UnaryExpression is evaluated as follows:

    1. Let expr be the result of evaluating UnaryExpression.
    2. 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"
    3. Let oldValue be ToNumber(GetValue(expr)).
    4. Let newValue be the result of adding the value 1 to oldValue, using the same rules as for the + operator (see 11.6.3).
    5. Call PutValue(expr, newValue).
    6. 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:

    1. Let lref be the result of evaluating LeftHandSideExpression.
    2. Let lval be GetValue(lref).
    3. Let rref be the result of evaluating AssignmentExpression.
    4. Let rval be GetValue(rref).
    5. Let r be the result of applying operator @ to lval and rval.
    6. 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"
    7. 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.