When you try to do this:
var b1, b2;
b1 = !b2 = true;
document.write(b1, " ", b2);
Because they are functionally equivalent? you are basically doing:
var b1, b2;
!b2 = true;
b1 = true; //just the value of b2, not b2 itself
document.write(b1, " ", b2);
In the line !b2 = true
, you are trying to assign an expression that evaluates to a value (the left side) to a value - that makes absolutely no sense. Think about it this way:
!b2
is being assigned to true
. !b2
is an expression and is evaluated to a boolean value, not variable.
- This would be analogous to doing
1 + 1 = 2
. Since 1 + 1
is evaluated to a value, you can't assign that to 2
, another value. You must assign a value to variable, as value-to-value assignment is semantically and logically invalid.
- Another way to think about the above is to realize this:
1 + 1
is a value. 2
is a value. You cannot assign a value to a value, as that value already has a value. A constant such as 2
has value 2
, it cannot be changed. What if we tried 1 - 1 = 2
? 0
, a constant and value, cannot be 2
, because it is a constant.
Thus, it is semantically and logically invalid to assign a value to a value. You cannot assign 0
to 2
just as you can't assign false
to true
.
If you want to understand the syntax and semantics better, and why this throws a ReferenceError
, you can delve into the ECMAScript? 2015 Language Specification?. Per the specification:
Section 12.14.1 - Assignment Operators - Static Semantics: Early Errors
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
- It is an early Reference Error if
LeftHandSideExpression
is neither an ObjectLiteral
nor an ArrayLiteral
and IsValidSimpleAssignmentTarget
of LeftHandSideExpression
is false.
Where IsValidSimpleAssignmentTarget
is:
Section 12.14.3 - Assignment Operators - Static Semantics: IsValidSimpleAssignmentTarget
AssignmentExpression :
YieldExpression
ArrowFunction
LeftHandSideExpression = AssignmentExpression
LeftHandSideExpression AssignmentOperator AssignmentExpression
1. Return false.
Now look back at your code: b1 = !b2 = true
. b1 = !b2
is fine because it is LeftHandSideExpression = AssignmentExpression
, thus returning true for IsValidSimpleAssignmentTarget
. The problem arises when we check !b2 = true
. If we look at the definition of LeftHandSideExpression
:
Section 12.3 - Left-Hand-Side Expressions
Syntax
LeftHandSideExpression :
NewExpression
CallExpression
(You can view the definitions of NewExpression
and CallExpression
in the specification link above)
You can see that !b2 = true
is not a valid AssignmentExpression
, as it does not fit the criteria LeftHandSideExpression = AssignmentExpression
. This is because !b2
is not a valid LeftHandSideExpression
, also not an ObjectLiteral
nor ArrayLiteral
, thus IsValidSimpleAssignmentTarget
returns false, throwing the ReferenceError
. Note that the error is an early error, meaning it is thrown before any code is executed, as noted in @Bergi's comment.
You can combat this by doing either of the following, depending on your desired outcome:
b1 = !(b2 = true);
With parentheses, inside the parentheses takes precedence over outside. That way, b2
is assigned, and since it is true
, inside the parentheses evaluates to true
. Next, it's equivalent to:
b1 = !(true);
As inside the parentheses is evaluated to true
as mentioned above. b1
will be the opposite of b2
as expected, and b2
will be true
.
If you wanted b1
to be true
and b2
to be false
, restructure the statement like this:
b2 = !(b1 = true);
This way, it's the exact opposite of the above, giving b1 = true
, and b2 = false
.
?As @Bergi mentioned in the comments, b1
is assigned the right operand, true
in this case, not !b2
.
?Although most browsers currently do not support all features of ECMAScript 6 (2015), and instead use ECMAScript 5.1 (2011), the specification is the same for both versions. All definitions are the same, and thus the explanation is still valid.