24

Here's a test situation for using the unary operator "++":

var j = 0 ;
console.log(j);
j = j++;
console.log(j);

For this, the output is:

0
0

Since the ++ operator's position is at the back of the operand, so its precedence is lower than the assignment's precedence, I would expect "j" to first receive the value of itself (i.e.0), but then be incremented. So why is the second console.log(j) call still showing "0"?

Just to be clear, I know that the solutions are:

// 1)
j++;
// 2)
++j;
// 3)
j += 1;
// 4)
j = ++j;

But I need to know why the increment step is not conducted in this specific scenario, NOT how to fix it!

Andrei Oniga
  • 8,219
  • 15
  • 52
  • 89
  • 2
    I think the ppl fast answering here do not get what you are asking and I agree with you this is confusing. I'd expect it to be 1, too. – Daniel W. Jan 20 '14 at 13:40
  • This is why I never use the j++, ++j and all that stuff. Stick to one clear operation per line... i.e. j += 1; – bgusach Jan 20 '14 at 13:41
  • No, it's precedence is much higher than the assignment? It's position doesn't make a difference. – Bergi Jan 20 '14 at 13:41
  • 18
    Note that `j = j++` is behaviour undefined in C and C++, with good reason. – Bathsheba Jan 20 '14 at 13:44
  • Postfix: passes the current value of j to j and then increments it. Prefix: increments the current value and then passes it to j. – Engineer2021 Jan 20 '14 at 13:59
  • 1
    Precedence has nothing to do with this. – Lightness Races in Orbit Jan 20 '14 at 14:37
  • 9
    There is NEVER a reason, to use the unary operator(++) on a variable in an expression which is assigned to the same variable. – Cruncher Jan 20 '14 at 14:51
  • 3
    @Cruncher: Hunting for rep on SO seems like a pretty good reason! – Lightness Races in Orbit Jan 20 '14 at 14:55
  • @LightnessRacesinOrbit Nevermind, I thought of one. If I wanted to add `x+(x+1)+(x+2)` I could do `x = x++ + x++ + x;`. Good luck reading that. (yes I know `x*3+3` is best lol) – Cruncher Jan 20 '14 at 14:59
  • 12
    You are confusing *precedence* with *order of evaluation*. Remember, they have *nothing* to do with each other. When you say `d = A() + B() * C();` it is *NOT* the case that `B() * C()` is evaluated before `A()` because multiplication is higher precedence! Rather, this is the same as `a = A(); b = B(); c = C(); t = b * c; d = a + t;` The multiplication happens before the addition, but that does not change the order in which the *operands* are computed. They are computed left-to-right. – Eric Lippert Jan 20 '14 at 15:04
  • @Cruncher: Are you trying to ruin my day – Lightness Races in Orbit Jan 20 '14 at 15:04
  • 1
    @LightnessRacesinOrbit If I wanted to ruin your day I would remove whitespace. `x=x+++x+++x`. I don't even know if this is `x = x++ + x++ + x` or `x = x + ++x + ++x;` both would give the same result – Cruncher Jan 20 '14 at 15:05

5 Answers5

52

This is an unintuitive (but not "weird"!) behaviour when using post-increment.

The statement j = j++ does this:

  1. Evaluate the LHS
    • In this case, nothing special happens because you simply named a variable, but this may not always be the case, e.g. foo() = j++
  2. Evaluate the RHS
    • Take the current value of j (0), and remember it as the result;
    • Increment j (to get 1);
  3. Assign the RHS to the LHS
    • recall that the RHS evaluates to that "remembered" value of j (0).

The result is a no-op.

The key here is that the entire RHS is evaluated and the post-increment performed, before the final assignment.


