JavaScript moderne : de solide à expert
Texte· 18 min

Coercion, == contre === et les comparaisons qui piègent

null >= 0 est vrai, null == 0 est faux

Vérifie par toi-même :

null > 0    // false
null == 0   // false
null >= 0   // true (!)

Si ta réaction est « ce langage est absurde », cette leçon est pour toi. Ces trois lignes suivent des règles écrites noir sur blanc dans la spec, et elles sont peu nombreuses. Les comparaisons relationnelles (>, >=, <, <=) convertissent leurs opérandes en nombre : null devient 0, et 0 >= 0 est vrai. L'égalité lâche ==, elle, suit un algorithme différent (IsLooselyEqual, §7.2.13) qui dit explicitement que null n'est lâchement égal qu'à undefined, à rien d'autre. Deux algorithmes, deux réponses. Une fois qu'on sait ça, tout le reste se déduit.

L'égalité stricte d'abord

=== (IsStrictlyEqual, §7.2.14) ne convertit rien : types différents, résultat false, point. Ses deux seules bizarreries viennent d'IEEE 754, pas de la coercion : NaN === NaN est faux, 0 === -0 est vrai. Pour les objets, === compare les références : deux objets distincts au contenu identique ne sont jamais égaux.

{ a: 1 } === { a: 1 }  // false : deux objets distincts en mémoire

L'égalité lâche, les vraies règles

== compare comme === quand les types sont identiques. Sinon, il convertit selon trois règles :

  • null == undefined vaut true, et ces deux-là ne sont lâchement égaux à rien d'autre. C'est même un idiome propre : x == null teste « null ou undefined » en une expression.
  • Nombre contre chaîne : la chaîne passe en nombre ("1" == 1 est vrai, "" == 0 aussi car Number("") vaut 0).
  • Un booléen est d'abord converti en nombre (true → 1, false → 0), puis on recommence.
  • Un objet comparé à un primitif passe par ToPrimitive : le moteur appelle valueOf, puis toString. Pour un tableau, ça donne sa jointure par virgules : [] → "", [5] → "5".
0 == ""      // true : "" -> 0
0 == "0"     // true : "0" -> 0
"" == "0"    // false : deux chaînes, aucune conversion
null == 0    // false : null n'est égal qu'à undefined
[] == 0      // true : [] -> "" -> 0

Les trois premières lignes prouvent que == n'est pas transitif : 0 est égal à "" et à "0", qui ne sont pas égaux entre eux. C'est exactement le genre de bug qui survit trois sprints dans une validation de formulaire.

Le cas d'école des entretiens combine tout :

[] == ![]   // true

Déroulé : ![] s'évalue d'abord. Un tableau est truthy, donc ![] vaut false. Reste [] == false : le booléen devient 0, le tableau passe par ToPrimitive et devient "", puis 0. 0 == 0 : vrai. Aucune magie, quatre règles appliquées dans l'ordre.

Truthy, falsy : la liste ferme

Dans un if, un &&, un || ou un !, la valeur passe par ToBoolean. Il y a exactement huit valeurs falsy : false, 0, -0, 0n, "", null, undefined, NaN. Tout le reste est truthy, y compris "0", "false", [], {} et new Boolean(false) (un objet wrapper est un objet, donc truthy, encore une raison de ne jamais utiliser new Boolean).

Les quatre algorithmes d'égalité, la vue d'ensemble

La spec définit quatre égalités, et tu les utilises toutes les quatre sans le savoir :

AlgorithmeUtilisé parNaN = NaN ?0 = -0 ?
IsLooselyEqual==nonoui
IsStrictlyEqual===, switch, indexOfnonoui
SameValueObject.isouinon
SameValueZeroincludes, Set, clés de Mapouioui

Conséquence concrète et méconnue :

[NaN].indexOf(NaN)   // -1 : introuvable (IsStrictlyEqual)
[NaN].includes(NaN)  // true (SameValueZero)

Si tu cherches des NaN dans un tableau avec indexOf, tu ne les trouveras jamais. includes (ES2016) a corrigé ça en changeant d'algorithme.

?? contre || pour les valeurs par défaut

port || 3000 remplace toute valeur falsy, donc écrase un 0 légitime. port ?? 3000 (ES2020) ne se déclenche que sur null et undefined. Il existe aussi l'affectation port ??= 3000. Détail de syntaxe : mélanger ?? avec || ou && sans parenthèses est interdit par la grammaire, le moteur lève SyntaxError: Unexpected token '??'. Écris (a || b) ?? c.

Ma règle en production : === partout, une exception assumée pour x == null, ?? pour les défauts. Et surtout, savoir lire == dans le code des autres, parce qu'il y en a.

À toi

Sans exécuter : "10" > "9" renvoie quoi ? Et "10" > 9 ?

"10" > "9" vaut false : deux chaînes se comparent lexicographiquement, caractère par caractère, et "1" vient avant "9". "10" > 9 vaut true : dès qu'un opérande est un nombre, l'autre est converti en nombre, et 10 > 9. Le même opérateur applique deux logiques selon les types. C'est la cause classique des tris cassés sur des nombres stockés en chaînes.

On mesure l'audience du site (GA4) et, si vous dites oui, on affine aussi la pub Meta. Rien ne se déclenche avant votre réponse — et votre choix tient 6 mois.