Why does (x += x += 1) evaluate differently in C and Javascript? - javascript

If the value of the variable x is initially 0, the expression x += x += 1 will evaluate to 2 in C, and to 1 in Javascript.
The semantics for C seems obvious to me: x += x += 1 is interpreted as x += (x += 1) which is, in turn, equivalent to
x += 1
x += x // where x is 1 at this point
What is the logic behind Javascript's interpretation? What specification enforces such behaviour? (It should be noted, by the way, that Java agrees with Javascript here).
Update:
It turns out the expression x += x += 1 has undefined behaviour according to the C standard (thanks ouah, John Bode, DarkDust, Drew Dormann), which seems to spoil the whole point of the question for some readers. The expression can be made standards-compliant by inserting an identity function into it as follows: x += id(x += 1). The same modification can be made to the Javascript code and the question still remains as stated. Presuming that the majority of the readers can understand the point behind "non-standards-compliant" formulation I'll keep it as it is more concise.
Update 2: It turns out that according to C99 the introduction of the identity function is probably not solving the ambiguity. In this case, dear reader, please regard the original question as pertaining to C++ rather than C99, where "+=" can be most probably now safely be regarded as an overloadable operator with a uniquely defined sequence of operations. That is, x += x += 1 is now equivalent to operator+=(x, operator+=(x, 1)). Sorry for the long road to standards-compliance.

x += x += 1; is undefined behavior in C.
The expression statement violates sequence points rules.
(C99, 6.5p2) "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression."

