Подтвердить что ты не робот

Почему это недопустимое назначение левой стороны?

Почему я могу выполнить следующие операции:

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);
4b9b3361

Ответ 1

При попытке сделать это:

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), спецификация для обеих версий одинакова. Все определения одинаковы, и поэтому объяснение остается в силе.

Ответ 2

b1 = b2 = true;

эквивалентно

b2 = true;
b1 = true;

Назначение возвращает правый операнд. Легко видеть в интерактивной консоли (например, Chrome DevTools, NodeJS, jsc). Подробнее см. В описании в ответе Эндрю.

node пример использования возвращаемого значения контировки для интерактивной консоли

И при попытке 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);

Ответ 3

Th выражение b1 = !b2 = true; оценивается справа налево

Сначала: !b2 = true

тогда: b1 = <result of previous assignment>

!b2 = true не делает логического смысла и, следовательно, ошибки.

Если вы пишете b1 = b2 = true;, это не даст такой ошибки

Ответ 4

Просто упомянуть AST Присвоение rvalue (1) (2)

if (1 + 1 = 2)
 console.log("1 + 1 = 2");