5 choses étranges sur Javascript expliquées (+ un bonus)

Vous avez sûrement vu passer ces exemples sur internet, aujourd'hui je vous explique le pourquoi du comment.

Article publié le 14/12/2020, dernière mise à jour le 19/09/2023

Je suis développeur Javascript, et il m'arrive parfois de croiser les doigts lorsque j'exécute un script pour la première fois en espérant que tout se passe comme prévu, et ma femme me demande souvent :

Mais pourquoi ça ne marcherait pas ? C'est un ordinateur, il fait uniquement ce que tu lui dis de faire !

Et en soit, elle a plus que raison, mais en même temps il arrive des situations où même si notre logique est bonne, quelque chose cloche à cause de la nature singulière de ce langage qu'est le Javascript.

Je vais donc vous présenter 5 exemples de résultats inattendus afin de vous expliquer la raison sous-jacente de chacun d'entre-eux !

Javascript, je t'aime, moi non plus.

Les exemples suivant proviennent presque tous du fait que Javascript soit un langage typé dynamiquement, ce qui signifie que lorsque l'on assigne une valeur à une variable, ce dernier va deviner le type selon la variable de manière dynamique.

Lors d'une opération, Javascript va essayer de transformer le contenu de la variable d'origine pour le faire coller au type attendu par l'opération, ce qui entrainer des résultats parfois étranges.

La vraie nature de NaN

console.log(typeof NaN)
// => "number"

console.log(NaN == NaN);
// => false

Quoi  ? La valeur NaN qui signifie "Not a Number" est en fait un nombre ?

Et oui.

On pourrait croire que NaN est une valeur spécifique comme null ou undefined, mais non, NaN est bien un nombre, et le pire, c'est qu'il n'est pas égal à lui-même comme vous pouvez le voir dans l'exemple ci-dessus.

La raison est simple, NaN sert de résultat dans plusieurs cas, par exemple :

  • lorsque l'on essaye de convertir une donnée qui ne représente pas un nombre (par exemple la chaine de caractères "bleu")
  • lors d'une expression mathématique impossible à calculer pour le système informatique mais dont le résultat est bien un nombre,  comme la racine carrée d'un nombre négatif.
  • ...

Il est donc impossible à savoir si deux valeurs NaN ont la même signification, c'est donc pour cela que NaN == NaN renvoit toujours faux !

Voilà pourquoi vous devrez toujours passer par la méthode isNaN()

Les chiffres à virgules n'existent pas

console.log(0.1+0.2)
// => 0.30000000000000004

console.log(0.1+0.2==0.3);
// => false

Lorsque l'on pense à des opérations dont les résultats sont connus de tous, on pense au additions en mathématiques, et pourtant comme vous pouvez le voir, on ne peut pas toujours s'y fier.

Mais alors Javascript ne sait même pas compter correctement ?

On pourrait être tenté de croire que ce résultat erroné est dû au langage en lui-même, et pourtant son origine est bien plus bas-niveau, à tel point que le même problème existe dans la plupart des autres langages.

La raison est assez complexe, mais pour faire simple cela provient du fait que nos circuit électroniques sont prévus pour stocker des nombres entiers (sous forme binaire), et que pour retrouver des nombres à virgules ces derniers sont obligés d'utiliser des fractions.

La nature même des fractions en mathématiques fait que certaines d'entre-elles se résolvent par une suite de nombres infinis (1/3 = 0.33333333...4 en base 10) et lorsque l'on passe en base 2 (système binaire), certaines fractions pourtant simples en base 10, deviennent complexes en base 2, c'est le cas pour 1/10 et 2/10, les chiffres problématiques dans les opération précédentes.

Si vous voulez des explications plus précises et mathématiquement plus exacte, je vous recommande la lecture de cet article sur Medium.

Pour éviter tout problème sur des calculs requiérant une précision infaillible, comme dans des systèmes financiers, vous serez donc obligé de prendre les entiers comme base de calculs, en comptant les centimes par exemple.

Les opérations caméléons

console.log(9+"1")
// => "91"

console.log(9-"1");
// => 8

La différence entre les opérateurs + et - en javascript est que l'un représente une opération arithmétique (-) tandis que l'autre représente à la fois une opération et une fonction logicielle.

L'opérateur "+" donne à la fois la possibilité d'additioner chiffres, mais aussi de concaténer deux chaines de caractères. La priorité est portée sur la concaténation, si bien que si l'une des deux valeurs (à gauche ou à droite) de l'opérateur est une chaine de caractères, Javascript transformera l'autre valeur en chaine de caractère et les mettra bout à bout.

L'opérateur "-" est différent car il ne représente que l'opération mathématique, si bien qu'il va essayer de convertir les valeurs de chaque côté en nombre avant d'effectuer l'opération, le résultat sera donc un nombre (peut-être NaN si la conversion se passe mal).

Les valeurs falsy et truthy

console.log(true+true+true)
// => 3

console.log(true == 1);
// => true

console.log("" == false);
// => true

En Javascript il existe des valeurs primitives true et false, mais il existe aussi le concept de valeurs truthy et falsy permettant des calculs plus souples, mais aussi bien souvent des confusions.

Lors d'une condition (avec les opérateurs ==, ===, <, > et autres) le résultat sera une valeur booléenne primitive, mais elle pourra être calculé à partir de valeurs qui sont considérés comme "à peu près" vrais ou fausses. Exemple, les valeurs :

  • "" (chaine vide), "false", 0 (zéro) ou "0" sont considérés comme falsy
  • "1" ou 1 sont considérés comme truthy

Attention, aucune autre chaine de caractère n'est considérée comme truthy, même pas la chaine "true".

Pour éviter tout problème, favorisez l'opérateur === qui ne prendra pas en compte les valeurs truthy et falsy mais seulement true et false.

Les tableaux vides

console.log([]+[])
// => ""

console.log([] == 0);
// => true

Lorsque que l'on additionne deux tableaux vides, Javascript nous renvoie une chaine vide car comme nous l'avons vu précédemment, l'opérateur "+" essaye de convertir ses opérandes en string si ce ne sont pas déjà des valeurs de type "number".

Et surprise, on se rend compte qu'un tableau vide correspond en fait à une chaine vide, car oui dans tous les langages de programmation, une chaine de caractères est en fait un tableau de caractères déguisé. Vous pouvez d'ailleurs faire le test, suivant :

console.log([1,2,3]+"");
// => "1,2,3"

Lorsqu'il doit convertir un tableau en string, Javascript va tout simplement séparer chaque valeur par une virgule, donc pour un tableau vide nous auront une simple chaine vide.

Additionnons les chaines vides, et nous auront une chaine vide en retour, et comparons une chaine vide (falsy) avec le nombre 0 (falsy) et le résultat sera "vrai".

En bonus

console.log(Math.min())
// => Infinity

console.log(Math.max());
// => -Infinity

Instinctivement, on imaginerait que la méthode mathématique min() appelée sans paramètre retournerait la valeur -Infinity et inversement pour la méthode max().

La raison se trouve dans l'implémentation technique et comme Math.min(3) doit renvoyer 3 et que Math.min(3, 4) doit renvoyer 3 aussi, il faut avoir une valeur de comparaison absolue. Si cette valeur était initialisée à "Infinity", le premier exemple reviendrait à appeler Math.min(Infinity, 3) et le résultat serait donc erroné.


Vous avez terminé l'article ?

Commentaires (0)

pour laisser un commentaire

Aucun commentaire pour l'instant