Priorité des opérateurs avec l'opérateur ternaire Javascript

116

Je ne peux pas sembler envelopper ma tête autour de la première partie de ce code (+ =) en combinaison avec l'opérateur ternaire.

h.className += h.className ? ' error' : 'error'

La façon dont je pense que ce code fonctionne est la suivante:

h.className = h.className + h.className ? ' error' : 'error'

Mais ce n'est pas correct car cela donne une erreur dans ma console.

Ma question est donc de savoir comment dois-je interpréter ce code correctement?

Baijs
la source

Réponses:

141
h.className = h.className + (h.className ? ' error' : 'error')

Vous voulez que l'opérateur travaille h.className, mieux vaut être précis à ce sujet.
Bien sûr, aucun mal ne devrait provenir h.className += ' error', mais c'est une autre affaire.

Notez également qui +a la priorité sur l'opérateur ternaire: JavaScript Operator Precedence

Kobi
la source
3
Je pense qu'il convient de noter que, bien qu'aucun mal ne puisse être causé h.className += ' error', cela laisse également un espace vide au début de la chaîne si elle était initialement vide. Je crois que le but de l'opération ternaire est de produire une corde d'apparence propre.
JMTyler
@JMTyler - C'est exactement ce que j'indiquais - Si tout est fait juste pour garder un espace dès le début, je n'en vaux pas la peine. (le cas de bord inclut les sélecteurs jQuery ou XPath exacts). Bref, merci.
Kobi
@Kobi +1 pour l'avertissement de priorité d'opérateur seul!
Ed Chapel
129

Pense-y de cette façon:

<variable> = <expression> ? <true clause> : <false clause>

La façon dont l'instruction est exécutée est essentiellement la suivante:

  1. S'évalue-t-il <expression>à vrai ou est-ce qu'il est faux?
  2. Si <expression>la valeur <true clause>est true, la valeur de est affectée à <variable>, <false clause>est ignorée et l'instruction suivante est exécutée.
  3. Si la valeur est <expression>false, alors <true clause>est ignoré et la valeur de <false clause>est attribuée à <variable>.

La chose importante à réaliser avec l'opérateur ternaire dans ce langage et dans d'autres est que quel que soit le code qui se trouve, il <expression>doit produire un résultat booléen lorsqu'il est évalué: vrai ou faux.

Dans le cas de votre exemple, remplacez «assigné à» dans mon explication par «ajouté à», ou similaire pour l'arithmétique abrégée que vous utilisez, le cas échéant.

Wayne Koorts
la source
Notez bien si le commentaire parfait est approprié :) Il ignore toute explication de la raison pour laquelle les expressions de gauche sont "regroupées" en premier (c'est-à-dire parce qu'elle +a une plus grande priorité que l'opérateur conditionnel / ternaire (en fait, l'opérateur conditionnel est presque toujours le dernier) évalué dans n'importe quelle expression).
Fini le codage le
10