JavaScript and Java have pretty much strict left-to-right evaluation rules for this expression. C does not (even in the version you provided that has the identity function intervening).
The ECMAScript spec I have (3rd Edition, which I'll admit is quite old – the current version can be found here: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) says that compound assignment operators are evaluated like so:
11.13.2 Compound Assignment ( op= )
The production AssignmentExpression : LeftHandSideExpression # =
AssignmentExpression, where# represents one of the operators indicated
above, is evaluated as follows:
Evaluate LeftHandSideExpression.
Call GetValue(Result(1)).
Evaluate AssignmentExpression.
Call GetValue(Result(3)).
Apply operator # to Result(2) and Result(4).
Call PutValue(Result(1), Result(5)).
Return Result(5)
You note that Java has the same behavior as JavaScript – I think its spec is more readable, so I'll post some snippets here (http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.7):
15.7 Evaluation Order
The Java programming language guarantees that the operands of
operators appear to be evaluated in a specific evaluation order,
namely, from left to right.
It is recommended that code not rely crucially on this specification.
Code is usually clearer when each expression contains at most one side
effect, as its outermost operation, and when code does not depend on
exactly which exception arises as a consequence of the left-to-right
evaluation of expressions.
15.7.1 Evaluate Left-Hand Operand First The left-hand operand of a binary operator appears to be fully evaluated before any part of the
right-hand operand is evaluated. For example, if the left-hand operand
contains an assignment to a variable and the right-hand operand
contains a reference to that same variable, then the value produced by
the reference will reflect the fact that the assignment occurred
first.
...
If the operator is a compound-assignment operator (§15.26.2), then
evaluation of the left-hand operand includes both remembering the
variable that the left-hand operand denotes and fetching and saving
that variable's value for use in the implied combining operation.
On the other hand, in the not-undefined-behavior example where you provide an intermediate identity function:
x += id(x += 1);
while it's not undefined behavior (since the function call provides a sequence point), it's still unspecified behavior whether the leftmost x is evaluated before the function call or after. So, while it's not 'anything goes' undefined behavior, the C compiler is still permitted to evaluate both x variables before calling the id() function, in which case the final value stored to the variable will be 1:
For example, if x == 0 to start, the evaluation could look like:
tmp = x; // tmp == 0
x = tmp + id( x = tmp + 1)
// x == 1 at this point
or it could evaluate it like so:
tmp = id( x = x + 1); // tmp == 1, x == 1
x = x + tmp;
// x == 2 at this point
Note that unspecified behavior is subtly different than undefined behavior, but it's still not desirable behavior.

In C, x += x += 1 is undefined behavior.
You can not count on any result happening consistently because it is undefined to try to update the same object twice between sequence points.

At least in C, this is undefined behavior. The expression x += x+= 1; has two sequence points: an implicit one right before the expression starts (that is: the previous sequence point), and then again at the ;. Between these two sequence points x is modified twice and this explicitly stated as undefined behavior by the C99 standard. The compiler is free to do anything it likes at this point, including making daemons fly out of your nose. If you're lucky, it simply does what you expect but there is simply no guarantee for that.
This is the same reason why x = x++ + x++; is undefined in C. See also the C-FAQ for more examples and explanations of this or the StackOverflow C++ FAQ entry Undefined Behavior and Sequence Points (AFAIK the C++ rules for this are the same as for C).

Several issues are at play here.
First and most important is this part of the C language specification:
6.5 Expressions
...
2 Between the previous and next sequence point an object shall have its stored value
modified at most once by the evaluation of an expression.72) Furthermore, the prior value
shall be read only to determine the value to be stored.73)
...
72) A floating-point status flag is not an object and can be set more than once within an expression.
73) This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a[i] = i;
Emphasis mine.
The expression x += 1 modifies x (side effect). The expression x += x += 1 modifies x twice without an intervening sequence point, and it's not reading the prior value only to determine the new value to be stored; hence, the behavior is undefined (meaning any result is equally correct). Now, why on Earth would that be an issue? After all, += is right-associative, and everything's evaluated left-to-right, right?
Wrong.
3 The grouping of operators and operands is indicated by the syntax.74) Except as specified
later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation
of subexpressions and the order in which side effects take place are both unspecified.
...
74) The syntax specifies the precedence of operators in the evaluation of an expression, which is the same
as the order of the major subclauses of this subclause, highest precedence first. Thus, for example, the
expressions allowed as the operands of the binary + operator (6.5.6) are those expressions defined in
6.5.1 through 6.5.6. The exceptions are cast expressions (6.5.4) as operands of unary operators
(6.5.3), and an operand contained between any of the following pairs of operators: grouping
parentheses () (6.5.1), subscripting brackets [] (6.5.2.1), function-call parentheses () (6.5.2.2), and
the conditional operator ?: (6.5.15).
Emphasis mine.
In general, precedence and associativity do not affect order of evaluation or the order in which side effects are applied. Here's one possible evaluation sequence:
t0 = x + 1
t1 = x + t0
x = t1
x = t0
Oops. Not what we wanted.
Now, other languages such as Java and C# (and I'm assuming Javascript) do specify that operands are always evaluated left-to-right, so there's always a well-defined order of evaluation.

All JavaScript expressions are evaluated left to right.
The associativity of...
var x = 0;
x += x += 1
will be...
var x = 0;
x = (x + (x = (x + 1)))
So because of its left to right evaluation, the current value of x will be evaluated before any other operation takes place.
The result could be viewed like this...
var x = 0;
x = (0 + (x = (0 + 1)))
...which will clearly equal 1.
So...
var x = 0;
x = (x + (x = (x + 1)));
// x = (0 + (x = (0 + 1))); // 1
x = (x + (x = (x + 1)));
// x = (1 + (x = (1 + 1))); // 3
x = (x + (x = (x + 1)));
// x = (3 + (x = (3 + 1))); // 7

Related

Why doesn't short hand decleration work in a for of loop JS

