Il n'y a pas si longtemps,
JavaScript offrait un nouveau
type de données
BigInt primitif pour travailler avec des nombres de précision arbitraires. Le minimum d'informations nécessaires a déjà été
dit /
traduit sur la motivation et les cas d'utilisation. Et je voudrais prêter un peu plus d'attention à l '"explicitness" local dépassé dans la conversion de type et
TypeError inattendu. Allons-nous gronder ou comprendre et pardonner (encore)?
Implicite devient explicite?
Dans un langage où la conversion de type implicite est utilisée depuis longtemps, elle est devenue un mème de presque toutes les conférences et peu de gens sont surpris par des subtilités telles que:
1 + {};
Nous obtenons soudainement une TypeError, essayant d'ajouter deux apparemment NUMBERS:
1 + 1n;
Et si l'expérience antérieure de l'implicitation n'a pas conduit à une rupture dans l'apprentissage de la langue, alors il y a une deuxième chance de décomposer et de jeter le manuel ECMA et d'entrer dans du Java.
De plus, le langage continue de «troll» pour les développeurs js:
1n + '1';
Oh oui, n'oubliez pas l'opérateur unaire
+ :
+1n;
En bref, nous ne pouvons pas mélanger
BigInt et
Number dans les opérations. Par conséquent, il n'est pas recommandé d'utiliser des «grands entiers» si 2 ^ 53-1 (
MAX_SAFE_INTEGER ) est suffisant pour nos besoins.
Décision clé
Oui, c'était la décision principale de cette innovation. Si vous oubliez qu'il s'agit de JavaScript, alors tout est tellement logique: ces conversions implicites contribuent à la perte d'informations.
Lorsque nous ajoutons deux valeurs de types numériques différents (grands entiers et nombres à virgule flottante), la valeur mathématique du résultat peut être en dehors de leur plage de valeurs possibles. Par exemple, la valeur de l'expression
(2n ** 53n + 1n) + 0,5 ne peut être représentée avec précision par aucun de ces types. Ce n'est plus un entier, mais un nombre réel, mais sa précision n'est plus garantie par le
format float64 :
2n ** 53n + 1n;
Dans la plupart des langages dynamiques, où les types d'entiers et de flottants sont représentés, les premiers sont écrits comme
1 et les seconds sont écrits comme
1.0 . Ainsi, lors d'opérations arithmétiques sur la présence d'un séparateur décimal dans l'opérande, on peut conclure que la précision du flottant dans les calculs est acceptable. Mais JavaScript n'en fait pas partie et
1 est un flottant! Et cela signifie que le calcul de
2n ** 53n + 1 renverra le flotteur 2 ^ 53. Ce qui, à son tour, brise les fonctionnalités clés de
BigInt :
2 ** 53 === 2 ** 53 + 1;
Eh bien, il n'y a aucune raison de parler de la mise en œuvre de la
"tour numérique" , car vous ne pourrez pas prendre le numéro existant comme type de données numériques générales (pour la même raison).
Et pour éviter ce problème, la
conversion implicite entre
Number et
BigInt dans les opérations a été interdite. Par conséquent, le «grand entier» ne peut pas être converti en toute sécurité dans une fonction JavaScript ou API Web, où le nombre habituel est attendu:
Math.max(1n, 10n);
Vous devez sélectionner explicitement l'un des deux types à l'aide de
Number () ou
BigInt () .
De plus, pour les opérations avec des types mixtes, il
existe une explication concernant une implémentation complexe ou une perte de performances, ce qui est assez courant pour les innovations de langage de compromis.
Bien sûr, cela s'applique aux conversions numériques implicites avec d'autres primitives:
1 + true;
Mais les concaténations suivantes (déjà) fonctionneront, car le résultat attendu est une chaîne:
1n + [0];
Une autre exception est sous la forme d'opérateurs de comparaison (comme
< ,
> et
== ) entre
Number et
BigInt . Il n'y a également aucune perte de précision, car le résultat est un booléen.
Eh bien, si vous vous souvenez du nouveau type de données
Symbol précédent, TypeError ne semble plus être un ajout aussi radical?
Symbol() + 1;
Et oui, mais non. En effet, conceptuellement, le symbole n'est pas du tout un nombre, mais un tout - beaucoup:
- Il est très peu probable que le symbole tombe dans une telle situation. Cependant, c'est très suspect et TypeError est tout à fait approprié ici.
- Il est très probable et habituel que le «grand tout» dans les opérations se révèle être l'un des opérandes alors qu'il n'y a vraiment rien de mal.
L'opérateur unary
+ lève une exception en raison d'un problème de compatibilité avec
asm.js , où
Number est attendu. Le plus unaire ne peut pas fonctionner avec
BigInt de la même manière que
Number , car dans ce cas, le code asm.js précédent deviendra ambigu.
Offre alternative
Malgré la relative simplicité et la «propreté» de la
mise en œuvre de
BigInt ,
Axel Rauschmeyer souligne le manque d'innovation. À savoir, sa seule rétrocompatibilité partielle avec le
numéro existant et les suivants:
Utilisez des nombres pour des nombres jusqu'à 53 bits. Utilisez des entiers si vous avez besoin de plus de bits
Comme alternative, il a
proposé ce qui suit .
Laissez
Number devenir le supertype pour les nouveaux
Int et
Double :
- typeof 123.0 === 'nombre' , et Number.isDouble (123.0) === true
- typeof 123 === 'nombre' , et Number.isInt (123) === true
Avec de nouvelles fonctions pour les
conversions Number.asInt () et
Number.asDouble () . Et, bien sûr, avec la surcharge de l'opérateur et les moulages nécessaires:
- Int × Double = Double (cast)
- Double × Int = Double (avec fonte)
- Double × Double = Double
- Int × Int = Int (tous les opérateurs sauf division)
Fait intéressant, dans la version simplifiée, cette phrase gère (au début) sans ajouter de nouveaux types à la langue. Au lieu de cela, la
définition du type de nombre se développe: en plus de tous les nombres possibles à double précision 64 bits (IEEE 754-2008), nombre inclut désormais tous les entiers.
Par conséquent, le «nombre inexact»
123,0 et le «nombre exact»
123 sont des numéros distincts du type de
numéro unique.
Cela semble très familier et raisonnable. Cependant, il s'agit d'une sérieuse mise à niveau du numéro existant, qui est plus susceptible de «casser le Web» et ses outils:
- Il y a une différence entre 1 et 1.0 , qui n'existait pas auparavant. Le code existant les utilise de manière interchangeable, ce qui, après la mise à niveau, peut être source de confusion (contrairement aux langues où cette différence était présente initialement).
- Il y a un effet lorsque 1 === 1.0 (c'est censé être une mise à niveau), et en même temps, Number.isDouble (1)! == Number.isDouble (1.0) : encore une fois, c'est comme ça.
- La «particularité» de l'égalité 2 ^ 53 et 2 ^ 53 + 1 disparaît, ce qui va casser le code qui en dépend.
- Le même problème de compatibilité avec asm.js et plus.
Par conséquent, nous avons finalement une solution de compromis sous la forme d'un nouveau type de données distinct. Il convient de souligner qu’une autre option a également été envisagée et
discutée .
Lorsque vous êtes assis sur deux chaises
En fait, le
commentaire du comité commence par les mots:
Trouver un équilibre entre maintenir l'intuition de l'utilisateur et préserver la précision
D'une part, je voulais enfin ajouter quelque chose d '«exact» à la langue. Et d'autre part, pour maintenir son comportement déjà familier pour de nombreux développeurs.
C'est juste que cet "exact" ne peut pas être ajouté, car vous ne pouvez pas le casser: les mathématiques, l'ergonomie du langage, asm.js, la
possibilité d'une nouvelle expansion du système de type , la productivité et, finalement, le web lui-même! Et vous ne pouvez pas tout casser en même temps, ce qui conduit au même.
Et vous ne pouvez pas briser l’intuition des utilisateurs de langues, qui, bien sûr, a également été
vivement débattue . C'est vrai, ça a marché?