Почему я могу выполнить следующие операции:
var b1, b2;
b1 = b2 = true;
document.write(b1," ", b2);
document.write("<br>");
b1 = !b2;
document.write(b1," ", b2);
document.write("<br>");
b1 = b2 = !true;
document.write(b1," ", b2);
Почему я могу выполнить следующие операции:
var b1, b2;
b1 = b2 = true;
document.write(b1," ", b2);
document.write("<br>");
b1 = !b2;
document.write(b1," ", b2);
document.write("<br>");
b1 = b2 = !true;
document.write(b1," ", b2);
При попытке сделать это:
var b1, b2;
b1 = !b2 = true;
document.write(b1, " ", b2);
Поскольку они функционально эквивалентны ‡ вы в основном делаете:
var b1, b2;
!b2 = true;
b1 = true; //just the value of b2, not b2 itself
document.write(b1, " ", b2);
В строке !b2 = true
вы пытаетесь назначить выражение, которое оценивает значение (слева), до значения - это совершенно не имеет смысла. Подумайте об этом так:
!b2
присваивается true
. !b2
является выражением и оценивается как логическое значение, а не переменная.1 + 1 = 2
. Поскольку 1 + 1
оценивается до значения, вы не можете назначить это значение 2
, другое значение. Вы должны назначить значение переменной, поскольку присвоение значения к значению семантически и логически недействительно.1 + 1
- значение. 2
- значение. Вы не можете присвоить значение значению, так как это значение уже имеет значение. Константа, такая как 2
имеет значение 2
, ее нельзя изменить. Что делать, если мы попробовали 1 - 1 = 2
? 0
, константа и значение не могут быть 2
, потому что это константа.Таким образом, семантически и логически неверно присваивать значение значению. Вы не можете назначить 0
2
так же, как вы не можете назначить false
true
.
Если вы хотите лучше понять синтаксис и семантику и почему это выбрасывает ReferenceError
, вы можете вникать в ECMAScript® 2015 Language Specification †. По спецификации:
Раздел 12.14.1 - Операторы присваивания - Статическая семантика: ранние ошибки
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
- Это ранний Reference Ошибка, если
LeftHandSideExpression
не является ниObjectLiteral
, ниArrayLiteral
иIsValidSimpleAssignmentTarget
изLeftHandSideExpression
является ложным.
Где IsValidSimpleAssignmentTarget
:
Раздел 12.14.3 - Операторы присваивания - Статическая семантика: IsValidSimpleAssignmentTarget
AssignmentExpression : YieldExpression ArrowFunction LeftHandSideExpression = AssignmentExpression LeftHandSideExpression AssignmentOperator AssignmentExpression
1. Вернуть false.
Теперь оглянитесь на свой код: b1 = !b2 = true
. b1 = !b2
отлично, потому что это LeftHandSideExpression = AssignmentExpression
, поэтому возвращается значение true для IsValidSimpleAssignmentTarget
. Проблема возникает, когда мы проверяем !b2 = true
. Если мы посмотрим на определение LeftHandSideExpression
:
Раздел 12.3 - Левые выражения
Синтаксис
LeftHandSideExpression : NewExpression CallExpression
(Вы можете просмотреть определения NewExpression
и CallExpression
в ссылке выше)
Вы можете видеть, что !b2 = true
не является допустимым AssignmentExpression
, так как он не соответствует критериям LeftHandSideExpression = AssignmentExpression
. Это связано с тем, что !b2
не является допустимым LeftHandSideExpression
, также не ObjectLiteral
и ArrayLiteral
, поэтому IsValidSimpleAssignmentTarget
возвращает false, бросая ReferenceError
. Обратите внимание, что ошибка - это ранняя ошибка, то есть она бросается перед выполнением любого кода, как отмечено в @Bergi комментарий.
Вы можете бороться с этим, выполнив одно из следующих действий, в зависимости от вашего желаемого результата:
b1 = !(b2 = true);
С круглыми скобками внутри скобок имеет приоритет над внешним. Таким образом, b2
назначается, а так как он true
, внутри круглых скобок вычисляется true
. Затем это эквивалентно:
b1 = !(true);
Как и в круглых скобках, оценивается как true
, как указано выше. b1
будет считаться противоположным b2
, как и ожидалось, и b2
будет true
.
Если вы хотите, чтобы b1
был true
и b2
равным false
, выполните реструктуризацию оператора следующим образом:
b2 = !(b1 = true);
Таким образом, это полностью противоположно сказанному, давая b1 = true
и b2 = false
.
‡ Как отмечает @Bergi в комментариях, b1
присваивается правый операнд true
в этом случае, а не !b2
.
† Хотя большинство браузеров в настоящее время не поддерживают все функции ECMAScript 6 (2015) и вместо этого используют ECMAScript 5.1 (2011), спецификация для обеих версий одинакова. Все определения одинаковы, и поэтому объяснение остается в силе.
b1 = b2 = true;
эквивалентно
b2 = true;
b1 = true;
Назначение возвращает правый операнд. Легко видеть в интерактивной консоли (например, Chrome DevTools, NodeJS, jsc). Подробнее см. В описании в ответе Эндрю.
И при попытке b1 = !b2 = true;
эквивалент не имеет смысла:
(!b2) = true; // true = true; causes the error.
b1 = b2; // never evaluated.
Это связано с тем, что !
принимает precedence в операторе присваивания =
, как показано в скобках в (!b2)
.
Порядок:
b2
undefined
, поскольку он еще не инициализирован.!b2 === true
как !undefined === true
, поэтому !b2
становится true
,true = true
.Вы можете заставить его работать так, как вы ожидаете, добавив круглые скобки:
b1 = !(b2 = true);
Th выражение b1 = !b2 = true;
оценивается справа налево
Сначала: !b2 = true
тогда: b1 = <result of previous assignment>
!b2 = true
не делает логического смысла и, следовательно, ошибки.
Если вы пишете b1 = b2 = true;
, это не даст такой ошибки
Просто упомянуть AST Присвоение rvalue (1) (2)
if (1 + 1 = 2)
console.log("1 + 1 = 2");