const charCount = {}
for (const char of modString1){
// if (charCount[char] === undefined) charCount[char] = 1
// else charCount[char]++
charCount[char] = charCount[char]++ || 1
}
So I have this for loop that I am iterating over each character of a string to generate the number of times it repeats. The commented out code works. My understanding charCount[char] = charCount[char]++ || 1 is the same but the code doesn't work it always returns 1 for every character. I have also tried the ternary operators as well with the same result. Anyone have answer to this quirk?
If x starts at 0, then x = x++ || 1 will always set x to 1, never allowing it to advance further. This is because the result of x++ is x before incrementing, which is 0 on the first invocation, so the || 1 kicks in and the entire right-hand side evaluates to 1.
The second (and every subsequent) time through, x++ || 1 is always 1; even though x++ then sets x to 2, you're setting it back to 1 when the assignment operator is evaluated (because again, x++ evaluates to whatever x was before being incremented).
You need x = (x + 1) || 1.
The fundamental problem here is that you're effectively doing x = x++. This is a weird and incorrect thing to do. You're taking a simple assignment, and turning it into two assignments: x++ is itself an assignment, it mutates x, and you're mixing this with a second assignment, which mutates x a second time, undoing the first assignment. One of those mutations should not exist. You should typically see x++ as a statement, or x = x + 1 as a statement, but not both together, that is almost always incorrect.
Don't use ++ here. It's the wrong place for it, it's not the tool for your job.
As user229044 answered, assignments of form x = x++ are redundant, because we are effectively discarding the increment. This is because a postfix increment evaluates to its old value.
Your assignment can therefore be rewritten as: x = x || 1 or in words, if x is falsy set it to 1.
This is not the intended behaviour.
If we refactor your commented out if-statement, we can more easily see a pattern behind it:
if (x === undefined) x = 0;
x++;
As you can see, we want to treat the nullish undefined as zero, and always increment (upon finding a certain character).
Your final code may look like:
x = (x || 0) + 1;
Sidenote: Your if-statement only fell back when x was undefined, but your refactor fell back when x was falsy, which has broader meaning. This is technically irrelevant since x is always either a number or undefined. But using the nullish coalescing operator ?? instead of the OR operator || conveys our original intention more clearly: x = (x ?? 0) + 1
Note that converting undefined to a number results in NaN. Because NaN is falsy, the OR operator || will replace it with 1 as intended regardless (cite: user229044's comment). Therefore, using a prefix increment works too:
x = ++x || 1;

Why `console.log(x = y)` prints the value of `x` instead of `y`?

I need come feedback and an explanation for the following quiz I encountered:
x = 10, y = 20 and finally x = y
If we consider x and y as variables and the = operator standing for 'assignment'. What are the values of x and y?
After doing some basic reasoning on what I learnt about variables and values, I've written the following code:
var y = 10;
var x = 20;
console.log(x = y);
The result printed out is 20. Is this process and reasoning giving the correct result? If it is I understand the process but I am not sure to know the reason why the result is 20.
I would need some feedback and explanation on this matter.
The assignment operation in JavasScript is an expression and the value it returns is right side of the operation. The output for this piece of code is 10, as it's the value of y
You are making something different that you tell.
See this fiddle:
http://jsfiddle.net/nowoq16f/
Result is 10 as expected
var y = 10;
var x = 20;
alert(x = y);
Either you are lying or your implementation of JavaScript is broken :)
According to the standard:
The production AssignmentExpression : LeftHandSideExpression =
AssignmentExpression is evaluated as follows:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
Let rval be GetValue(rref).
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, rval).
Return rval.
So x = y should be evaluated to the value assumed by x after the assignment, which is to say, the value of y: 10.
As a side note, this feature of the language is particularly useful for doing something like:
min = max = 0
Thereby assigning to min the result of the evaluation of max = 0, which is to say, 0.
It outputs 10.
It firstly assigned y to x
Then output the result.
The = operator in JavasScript assign an expression (left side) to the right side of the operator. So, the output should be 10.

++ operator returns original value if placed after operand — how?

