不久之前,
JavaScript 吹嘘了一种新的原始
BigInt数据
类型 ,可以处理任意精度的数字。 关于动机和用例的必要信息已经被
告知 /
翻译 。 我想更多地关注类型转换中夸大的局部“显式性”和意外的
TypeError 。 我们会再次责骂或理解并原谅吗?
隐含变得显露?
在隐式类型转换已被长期使用的语言中,它已成为几乎所有会议的模因,而很少有人对诸如此类的复杂性感到惊讶:
1 + {};
我们突然收到一个TypeError,试图添加两个看似数字:
1 + 1n;
而且,如果以前的隐含经验并未导致学习该语言的失败,那么就有第二次机会来分解并扔掉ECMA教科书并使用Java。
此外,该语言继续吸引js开发人员:
1n + '1';
哦,是的,不要忘记一元
+运算符:
+1n;
简而言之,我们不能在操作中混合使用
BigInt和
Number 。 因此,如果2 ^ 53-1(
MAX_SAFE_INTEGER )足以满足我们的目的,则不建议使用“大整数”。
关键决定
是的,这是这项创新的主要决定。 如果您忘记了这是JavaScript,那么一切都是那么合理:这些隐式转换会导致信息丢失。
当我们将两个不同数字类型的值(大整数和浮点数)相加时,结果的数学值可能超出了它们的可能值范围。 例如,表达式
(2n ** 53n + 1n)+ 0.5的值不能用这些类型中的任何一种准确表示。 这不再是整数,而是一个实数,但
float64格式不再保证其准确性:
2n ** 53n + 1n;
在大多数动态语言中,表示整数和浮点数的类型时,前者写为
1 ,后者写为
1.0 。 因此,在操作数中存在小数点分隔符的算术运算期间,我们可以得出结论,计算中的浮点精度是可以接受的。 但是JavaScript并不是其中之一,
1是浮点数! 这意味着计算
2n ** 53n +1将返回浮点数2 ^ 53。 反过来,这破坏了
BigInt的关键功能:
2 ** 53 === 2 ** 53 + 1;
嗯,也没有理由谈论
“数字塔”的实现,因为您不会成功地将现有数字作为通用数字数据类型(出于同样的原因)。
为避免此问题,禁止在操作中在
Number和
BigInt之间进行隐式转换。 结果,“大整数”不能安全地转换为任何需要通常数字的JavaScript或Web API函数:
Math.max(1n, 10n);
您必须使用
Number()或
BigInt()显式选择两种类型之一。
另外,对于混合类型的操作,有
一个关于复杂的实现或性能损失
的解释 ,这在折衷的语言创新中很常见。
当然,这适用于其他原语的隐式数值转换:
1 + true;
但是,以下(已经)的串联将起作用,因为预期结果是字符串:
1n + [0];
另一个例外是
Number和
BigInt之间的比较运算符(例如
< ,
>和
== )形式。 由于结果是布尔值,因此也不会损失准确性。
好吧,如果您还记得以前的新
Symbol符号数据类型,那么TypeError不再看起来像是一个如此激进的补充吗?
Symbol() + 1;
是的,但是没有。 实际上,概念上的符号根本不是数字,而是一个整体-非常:
- 符号极不可能落入这种情况。 但是,这非常可疑,TypeError在这里非常合适。
- 当确实没有错的时候,运算中的“大整体”很有可能成为运算元之一。
由于与
asm.js的兼容性问题,一元
+运算符会引发异常,其中
Number是预期的。 一元加号不能以与
Number相同的方式与
BigInt一起使用 ,因为在这种情况下,先前的asm.js代码将变得模棱两可。
替代报价
尽管
BigInt实现相对简单和“干净”,但
Axel Rauschmeyer强调缺乏创新。 即,它仅与现有
Number及其随后的部分向后兼容:
最多使用53位整数。 如果需要更多位,请使用整数
作为替代方案,他
提出了以下建议 。
让
Number成为新的
Int和
Double的超类型:
- typeof 123.0 ==='number'和Number.isDouble(123.0)=== true
- typeof 123 ==='number'和Number.isInt(123)=== true
使用
Number.asInt()和
Number.asDouble()转换的新函数。 当然,还有运算符重载和必要的强制转换:
- Int×Double = Double(cast)
- Double×Int = Double(带强制转换)
- 双倍×双倍=双倍
- Int×Int = Int(除除运算符外的所有运算符)
有趣的是,在简化版本中,这句话在管理时(首先)没有在语言中添加新类型。 取而代之的是
,“数字类型”的
定义扩展了:除了所有可能的64位双精度数字(IEEE 754-2008),数字现在还包括所有整数。 结果,“不正确的数字”
123.0和“精确的数字”
123是单个
数字类型的分开的数字。
它看起来非常熟悉且合理。 但是,这是对现有数字的严重升级,很可能会“破坏网络”及其工具:
- 1和1.0之间存在差异,以前是没有的。 现有代码可互换使用,升级后可能导致混乱(与最初存在这种差异的语言不同)。
- 当1 === 1.0 (应该是升级)并且同时Number.isDouble(1)!== Number.isDouble(1.0)时会起作用 :同样,就像这样。
- 等式2 ^ 53和2 ^ 53 +1的“特殊性”消失了,这将破坏依赖于此的代码。
- 与asm.js或更多版本存在相同的兼容性问题。
因此,最后,我们有了一种折衷的解决方案,采用了新的单独数据类型。 值得强调的是,还考虑并
讨论了另一种选择。
当你坐在两把椅子上
实际上,
委员会的评论开头是这样的:
在保持用户直觉和保持精度之间找到平衡
一方面,我终于想在语言中添加一些“精确”的东西。 另一方面,保持许多开发人员已经熟悉的行为。
只是无法添加此“精确”字,因为您无法破坏它:数学,语言的人体工程学,asm.js,
类型系统进一步扩展的
可能性 ,生产力以及最终的网络本身! 而且,您不能同时破坏所有这些,从而导致相同的结果。
而且您无法打破语言用户的直觉,当然,这也
引起了激烈的争论 。 是的,结果可行吗?