Le +=fait ce que vous voulez, mais dans la déclaration ternaire à sa droite, il vérifie si h.classNameest faux, ce qu'il serait s'il n'était pas défini. Si c'est vrai (c'est-à-dire si un nom de classe est déjà spécifié), alors l'erreur est ajoutée avec un espace (c'est-à-dire en ajoutant une nouvelle classe), sinon elle est ajoutée sans l'espace.

Le code peut être réécrit comme vous le suggérez, mais vous devez spécifier qu'il h.classNamedoit être utilisé pour la comparaison de vérité, plutôt que pour utiliser sa valeur réelle, dans l'opérateur ternaire, alors assurez-vous de ne pas vous soucier de la concaténation des valeurs en même temps que votre opération ternaire:

h.className = h.className + (h.className ? ' error' : 'error');
David Hedlund
la source
13
eh bien, undefinedn'est-ce pas faux, c'est juste traité comme si c'était
David Hedlund
4

Le côté droit de l' =opérateur est évalué de gauche à droite. Alors,

g.className = h.className + h.className ? ' error' : 'error';`

est équivalent à

h.className = (h.className + h.className) ? ' error' : 'error';

Etre équivalent à

h.className += h.className ? ' error' : 'error';

vous devez séparer l'énoncé ternaire entre parenthèses

h.className = h.className + (h.className ? ' error' : 'error');
Justin Johnson
la source
3
if (h.className) {
    h.className = h.className + ' error';
} else {
    h.className = h.className + 'error';
}

devrait être équivalent à:

h.className += h.className ? ' error' : 'error';
Darin Dimitrov
la source
1

Je sais que c'est une question très ancienne, mais je ne suis satisfait à 100% d'aucune des réponses car elles semblent toutes incomplètes. Donc, nous revenons des premiers principes:

L'objectif général de l'utilisateur:

Résumant le code: "Je souhaite ajouter un errornom de classe à une chaîne, éventuellement avec un espace au début s'il y a déjà des noms de classe dans la chaîne."

La solution la plus simple

Comme Kobi l'a souligné, il y a 5 ans, avoir un espace de premier plan dans les noms de classe ne posera aucun problème avec les navigateurs connus, donc la solution correcte la plus courte serait en fait:

h.className += ' error';

Cela aurait dû être la vraie réponse au problème réel .


Quoi qu'il en soit, les questions posées étaient ...

1) Pourquoi cela a-t-il fonctionné?

h.className += h.className ? ' error' : 'error'

L'opérateur conditionnel / ternaire fonctionne comme une instruction if, qui attribue le résultat de ses chemins trueou falseà une variable.

Donc, ce code a fonctionné car il est évalué simplement comme:

if (h.className IS NOT null AND IS NOT undefined AND IS NOT '') 
    h.className += ' error'
else
    h.className += 'error'

2) et pourquoi cette rupture?

h.className = h.className + h.className ? ' error' : 'error'

La question indique "qui donne une erreur [n] dans ma console", ce qui peut vous induire en erreur en pensant que le code ne fonctionne pas . En fait, le code suivant s'exécute, sans erreur , mais il renvoie simplement «erreur» si la chaîne n'était pas vide et «erreur» si la chaîne était vide et ne répondait donc pas aux exigences .

Ce code aboutit toujours à une chaîne qui contient uniquement ' error'ou 'error'parce qu'il évalue ce pseudo code:

if ((h.className + h.className) IS NOT null AND IS NOT undefined AND IS NOT '')
    h.className = ' error'
else
    h.className = 'error'

La raison en est que l'opérateur d'addition ( +aux gens du commun) a une "priorité" plus élevée (6) que l'opérateur conditionnel / ternaire (15). Je sais que les chiffres apparaissent à l'envers

La préséance signifie simplement que chaque type d'opérateur dans une langue est évalué dans un ordre prédéfini particulier (et pas seulement de gauche à droite).

Référence: Javascript Operator Precedence

Comment changer l'ordre d'évaluation:

Maintenant que nous savons pourquoi cela échoue, vous devez savoir comment le faire fonctionner.

Certaines autres réponses parlent de changer la priorité , mais vous ne pouvez pas . La préséance est câblée dans la langue. Ce n'est qu'un ensemble fixe de règles ... Cependant, vous pouvez changer l' ordre d'évaluation ...

L'outil de notre boîte à outils qui peut changer l'ordre d'évaluation est l'opérateur de regroupement (aka parenthèses). Pour ce faire, il s'assure que les expressions entre crochets sont évaluées avant les opérations en dehors des crochets. C'est tout ce qu'ils font, mais c'est suffisant.

Les parenthèses fonctionnent simplement parce qu'elles (opérateurs de regroupement) ont une priorité plus élevée que tous les autres opérateurs ("il y a maintenant un niveau 0").

En ajoutant simplement des crochets, vous modifiez l'ordre d'évaluation pour vous assurer que le test conditionnel est effectué en premier, avant la simple concaténation de chaînes:

h.className = h.className + (h.className ? ' error' : 'error')

Je vais maintenant laisser cette réponse à la rouille invisible parmi les autres :)

Codage terminé
la source
1

Je voudrais choisir l'explication de wayne:

<variable> = <expression> ? <true clause> : <false clause>

Considérons les deux cas:

case 1:
h.className += h.className ? 'true' : 'false'     
  • L'opérateur d'affectation fonctionne bien et la valeur est ajoutée
  • lors de la première exécution, o / p: false
  • 2ème fois. o / p: falsetrue - les valeurs s'ajoutent sans cesse

case2: h.className = h.className + h.className? 'vrai faux'

  • le résultat n'est pas le même que le cas 1
  • lors de la première exécution, o / p: false
  • 2ème fois. o / p: false - les valeurs ne continuent pas à s'ajouter

explanation

Dans le code ci-dessus, le cas 1 fonctionne bien

considérant que case2:

h.className = h.className + h.className ? 'true' : 'false'
is executed as 
 h.className = (h.className + h.className) ? 'true' : 'false'

h.className + h.className=> considéré comme une expression pour l'opérateur ternaire car l'opérateur ternaire a une priorité plus élevée. donc, toujours le résultat de l'expression ternaire est juste assigné

Vous devez définir la priorité en utilisant des crochets

Vous devez définir l'ordre d'évaluation à prendre en compte à l'aide de parenthèses pour que le cas 2 fonctionne comme le cas 1

h.className = h.className + (h.className ? ' error' : 'error') 
Angelin Nadar
la source
1
La terminologie ici n'est pas tout à fait correcte. La priorité est inhérente à la langue, vous ne la définissez pas . Vous définissez plutôt l' ordre d'évaluation en introduisant des crochets (qui ont une priorité plus élevée que tous les autres opérateurs).
Fini le codage le
@TrueBlueAussie Je l'accepte. J'apprécie votre intention de lire +1
Angelin Nadar