As far as I've been led to understand, x++ is essentially a terser way of saying x = x + 1. So far, so clear. In front-end Javascript, I've occasionally seen ++x — I seem to remember from a jsPerf test I can no longer find (how does one Google ++ effectively?) that this somehow had a small performance benefit in a particular version of IE, and let it go at that.
However I've recently encountered something that speaks of a weird quirk in execution order (JS code):
var x = 1;
console.log(x++); // 1 (?!)
console.log(x); // 2
…whereas
var x = 1;
console.log(++x); // 2 (what I would've expected)
console.log(x); // 2
I can't get my head around this. How can we return the unmodified variable when the operation and assignment are within the parenthesis, and thus by all rights should be executed before console.log is even invoked, let alone executed and returned?
Those are two different things
x++
is a post-increment. It returns x before the change but then changes it:
tmp = x;
x = x+1;
return tmp;
whereas
++x
is a pre-increment. It first changes x and returns the new value afterwards:
x = x+1;
return x;
The second one is also slightly faster as your compliler/interpreter doesn't need to create a temporary variable and copy the data across.
x++ gets the value, then increments it.
++x increments the value, then gets it.
This is the behavior in every language I've used that supports these operators.
Using ++ AFTER the variable increments the value after that line of code.
Likewise, using ++ BEFORE the variable increments the value before using it in that line of code.
Cool huh?
var x = 1;
x++;
console.log(x++); // 2 (?!)
console.log(x); // 3
console.log(++x); // 4
console.log(x++); // 4
console.log(x); // 5
You're talking about the difference between the pre- and post- increment operators. In the pre- case, the operation is essentially (x = x + 1; yield x), and in the second case it's (yield x; x = x + 1).

Why does +++x gives an error message when +x++ works fine?

var x = null;
+++x generates a ReferenceError, but when I do the same using postfix increment operator +x++, it works just fine.
The LeftHandSideExpression for the ++ operator must not be a number. For instance
1++;
will fail with the same error (invalid increment operand). You can only apply the pre- and postincrement operators on variables/identifiers/expressions.
Since the + sign casts the null value into a number (0), you got the same outcome.
Examples:
var foo = null,
bar = 5;
foo++; // 0
0++; // invalid increment operand
null++; // invalid increment operand
(+bar)++ // invalid increment operand
foo++ +2; // 2
+x++ is split into two steps:
+x initialises x to 0, so it's no longer null.
x++ then increments x, which works since x is no longer null.
+++x is also split into two steps, but in a particular order:
++x is evaluated first, which throws the exception because x is null.
+x would then be evaluated, except you've already had an exception.
I think your assumption was that +++x would be parsed as ++(+x), but it's actually parsed as +(++x). It's an ambiguous-looking syntax, the language designers had to pick one of the two ways to parse it, and from your point of view they chose "the other one".
To be honest, there's absolutely no value in formatting your code this way anyway - all you end up with is dubious-looking code which is destined to confuse people.
if u used the x= 0 ;
x will be initalized with a integer type that will accept the ++x operator while ++(+x)
is like a ++(+null)
so better try to change to X = 0 ;

Why does javascript accept commas in if statements?

I stumbled across some javascript syntax that seemed like it should produce a parse error of some kind but doesn't:
if (true, true) {console.log('splendid')} else {console.log('horrid')} // splendid
if (true, false) {console.log('splendid')} else {console.log('horrid')} // horrid
It seems only the last expression affects the logic, though all expressions are executed:
if (console.log('super'), true) {console.log('splendid')} // super splendid
Anyone know why that is valid javascript syntax? Is there any practical use for it?
The comma operator chains multiple expressions together, and the result of the operation is the value of the last operand. The only real use for it is when you need multiple side effects to occur, such as assignment or function calls.
The comma operator evaluates each of its operands (from left to right)
and returns the value of the last operand.
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/Comma_Operator
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Expressions_and_Operators#comma_operator
commas in javascript are actually pretty arcane. The coolest use I have seen is this
while(doSomething(), checkIfSomethingHappened());
the most common would be the way var is used in modern js
var foo = 1,
bar = 2;
This is also the same as in most other programming languages where you might have multiple iterators in a loop.
int x,y;
for(x = 0, y = 0; x < 10 || y < 100; x++, y++) {
....
}
I permits to do operations and comparisons in the same context.
Example:
if(a = 2, a > 1) console.log('a is', a)

Categories