【第1751期】JavaScript基本类型之--BigInt
前言
BigInt可以了解一下了。今日早读文章由跟谁学前端工程师@刘磊投稿分享。
正文从这开始~~
BigInt目前已经进入Stage 4阶段 下一个版本将会作为新特性出现在ECMAScript,下面我们来一起了解一下Bigint。
BigInt是什么? BigInt是JavaScript中一种可以用来表示任意精度整数的基本数据类型
BigInt可以用来表示任意精度整数的特性为JavaScript解锁了更多的骚操作,使用BigInt可以告别过去因为整数运算导致溢出的痛苦。特别是金融方面因为涉及大量的数据运算,比如高精度时间戳,或者数值过大的ID,这些是无法安全的用Number类型去存储的,所以退而求其次使用String类型去存储,有了BigInt类型后就可以安全的将其存储为数值类型。
另外BigInt的实现也为实现BigDecimal打下坚实基础,那将对于以十进制精度表示货币金额并对其进行精确运算(也就是0.10 + 0.20 !== 0.30问题)非常有帮助
此前已经有不少库实现了BigInt式的整数存储,当BigInt完全可用时,就可以拿掉那些依赖了,因为相比于使用这些依赖库,Native BigInt则更具优势。因为与之相比,NativeBigInt不需要加载解析编译的额外时间,并且在性能上表现更好。
现状:Number
JavaScript中Number是以64位双精度浮点型存储,所以会有精度限制,JavaScript中可以准确表示的最大整数是Number.MAXSAFEINTEGER这个值是2^53-1
const max = Number.MAX_SAFE_INTEGER;
// → 9_007_199_254_740_991
Tips:为了可读性使用下划线作为分隔符进行分组 The numeric literal separators proposal
当自增一次时,可以得到正确值:
max + 1;
// 9_007_199_254_740_992 ✅
当自增两次时,我们发现结果并非预期
max + 2;
// → 9_007_199_254_740_992 ❌
max+1 和max+2的结果一致,这就导致我们无法保证在JavaScript中获取到的这个值的准确性,JavaScript中任何超出安全值范围的计算都会丢失精度,正因为如此我们只能信任安全值范围内的整数。
新热点:BigInt
BigInt是JavaScript中一种可以用来表示任意精度(arbitrary precision)整数的基本数据类型,使用BigInt可以安全的存储和操作任意大小的整数而不受Number类型的安全值范围的限制。
生成一个BigInt类型的值只需要在任意整数后加上n做后缀即可。例如:123 用BigInt类型表示123n,也可以通过全局函数BigInt(number)来将Number类型转化为BigInt类型,换言之BigInt(123) === 123n,让我们用BigInt来解决一下上文所提到的问题
BigInt(Number.MAX_SAFE_INTEGER) + 2n;
// 9_007_199_254_740_993n ✅
再看一个两个Number类型的数值相乘的例子
1234567890123456789*123;
// -> 151851850485185200000 ❌
仔细看这个结果肯定是不对的,因为最低位一个是9一个是3所以正确值肯定是以7结尾的(3*9=27),但是这里却是一串0结尾,我们来用BigInt重新计算一下
1234567890123456789n*123n;
// -> 151851850485185185047n ✅
很显然这次是对的,当我们用BigInt来处理时不会受到Number中的安全值范围的限制,所以不用担心精度丢失
BigInt是JavaScript中新的的基础类型,所以可以用typeof操作符去检测
typeof 123
// 'number'
typeof 123n
// 'bigint'
因为Bigint是一个单独的类型,所以BigInt类型值和Number严格模式下不相等,e.g. 4!== 4n,BigInt类型和Number类型作比较时需要将自身类型转化为相互的类型,或者直接使用严格相等(===)
4n === BigInt(4);
// => true
4n == 4;
// => true
4n === 4;
// => false
当强制类型转化为布尔值时(例如在使用if,&&,||,或者Boolean(int)时触发),BigInt遵循和Numebr一样的规则
if(0n){
console.log('if');
}else{
console.log('else');
}
// 输出:'else', 因为0n是假值
0n || 12n
// -> 12n
0n && 12n
// -> 0n
Boolean(0n);
// -> false
Boolean(12n);
// -> true
!12n
// -> false
!0n
// -> true
BigInt支持那些常见的运算符例如:+,-,,/ * %,包括一些按位运算符如|, & , <<, >> ^ ,它和Number类型值的表现一致
(7 + 6 - 5) * 4 ** 3 / 2 % 3;
// → 1
(7n + 6n - 5n) * 4n ** 3n / 2n % 3n;
// → 1n
一元运算符-可以用来表示BigInt中的负数,如:-42n ,但是一元运算符+不支持,因为如果支持则会导致+x表示结果为非Number值,从而引起和现有逻辑的冲突。
一个值得注意的点是不要混合操作BigInt类型和Number类型,因为任何隐式强制类型转化都会导致精度丢失,看下面的例子:
BigInt(Number.MAX_SAFE_INTEGER)+2.5
// => ?? 🤔
可以猜一下结果,其实并没有合理的答案。因为BigInt无法表示小数,而Number则无法正确表示BigInt类型的超出安全范围的值,因此当混用BigInt和Number时会报TypeError
其中唯一的例外是比较运算符,比如 === < > <= >=等,因为这类操作符最终会返回一个布尔类型值,不存在精度丢失的情况:
1+1n
// -> TypeError
123<124n;
// -> true
建议:BigInt和Number一般情况下不要混合操作,BigInt对于可能操作较大整数的情况下是合理的选择,Number则对于在安全值范围内的操作更合适,所以选定一种合适的类型用下去,不要相互混用。
注意⚠️:额外需要注意的一点是无符号右移操作符>>>,因为BigInt始终是有符号的所以无符号右移操作符对于BigInt来说不会生效。
API
关于BigInt的几个API BigInt() BigInt.asIntN(width, value) BigInt.asUintN(width, value) BigInt64Array,BigUint64Array
BigInt函数,这个BigInt全局构造函数和Number的构造函数类似,将传入的参数转化为BigInt类型,如果转化失败,会报SyntaxError或者RangeError
BigInt(123);
// -> 123n
BigInt(1.2);
// -> RangeError
BigInt('1.2');
// -> SyntaxError
BigInt.asIntN(width, value) BigInt.asUintN(width, value),通过这两个库函数,可以将BigInt值包装为有符号或无符号整数,并限制在特定位数。其中BigInt.asIntN(width,value)将BigInt类型值包装为有符号二进制整数,BigInt.asUintN(width,value)将BigInt类型值包装为无符号二进制整数。例如:如果你要执行64位算术运算,则可以使用它们来将其保持在适当的范围内:
// BigInt类型值所能表示的最大的有符号的64位整数值
const max = 2n**(64n - 1n) - 1n;
BigInt.asIntN(64,max);
// -> 9_223_372_036_854_775_807n
BigInt.asIntN(64, max+1n);
// -> -9_223_372_036_854_775_808n
// ^ 变为负值 因为溢出了
// 一旦传给超过64位整数范围(即63位的绝对数值+1位符号位)的BigInt值,就会发生溢出。
BigInt.asUintN(64,max);
// -> 9_223_372_036_854_775_807n
BigInt.asUintN(64,max+1n)
// -> 9_223_372_036_854_775_808n
BigInt使得准确表示其他编程语言中常用的64位有符号和无符号整数成为可能,其中BigInt64Array和BigUint64Array可以使我们更加容易且有效地表示和操作此类值的列表。
const view = new BigInt64Array(4);
// -> [0n,0n,0n,0n]
view.length;
// -> 4
view[0];
// -> 0n
view[0] = 40n;
view[0];
// -> 40n
BigInt64Array可以确保其值保持在有符号的64位限制范围内。BigUint64Array则确保其值保持在无符号位的64位限制范围内
// BigInt类型值所能表示的最大的有符号的64位整数值
const max = 2n**(64n - 1n) - 1n;
view[0] = max;
view[0]
// -> 9_223_372_036_854_775_807n
view[0] = max + 1n;
view[0];
// -> -9_223_372_036_854_775_808n
// ^ 溢出了
const view_u = new BigUint64Array(4);
view_u[0] = max;
view_u[0];
// -> 9_223_372_036_854_775_807n
view_u[0] = max+1n;
view_u[0];
// -> 9_223_372_036_854_775_808n
兼容性
到目前为止已经实现BigInt的有Chrome(67+),Firefox(68+),Opear(54+),Node(10.4.0+),其中Safari正在实现中
参考链接:
proposal-bigint
bigint
BigInt: arbitrary-precision integers in JavaScript
关于本文 作者:@刘磊 原文:https://github.com/LiuL0703/blog/issues/30
为你推荐
【第1745期】现代 JavaScript 教程 - 类型检测:"instanceof"