Почему я могу выполнить следующие операции:
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 AssignmentExpression1. Вернуть 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");