http://www.ecma-international.org/ecma-262/5.1/#sec-11.3.1
http://www.ecma-international.org/ecma-262/5.1/#sec-11.13.1

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • The ecma-script specs (and your explanation) are confusing to me if I consider two post-increment scenarios: 1. `j=j++`; 2. `j++`. Why does j get the incremented value in the second scenario as opposed to the first, namely `j=j++`? – Andrei Oniga Jan 20 '14 at 14:16
  • 3
    @AndreiOniga: `j` gets the incremented value in _both_ cases; however, in scenario #1 you are then re-assigning the original value immediately afterwards. Recall that `var j = 0; alert(j++); alert(j);` pops up `0` and then `1`, rather than `1` and then `1`. – Lightness Races in Orbit Jan 20 '14 at 14:35
  • 6
    Note that if the left hand side itself has a side effect, that side effect occurs *before* the evaluation of the right hand side. – Eric Lippert Jan 20 '14 at 14:44
  • 1
    @EricLippert: \*nods\* To be complete, I could have included an "_Evaluate the LHS_" step at the very beginning of the list of steps. I elided it in this instance, though possibly I shouldn't have. _(edit: better? :P)_ – Lightness Races in Orbit Jan 20 '14 at 14:53
  • Quite fine, though I note that `foo() = whatever` is an extremely odd case, as the specification notes `the left-hand operand of an assignment is expected to produce a reference. ... function calls are permitted to return references. This possibility is admitted purely for the sake of host objects. No built-in ECMAScript function defined by this specification returns a reference and there is no provision for a user-defined function to return a reference"`. Did you have a host object in mind for `foo()` ? – Eric Lippert Jan 20 '14 at 15:00
  • @EricLippert: Not in particular. :) But I'd rather not complicate matters here by introducing another increment operation (can't think of many other relevant expressions with side-effects), so let's just let `foo()` stand for any such expression evaluating to `j`. – Lightness Races in Orbit Jan 20 '14 at 15:02
  • 2
    Note that some folks use "no-op" to describe an assembly language construct where no actual work is performed (also "NOP"). I completely agree this is _effectively_ a no-op, it's not actually a "no-op" in the assembly language sense. (Unless your compiler is super smart and optimizes.... :-) – kmort Jan 20 '14 at 18:59
  • It might be clearer to emphasize that j++ *returns* a value. j++ returns the pre-increment value (vs. ++j which returns the post-increment value). – Brandon Jan 20 '14 at 19:45
  • 1
    @Brandon: I disagree. Functions return things. `j++` is not a function. This is about evaluation. – Lightness Races in Orbit Jan 20 '14 at 20:42
10

According to the ECMA Specifications for Postfix Increment Operator,

  1. Let lhs be the result of evaluating LeftHandSideExpression.
  2. Throw a SyntaxError exception if the following conditions are all true:
    1. Type(lhs) is Reference is true
    2. IsStrictReference(lhs) is true
    3. Type(GetBase(lhs)) is Environment Record
    4. GetReferencedName(lhs) is either "eval" or "arguments"
  3. Let oldValue be ToNumber(GetValue(lhs)).
  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(lhs, newValue).
  6. Return oldValue.

So, it is clear that the new value is first set on the lhs (in this case j) and the the old value is returned as the result of the expression, which is again set back in j. So, the value doesn't change.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • 1
    The relevant point is that `PutValue` (#5) is executed *before* the post-increment finishes evaluating. This is in contrast with C and C++, where the equivalent of `PutValue` is executed at the next "sequence point", and using the same operand elsewhere before the sequence point is undefined behavior and therefore forbidden. – user4815162342 Jan 20 '14 at 13:47
  • @user4815162342: It appears that the post-increment occurs in #4, but it returns the oldValue anyways in #6 actually. Unless I misunderstood what you are saying. – Engineer2021 Jan 20 '14 at 13:55
  • 1
    @Brian: That is correct. Well, the real point is that _all of that_ occurs before the encapsulating assignment operation does its work. – Lightness Races in Orbit Jan 20 '14 at 13:58
  • @LightnessRacesinOrbit: Right, so the take away here is to ask yourself as a programmer what is the intended behavior. My guess is here in this case the intent was actually `j += 1`. – Engineer2021 Jan 20 '14 at 14:02
  • 1
    @Brian #4 only calculates the new value, #5 stores it in `j`. Note that "LHS" here refers to the operand of the inner expression `j++`, not to the LHS of the outer assignment `j = ...`. The outer assignment receives the old value (as per #6) and resets `j` back to 0. In C, however, the post-increment operator works differently and is defined in terms of "sequence points", so in C the incrementing might be performed *after* the outer assignment. To avoid this kind of confusion, C and C++ consider "undefined" expressions that both refer to and increment the same object. – user4815162342 Jan 20 '14 at 14:17
  • @Brian: It's impossible to tell. The intended behaviour could have been `j+=1` but then it was implemented incorrectly. If the intended behaviour was `j+=0` then it was pointless. Either way it's a construct that one should not find in real code. – Lightness Races in Orbit Jan 20 '14 at 14:37
2

Where the ++ is lets you know what value you are going to get from it at that moment

++j; // increment the value and then give it to me.
j++; // give me the value and then increment it.

so you were saying

j = j++;

set j to the value of j before it was incremented.

0xFADE
  • 832
  • 5
  • 7
0

j = j++ means assign RHS value first to LHS and then increment RHS by 1. On the other hand, j = ++j means increment RHS by 1 and then assign it to LHS

Maxime Lorant
  • 34,607
  • 19
  • 87
  • 97
Janar
  • 1
-3

j++ means at first assign,then increment

++j means at first increment,then assign

So,

var j = 0 ;
console.log(j);
j = ++j;
console.log(j);

0
1
vborutenko
  • 4,323
  • 5
  • 28
  • 48