
Quelle que soit votre expérience de programmation JavaScript, vous ne comprenez probablement pas parfaitement le langage. Ce guide concis explore les types plus en profondeur que tous les livres existants: vous apprendrez comment les types fonctionnent, les problèmes de leur conversion et apprendrez à utiliser les nouvelles fonctionnalités.
Comme d'autres livres de la série
«You Don't Know JS» , il montre des aspects non triviaux du langage que les programmeurs JavaScript préfèrent éviter (ou supposent qu'ils n'existent pas). Armé de ces connaissances, vous obtiendrez une véritable maîtrise de JavaScript.
Extrait. L'égalité est stricte et non stricte.
L'égalité non stricte est vérifiée par l'opérateur ==, et l'égalité stricte par l'opérateur ===. Les deux opérateurs sont utilisés pour comparer deux valeurs pour «égalité», mais le choix de la forme (stricte / non stricte) conduit à des différences de comportement très importantes, notamment dans la façon dont la décision est prise sur l'égalité.
Il existe une idée fausse commune concernant ces deux opérateurs: "== vérifie l'égalité de valeur et === vérifie l'égalité des valeurs et des types." Cela semble raisonnable
mais inexact. D'innombrables livres et blogs JavaScript réputés le disent, mais malheureusement, ils ont tous tort.
La description correcte est: "== autorise la conversion de type lors de la vérification de l'égalité et === interdit la conversion de type."
Performance de vérification de l'égalité
Arrêtez-vous et réfléchissez à la façon dont la première explication (inexacte) diffère de la seconde (exacte).
Dans la première explication, il semble évident que l'opérateur === fait plus de travail que == car il doit également vérifier le type.
Dans la deuxième explication, l'opérateur == fait plus de travail, car avec différents types, il doit passer par la conversion de type.
Ne tombez pas dans le piège dans lequel beaucoup tombent. Ne pensez pas que cela affectera en quelque sorte la vitesse du programme, et == sera beaucoup plus lent ===. Bien que la conversion prenne un certain temps, elle prend quelques microsecondes (oui, des millionièmes de seconde).
Si vous comparez deux valeurs du même type, == et === utilisent le même algorithme, donc si vous ne tenez pas compte des petites différences dans l'implémentation du moteur, ils doivent en effectuer un
et le même travail.
Si vous comparez deux valeurs de types différents, les performances ne sont pas un facteur important. Vous devez vous demander autre chose: si je compare deux valeurs, est-ce que je veux que la conversion de type se produise ou non?
Si vous avez besoin d'une conversion, utilisez l'égalité non stricte ==, et si la conversion n'est pas souhaitable, utilisez l'égalité stricte ===.
Les deux opérateurs, == et ===, vérifient les types de leurs opérandes. La différence est de savoir comment ils réagissent à la non-correspondance de type.
Vérification de l'égalité abstraite
Le comportement de l'opérateur == est défini à la section 11.9.3 de la spécification ES5 («algorithme de contrôle d'égalité abstrait»). Voici un algorithme détaillé mais simple, avec une liste explicite de toutes les combinaisons possibles de types et de méthodes de conversion de type (si nécessaire) qui devraient être appliquées dans chaque combinaison.
Lorsque quelqu'un condamne la conversion de type (implicite) comme étant trop complexe et contenant trop de défauts pour une utilisation pratique utile, il condamne les règles du "contrôle d'égalité abstrait". On dit généralement que ce mécanisme est trop compliqué et artificiel pour une étude et une utilisation pratiques, et qu'il crée des erreurs dans les programmes JS plutôt que de simplifier la lecture du code.
Je crois que c'est une hypothèse erronée - vous, lecteurs, êtes des développeurs compétents qui écrivent des algorithmes, c'est-à-dire du code (et aussi le lisent et le comprennent), toute la journée. Pour cette raison, je vais essayer d'expliquer le "test d'égalité abstraite" en termes simples. Cependant, je recommande également de lire la section 11.9.3 de la spécification ES5. Je pense que cela vous surprendra à quel point tout est logique.
En fait, la première section (11.9.3.1) indique que si deux valeurs comparées sont du même type, elles sont comparées de manière simple et naturelle. Par exemple, 42 n'est que 42 et la chaîne «abc» n'est que «abc».
Quelques exceptions mineures à garder à l'esprit:
- La valeur de NaN n'est jamais égale à elle-même (voir chapitre 2).
- +0 et -0 sont égaux (voir chapitre 2).
La dernière section de la section 11.9.3.1 est consacrée à un test rigoureux de == égalité avec les objets (y compris les fonctions et les tableaux). Deux de ces valeurs ne sont égales
que si les deux se réfèrent exactement à la
même valeur . Aucune conversion de type n'est effectuée.
Un contrôle d'égalité stricte === est défini de manière identique à 11.9.3.1, y compris la disposition pour deux valeurs d'objet. Ce fait est très peu connu, mais == et === se comportent de manière totalement identique lors de la comparaison de deux objets!
Le reste de l'algorithme en 11.9.3 indique que l'égalité non stricte == peut être utilisée pour comparer deux types de valeurs différents, l'un ou les deux nécessiteront
conversion implicite. À la suite de la conversion, les désignations sont converties en un seul type, après quoi elles peuvent être directement comparées pour l'égalité par une identité simple
valeurs.
Le fonctionnement d'une faible vérification de l'inégalité! = Est déterminé exactement comme on pourrait s'y attendre; en fait, l'opération == est entièrement mise en œuvre, suivie d'un calcul
déni de résultat. Il en va de même pour l'opération de vérification stricte de l'inégalité! ==.
Comparaison: chaînes et nombres
Pour illustrer la conversion de ==, créez d'abord des exemples de chaînes et de nombres, ce qui a été fait plus tôt dans ce chapitre:
var a = 42; var b = "42"; a === b;
Comme prévu, la vérification a === b échoue car la conversion n'est pas autorisée et les valeurs 42 et "42" sont différentes.
Cependant, dans la deuxième comparaison a == b, l'égalité non stricte est utilisée; cela signifie que si les types sont différents, l'algorithme de comparaison effectuera une conversion implicite d'un
ou les deux.
Mais quel type de conversion est effectué ici? La valeur a, c'est-à-dire 42, deviendra-t-elle une chaîne ou la valeur b "42" deviendra-t-elle un nombre? La spécification ES5 dans les sections 11.9.3.4–5 dit:
- Si Type (x) est de type Number et Type (y) est de type String, retournez le résultat de la comparaison x == ToNumber (y).
- Si Type (x) est de type String et Type (y) est de type Number, retourne le résultat de la comparaison ToNumber (x) == y.
Dans la spécification, les noms formels des types Number et String sont utilisés, tandis que dans le livre pour les types primitifs, le numéro de notation et la chaîne sont généralement utilisés. Ne confondez pas la casse du symbole numérique dans la spécification avec la fonction intégrée Number (). Pour nos besoins, la casse des caractères au nom du type ne joue pas de rôle - ils signifient la même chose.
La spécification indique que la valeur "42" est convertie en un nombre pour comparaison. À propos de la façon dont la conversion est effectuée, elle a déjà été décrite précédemment, et en particulier lors de la description de l'opération abstraite ToNumber. Dans ce cas, c'est assez évident
que les deux valeurs résultantes de 42 sont égales.
Comparaison: n'importe quoi avec des booléens
L'un des pièges les plus dangereux dans la conversion implicite de type == est rencontré lors de la tentative de comparaison directe des valeurs avec vrai ou faux.
Un exemple:
var a = "42"; var b = true; a == b;
Attendez, que se passe-t-il ici? Nous savons que «42» est le vrai sens (voir plus haut dans ce chapitre). Comment se révèle-t-il que la comparaison avec vrai avec la déclaration d'égalité stricte ==
ne donne pas vrai?
La raison est simple et trompeuse à la fois. Il est facile de se méprendre, de nombreux développeurs JS ne font pas l'effort de le comprendre pleinement.
Encore une fois, nous citons la spécification, sections 11.9.3.6–7:
- Si Type (x) est de type booléen, retournez le résultat de la comparaison ToNumber (x) == y.
- Si Type (y) est de type booléen, retournez le résultat de la comparaison x == ToNumber (y).
Voyons voir ce qui est ici. Première étape:
var x = true; var y = "42"; x == y;
Le type (x) appartient vraiment au type booléen, donc l'opération ToNumber (x) est effectuée, ce qui convertit true en 1. La condition 1 == «42» est maintenant calculée. Les types sont toujours différents, donc (presque récursivement) l'algorithme se répète; comme dans le cas précédent, "42" est converti en 42, et la condition 1 == 42 est évidemment fausse.
Si vous échangez des opérandes, le résultat restera le même:
var x = "42"; var y = false; x == y;
Cette fois, Type (y) est de type booléen, donc ToNumber (y) donne 0. La condition "42" == 0 devient récursivement 42 == 0, ce qui, bien sûr, est faux.
En d'autres termes, la valeur "42" n'est ni == vraie ni == fausse. À première vue, cette affirmation semble totalement impensable. Comment le sens ne peut-il être ni vrai ni faux?
Mais c'est ça le problème! Vous posez la mauvaise question. Bien que ce ne soit pas de votre faute, c'est le cerveau qui vous trompe.
La valeur "42" est en effet vraie, mais la construction "42" == true n'effectue pas du tout un test booléen / transform, quoi qu'en dise votre cerveau. "42" ne se convertit pas en booléen (vrai); au lieu de cela, true est converti en 1, puis «42» est converti en 42.
Que cela vous plaise ou non, ToBoolean n'est pas utilisé du tout ici, donc la vérité ou le mensonge de «42» n'est pas du tout important pour l'opération ==! Il est important de comprendre comment l'algorithme de comparaison == se comporte dans toutes les différentes combinaisons de types. Si la valeur booléenne est d'un côté, elle est toujours d'abord convertie en nombre.
Si cela vous semble étrange, vous n'êtes pas seul. Personnellement, je recommande de ne jamais, jamais, en aucun cas, utiliser == true ou == false. Jamais.
Mais rappelez-vous que je ne parle que de == ici. Les constructions === true et === false ne permettent pas la conversion de type, elles sont donc protégées de la conversion ToNumber cachée.
Un exemple:
var a = "42";
Si vous évitez == true ou == false (égalité lâche avec booléen) dans votre code, vous n'aurez jamais à vous soucier de ce piège vérité / fausseté.
Comparaison: null avec undefined
Un autre exemple de conversion implicite se produit lorsque vous utilisez l'égalité lax == entre les valeurs nulles et non définies. Encore une fois, je citerai la spécification ES5,
sections 11.9.3.2-3:
- Si x contient null et y contient undefined, retournez true.
- Si x contient undefined et y contient null, retourne true.
Null et indéfini par rapport à l'opérateur non strict == sont égaux les uns aux autres (c'est-à-dire qu'ils sont convertis les uns aux autres), et aucune autre valeur dans la langue entière.
Pour nous, cela signifie que null et undefined peuvent être considérés comme indiscernables à des fins de comparaison si vous utilisez l'opérateur de test d'égalité non strict ==, qui permet leur conversion implicite mutuelle:
var a = null; var b; a == b;
La conversion entre null et undefined est sûre et prévisible, et aucune autre valeur ne peut donner de faux positifs pour une telle vérification. Je recommande d'utiliser cette conversion pour que null et undefined ne diffèrent pas dans le programme et soient interprétés comme une valeur unique.
Un exemple:
var a = doSomething(); if (a == null) {
La vérification a == null réussit uniquement si doSomething () renvoie null ou undefined et échoue pour toute autre valeur (y compris 0, false et "").
La forme explicite de cette vérification, qui interdit de telles conversions de type, semble (à mon avis) beaucoup plus laide et peut fonctionner un peu moins efficacement!
var a = doSomething(); if (a === undefined || a === null) {
Je crois que la forme a == null est un autre exemple d'une situation dans laquelle une conversion implicite facilite la lecture du code, mais le fait de manière fiable et sûre.
Comparaison: objets et non-objets
Si un objet / une fonction / un tableau est comparé à une simple primitive scalaire (chaîne, nombre ou booléen), la spécification ES5 indique ce qui suit (section 11.9.3.8–9):
- Si Type (x) est de type String ou Number et Type (y) est de type Object, retournez le résultat de la comparaison x == ToPrimitive (y).
- Si Type (x) est de type Object et Type (y) est de type String ou Number, retournez le résultat de la comparaison ToPrimitive (x) == y.
Vous avez peut-être remarqué que dans ces sections de la spécification, seuls String et Number sont mentionnés, mais pas booléens. Le fait est que, comme mentionné ci-dessus, les sections 11.9.3.6 à 7 garantissent que tout opérande booléen est d'abord représenté par Number.
Un exemple:
var a = 42; var b = [ 42 ]; a == b;
Pour la valeur [42], l'opération abstraite ToPrimitive est appelée (voir "Opérations abstraites"), ce qui donne le résultat "42". A partir de ce moment, la condition simple "42" == 42 reste, qui, comme nous l'avons déjà découvert, se transforme en 42 == 42, de sorte que a et b sont égaux jusqu'à la conversion de type.
Comme vous vous en doutez, toutes les fonctionnalités de l'opération ToPrimitive abstraite discutées plus haut dans ce chapitre ((toString (), valueOf ()) sont également applicables dans ce cas. Cela peut être très utile si vous avez une structure de données complexe et que vous souhaitez lui définir une méthode spécialisée valueOf (), qui devra fournir une valeur simple aux fins de vérification de l'égalité.
Le chapitre 3 a examiné le «déballage» d'un wrapper d'objet autour d'une valeur primitive (comme dans la nouvelle chaîne («abc»), par exemple), entraînant le retour de la primitive sous-jacente
valeur ("abc"). Ce comportement est lié à la transformation ToPrimitive dans l'algorithme ==:
var a = "abc"; var b = Object( a );
a == b donne la valeur true car b est converti (ou «décompressé») par l'opération ToPrimitive en la valeur primitive scalaire simple de base «abc», qui correspond à la valeur de a.
Il existe certaines valeurs pour lesquelles ce n'est pas le cas en raison d'autres règles de substitution dans l'algorithme ==. Un exemple:
var a = null; var b = Object( a );
Les valeurs nulles et non définies ne peuvent pas être compressées (elles n'ont pas de wrapper d'objet équivalent), donc Object (null) n'est pas fondamentalement différent d'Object (): les deux appels créent l'habituel
aucun objet.
NaN peut être empaqueté dans l'encapsuleur d'objet Number équivalent, mais lorsque == provoque le décompactage, la comparaison NaN == NaN échoue car la valeur NaN n'est jamais égale à elle-même (voir chapitre 2).
»Plus d'informations sur le livre sont disponibles sur
le site Web de l'éditeur»
Contenu»
Extrait25% de réduction sur les colporteurs -
JavaScriptLors du paiement de la version papier du livre, un livre électronique est envoyé par e-mail.