Pre increment in Javascript
Asked Answered
K

2

11

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?

Kiloton answered 29/5, 2014 at 10:0 Comment(4)
Maybe not an explanation, or even a solution, but certainly a good read: #971812Locker
In C++, this isn't even meaningful. it's undefined behavior.Quezada
Another good read: javascript.about.com/od/hintsandtips/a/…Locker
#1547481Mesothelium
L
7

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.

Leonhard answered 29/5, 2014 at 10:13 Comment(0)
E
0

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
Epilepsy answered 29/5, 2014 at 10:5 Comment(5)
The third option is do ++i (i.e. i=1), then do i+=i (which is at that point i=1+1). This results in 2.Leonhard
I don't see how that is a valid option, as if both assignments are in the same statement the original value of i at the point of entry to the statement is zero. One of the assignments has to take precedence and the other has to use the original value. I can see how it would be 2 if the code was written like this: var i = 0; ++i; i += i;Epilepsy
The way this works is in compilers/interpreters that resolve the preincrement before resolving anything else in the statement, so that i+=++i is indeed equivalent to ++i; i+=i. This, as I show in my answer, is not according to EcmaScript spec, but is a valid option for a C++ compiler, as OP clearly shows.Leonhard
I see what you're getting at, I guess I just would have assumed the grammar of the language would use something akin to operator precedence when working out compound assignments. I can see that the OP was getting as a kind of ref vs value type question. With this in mind, I would perhaps revise my comment / answer to perhaps refer to JS handling primitive as immutable.Epilepsy
In EcmaScript, it does. See my answer to the exact mechanism. In C++, the spec writers basically had the attitude "if you're stupid enough to use pre/post-modified variable twice in a statement, you deserve everything you get." (Specifically, Spec says "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression."). i+=++i modifies i twice, thus what happens is not covered under the C99 standard.Leonhard

© 2022 - 2024 — McMap. All rights reserved.