10 octobre 2020

Comparaisons

Il y a de nombreux opérateurs de comparaison que nous connaissons des mathématiques :

  • Plus grand/petit que : a > b, a < b.
  • Plus grand/petit ou égal à : a >= b, a <= b.
  • Le contrôle d’égalité est écrit en tant que a == b (veuillez noter le signe de la double équation =. Un seul symbole a = b signifierait une affectation).
  • Pas égal. En maths, la notation est , en JavaScript, elle est écrite comme une assignation avec un signe d’exclamation : a != b.

Dans cet article, nous en apprendrons plus sur les différents types de comparaisons, sur la façon dont JavaScript les fait, y compris sur les particularités importantes.

À la fin, vous trouverez une bonne recette pour éviter les problèmes liés aux “bizarreries JavaScript”.

Booléen est le résultat

Tout comme tous les autres opérateurs, une comparaison renvoie une valeur de type booléenne.

  • true – signifie “oui”, “correct” ou “vrai”.
  • false – signifie “non”, “incorrect” ou “faux”.

Par exemple :

alert( 2 > 1 );  // true (correct)
alert( 2 == 1 ); // false (faux)
alert( 2 != 1 ); // true (correct)

Un résultat de comparaison peut être affecté à une variable, comme toute valeur :

let result = 5 > 4; // attribue le résultat de la comparaison
alert( result ); // true

Comparaison de chaînes de caractères

Pour voir quelle chaîne de caractères est plus grande que l’autre, on utilise l’ordre dit “dictionnaire” ou “lexicographique”.

En d’autres termes, les chaînes de caractères sont comparées lettre par lettre.

Par exemple :

alert( 'Z' > 'A' ); // true
alert( 'Glow' > 'Glee' ); // true
alert( 'Bee' > 'Be' ); // true

L’algorithme pour comparer deux chaînes de caractères est simple :

  1. Compare les premiers caractères des deux chaînes de caractères.
  2. Si le premier est supérieur (ou inférieur), la première chaîne de caractères est supérieure (ou inférieure) à la seconde. Nous en avons fini.
  3. Sinon, si les premiers caractères sont égaux, comparez les deuxièmes caractères de la même manière.
  4. Répéter jusqu’à la fin d’une chaîne de caractères.
  5. Si les deux chaînes de caractères se sont terminées simultanément, alors elles sont égales. Sinon, la chaîne la plus longue est plus grande.

Dans l’exemple ci-dessus, la comparaison 'Z' > 'A' obtient le résultat à la première étape.

La deuxième comparaison 'Glow' et 'Glee' nécessite plus d’étapes car les chaînes de caractères sont comparées caractère par caractère :

  1. G est identique à G.
  2. l est identique à l.
  3. o est plus grand que e. On stop ici. La première chaîne de caractères est plus grande.
Pas vraiment un dictionnaire, mais plutôt l’ordre Unicode

L’algorithme de comparaison ci-dessus est à peu près équivalent à celui utilisé dans les dictionnaires ou les annuaires téléphoniques. Mais ce n’est pas exactement la même chose.

Par exemple, cette notion est importante à comprendre. Une lettre majuscule "A" n’est pas égale à la lettre minuscule "a". Lequel est le plus grand ? En fait, le minuscule "a" l’est. Pourquoi ? Parce que le caractère minuscule a un index plus grand dans la table de codage interne (Unicode). Nous reviendrons sur les détails spécifiques et leurs conséquences dans le chapitre Strings.

Comparaison de différents types

Lorsque les valeurs comparées appartiennent à différents types, elles sont converties en nombres.

Par exemple :

alert( '2' > 1 ); // true, la chaîne '2' devient un numéro 2
alert( '01' == 1 ); // true, chaîne '01' devient un numéro 1

Pour les valeurs booléennes, true devient 1 et false devient 0.

Par exemple :

alert( true == 1 ); // true
alert( false == 0 ); // true
Une conséquence amusante

Il est possible que dans le même temps :

  • Deux valeurs sont égales.
  • L’un d’eux est vrai comme booléen et l’autre est faux comme booléen.

Par exemple :

let a = 0;
alert( Boolean(a) ); // false

let b = "0";
alert( Boolean(b) ); // true

alert(a == b); // true!

Du point de vue de JavaScript, c’est tout à fait normal. Un contrôle d’égalité convertit en utilisant la conversion numérique (par conséquent, "0" devient 0), tandis que la conversion booléenne utilise un autre ensemble de règles.

Égalité stricte

Une vérification d’égalité régulière == a un problème. Elle ne peut pas faire la différence entre 0 et false :

alert( 0 == false ); // true

La même chose avec une chaîne de caractères vide :

alert( '' == false ); // true

C’est parce que les opérandes de différents types sont convertis en un nombre par l’opérateur d’égalité ==. Une chaîne de caractères vide, tout comme false, devient un zéro.

Que faire si nous voulons différencier 0 de faux ?

Un opérateur d’égalité stricte === vérifie l’égalité sans conversion de type.

En d’autres termes, si a et b sont de types différents, alors a === b renvoie immédiatement false sans tenter de les convertir.

