web-dev-qa-db-fra.com

Pourquoi est-ce une affectation non valide à gauche?

Pourquoi puis-je effectuer les opérations suivantes:

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

Pourtant, lorsque j'essaie l'opération suivante, je reçois un ReferenceError: invalid assignment left-hand side?

var b1, b2;

b1 = !b2 = true;

document.write(b1," ", b2);

Il est évident que je ne peux pas faire cela, mais je ne trouve pas d'explication pour laquelle je ne peux pas. Le guide du développeur MDN pour les états d'erreur:

Il y avait une mission inattendue quelque part. Cela peut être dû à un décalage entre un opérateur d'affectation et un opérateur de comparaison, par exemple. Alors qu'un seul signe "=" attribue une valeur à une variable, les opérateurs "==" ou "===" comparent une valeur.

Tous les opérateurs d'affectation fonctionnent individuellement comme cela a été prouvé, alors pourquoi cela ne peut-il pas être combiné en une opération singulière/affectation chaînée?

37
Zze

Lorsque vous essayez de faire ceci:

var b1, b2;

b1 = !b2 = true;

document.write(b1, " ", b2);

Parce qu'ils sont fonctionnellement équivalents vous faites essentiellement:

var b1, b2;

!b2 = true;
b1 = true; //just the value of b2, not b2 itself

document.write(b1, " ", b2);

Dans la ligne !b2 = true, vous essayez d'affecter une expression qui évalue une valeur (le côté gauche) à une valeur - cela n'a absolument aucun sens. Pensez-y de cette façon:

  • !b2 est affecté à true. !b2 est une expression et est évaluée en un booléen valeur, pas variable.
  • Ce serait analogue à faire 1 + 1 = 2. Puisque 1 + 1 est évalué à une valeur , vous ne pouvez pas l'attribuer à 2, une autre valeur. Vous devez attribuer une valeur à variable, car l'attribution de valeur à valeur est sémantiquement et logiquement invalide.
  • Une autre façon de penser à ce qui précède est de réaliser ceci: 1 + 1 est une valeur. 2 est une valeur. Vous ne pouvez pas affecter une valeur à une valeur, car cette valeur a déjà une valeur. Une constante telle que 2 a une valeur 2, il ne peut pas être modifié. Et si nous essayions 1 - 1 = 2? 0, une constante et une valeur, ne peut pas être 2, car c'est une constante.

Ainsi, il est sémantiquement et logiquement invalide d'attribuer une valeur à une valeur. Vous ne pouvez pas attribuer 0 à 2 tout comme vous ne pouvez pas affecter false à true.

Si vous souhaitez mieux comprendre la syntaxe et la sémantique, et pourquoi cela génère un ReferenceError, vous pouvez vous plonger dans le ECMAScript® 2015 Language Specification. Selon la spécification:

Section 12.14.1 - Opérateurs d'affectation - Sémantique statique: erreurs précoces

AssignmentExpression : LeftHandSideExpression = AssignmentExpression

  • C'est une erreur précoce Référence si LeftHandSideExpression n'est ni un ObjectLiteral ni un = ArrayLiteral et IsValidSimpleAssignmentTarget de LeftHandSideExpression est faux.

IsValidSimpleAssignmentTarget est:

Section 12.14.3 - Opérateurs d'affectation - Sémantique statique: IsValidSimpleAssignmentTarget

AssignmentExpression :
  YieldExpression
  ArrowFunction
  LeftHandSideExpression = AssignmentExpression
  LeftHandSideExpression AssignmentOperator AssignmentExpression

1. Retournez faux.

Revenons maintenant à votre code: b1 = !b2 = true. b1 = !b2 c'est bien parce que c'est LeftHandSideExpression = AssignmentExpression, renvoyant ainsi true pour IsValidSimpleAssignmentTarget. Le problème se pose lorsque nous vérifions !b2 = true. Si nous regardons la définition de LeftHandSideExpression:

Section 12.3 - Expressions du côté gauche

Syntaxe

LeftHandSideExpression :
  NewExpression
  CallExpression

(Vous pouvez afficher les définitions de NewExpression et CallExpression dans le lien de spécification ci-dessus)

Tu peux voir ça !b2 = true n'est pas valide AssignmentExpression, car il ne correspond pas aux critères LeftHandSideExpression = AssignmentExpression. Ceci est dû au fait !b2 n'est pas valide LeftHandSideExpression, pas non plus ObjectLiteral ni ArrayLiteral, donc IsValidSimpleAssignmentTarget renvoie false, jetant le ReferenceError. Notez que l'erreur est une erreur tôt , ce qui signifie qu'elle est lancée avant l'exécution de tout code, comme indiqué dans @ Commentaire de Bergi .


Vous pouvez lutter contre cela en effectuant l'une des opérations suivantes, selon le résultat souhaité:

b1 = !(b2 = true);

Avec les parenthèses, l'intérieur des parenthèses a priorité sur l'extérieur. De cette façon, b2 est assigné, et puisqu'il s'agit de true, à l'intérieur des parenthèses est évalué à true. Ensuite, cela équivaut à:

b1 = !(true);

Comme à l'intérieur des parenthèses est évalué à true comme mentionné ci-dessus. b1 sera l'opposé de b2 comme prévu et b2 sera true.

Si vous vouliez b1 être true et b2 pour être false, restructurez l'instruction comme ceci:

b2 = !(b1 = true);

De cette façon, c'est exactement l'opposé de ce qui précède, ce qui donne b1 = true, et b2 = false.


Comme @Bergi l'a mentionné dans les commentaires, b1 est affecté à l'opérande de droite, true dans ce cas, pas !b2.

Bien que la plupart des navigateurs ne prennent actuellement pas en charge toutes les fonctionnalités d'ECMAScript 6 (2015) et utilisent plutôt ECMAScript 5.1 (2011) , la spécification est la même pour les deux versions. Toutes les définitions sont les mêmes, et donc l'explication est toujours valable.

71
Li357
b1 = b2 = true;

est équivalent à

b2 = true;
b1 = true;

Une affectation renvoie l'opérande de droite . Il est facile de voir dans une console interactive (comme Chrome DevTools , NodeJS , jsc ). Voir le détails de la spécification dans la réponse d'Andrew .

node interactive console assignement return value example

Et quand vous essayez b1 = !b2 = true;, l'équivalent n'a aucun sens:

(!b2) = true; // true = true; causes the error.
b1 = b2;      // never evaluated.

En effet, le ! prend priorité le = opérateur d'affectation comme démontré par les parenthèses dans (!b2).

La commande va:

  1. b2 est undefined car il n'a pas encore été initialisé.
  2. Alors !b2 === true comme !undefined === true, donc !b2 devient true,
  3. puis l'affectation se produit, donc true = true.

Vous pouvez le faire fonctionner comme vous le souhaitez en ajoutant des parenthèses:

b1 = !(b2 = true);
27
Emile Bergeron

L'expression b1 = !b2 = true; est évalué de droite à gauche

Premier : !b2 = true

puis : b1 = <result of previous assignment>

!b2 = true n'a pas de sens logique et donc l'erreur.

Si vous écrivez b1 = b2 = true;, cela ne donnera pas une telle erreur

10
mrid

Juste pour mentionner le AST Assignation à rvalue ( 1 ) ( 2 )

if (1 + 1 = 2)
 console.log("1 + 1 = 2");
var b1, b2;
b1 = !b2 = true;

pour confirmer l'expression n'est jamais évaluée.

3
prosti