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?
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.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.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 unObjectLiteral
ni un =ArrayLiteral
etIsValidSimpleAssignmentTarget
deLeftHandSideExpression
est faux.
Où 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.
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 .
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:
b2
est undefined
car il n'a pas encore été initialisé.!b2 === true
comme !undefined === true
, donc !b2
devient true
,true = true
.Vous pouvez le faire fonctionner comme vous le souhaitez en ajoutant des parenthèses:
b1 = !(b2 = true);
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