Essayons :

alert( 0 === false ); // false, parce que les types sont différents

Il existe également un opérateur de “strict non-égalité” !==, par analogie à la non-égalité !=.

L’opérateur de vérification de l’égalité stricte est un peu plus long à écrire, mais rend évident ce qui se passe et laisse moins d’espace pour les erreurs.

Comparaison avec null et undefined

Il existe un comportement non intuitif lorsque null ou undefined sont comparés à d’autres valeurs.

Pour un contrôle de strict égalité ===

Ces valeurs sont différentes car chacune d’entre elles appartient à un type distinct.

alert( null === undefined ); // false
Pour un contrôle d’égalité non strict ==

Il y a une règle spéciale. Ces deux là forment “un beau couple” : ils sont égaux (au sens de ==), mais pas à d’autres valeurs.

alert( null == undefined ); // true
Pour les maths et autres comparaisons < > <= >=

Les valeurs null/undefined sont converties en un nombre : null devient 0, alors qu’undefined devient NaN.

Voyons maintenant des choses amusantes qui se produisent lorsque nous appliquons ces règles. Et, ce qui est plus important, comment ne pas tomber dans un piège avec ces caractéristiques.

L’étrange résultat : null vs 0

Comparons null avec un zéro :

alert( null > 0 );  // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true

Ouais, mathématiquement c’est étrange. Le dernier résultat indique que "null est supérieur ou égal à zéro". Alors que l’une des comparaisons au dessus devrait être correcte, mais les deux sont fausses.

La raison est qu’une vérification d’égalité == et les comparaisons> <> >= <= fonctionnent différemment. Les comparaisons convertissent null en un nombre, donc le traitent comme 0. C’est pourquoi (3) null >= 0 est vrai et (1) nul > 0 est faux.

D’un autre côté, la vérification de l’égalité == pour undefined et null est définie de telle sorte que, sans aucune conversion, ils sont égaux et ne correspondent à rien d’autre. C’est pourquoi (2) null == 0 est faux.

Un undefined incomparable

La valeur undefined ne doit pas du tout participer aux comparaisons :

alert( undefined > 0 ); // false (1)
alert( undefined < 0 ); // false (2)
alert( undefined == 0 ); // false (3)

Pourquoi est-ce qu’il n’aime pas le zéro ? Toujours faux!

Nous avons ces résultats parce que :

  • Les comparaisons (1) et (2) renvoient false car undefined est converti en NaN et NaN est une valeur numérique spéciale qui renvoie false pour toutes les comparaisons.
  • Le contrôle d’égalité (3) renvoie false, car undefined est uniquement égal à null et à aucune autre valeur.

Éviter les problèmes

Pourquoi avons-nous observé ces exemples? Devrions-nous nous souvenir de ces particularités tout le temps ? Eh bien pas vraiment. En fait, ces notions délicates deviennent progressivement familières au fil du temps, mais il existe un moyen solide d’éviter tout problème avec elles.

Il suffit de traiter toute comparaison avec undefined/null (à l’exception de la stricte égalité ===) avec un soin exceptionnel.

N’utilisez pas de comparaisons => > < <= avec une variable qui peut être null/undefined, sauf si vous êtes vraiment sûr de ce que vous faites. Si une variable peut avoir de telles valeurs, vérifiez-les séparément.

Résumé

  • Les opérateurs de comparaison renvoient une valeur logique.
  • Les chaînes de caractères sont comparées lettre par lettre dans l’ordre “dictionnaire”.
  • Lorsque des valeurs de différents types sont comparées, elles sont converties en nombres (à l’exclusion d’un contrôle d’égalité strict).
  • Les valeurs null et undefined sont égales == et ne correspondent à aucune autre valeur.
  • Soyez prudent lorsque vous utilisez des comparaisons telles que > ou < avec des variables pouvant parfois être null/undefined. Faire une vérification séparée pour null/undefined est une bonne idée.

Exercices

importance: 5

Quel sera le résultat pour les expressions suivantes :

5 > 4
"apple" > "pineapple"
"2" > "12"
undefined == null
undefined === null
null == "\n0\n"
null === +"\n0\n"
5 > 4 → true
"apple" > "pineapple" → false
"2" > "12" → true
undefined == null → true
undefined === null → false
null == "\n0\n" → false
null === +"\n0\n" → false

Quelques raisons :

  1. Évidemment, c’est vrai.
  2. Comparaison du dictionnaire, donc fausse. "a" est plus petit que "p".
  3. Encore une fois, comparaison du dictionnaire, le premier caractère de "2" est plus grand que le premier caractère de"1".
  4. Les valeurs null et undefined sont exclusivement égale entre elles.
  5. L’égalité stricte est stricte. Des types différents des deux côtés conduisent à false.
  6. Similaire à (4), null n’est égale qu’à undefined.
  7. Egalité stricte de différents types.
Carte du tutoriel

Commentaires

lire ceci avant de commenter…
  • Si vous avez des améliorations à suggérer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de préciser.
  • Pour insérer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes -- enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen…)