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émoireL'égalité lâche, les vraies règles
== compare comme === quand les types sont identiques. Sinon, il convertit selon trois règles :
null == undefinedvauttrue, et ces deux-là ne sont lâchement égaux à rien d'autre. C'est même un idiome propre :x == nullteste « null ou undefined » en une expression.- Nombre contre chaîne : la chaîne passe en nombre (
"1" == 1est vrai,"" == 0aussi carNumber("")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, puistoString. 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 : [] -> "" -> 0Les 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 :
[] == ![] // trueDé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 :
| Algorithme | Utilisé par | NaN = NaN ? | 0 = -0 ? |
|---|---|---|---|
| IsLooselyEqual | == | non | oui |
| IsStrictlyEqual | ===, switch, indexOf | non | oui |
| SameValue | Object.is | oui | non |
| SameValueZero | includes, Set, clés de Map | oui | oui |
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"vautfalse: deux chaînes se comparent lexicographiquement, caractère par caractère, et"1"vient avant"9"."10" > 9vauttrue: 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.