2

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不需要加载解析编译的额外时间,并且在性能上表现更好。

Snipaste_2019-10-11_16-15-52-3ca05966-1757-413f-a051-29c2b22e8e62
图示为BigInt与其他流行库在Chrome中的表现情况对比(值越大表现越好)

现状:Number

JavaScript中Number是以64位双精度浮点型存储,所以会有精度限制,JavaScript中可以准确表示的最大整数是Number.MAX_SAFE_INTEGER这个值是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+1max+2的结果一致,这就导致我们无法保证在JavaScript中获取到的这个值的准确性,JavaScript中任何超出安全值范围的计算都会丢失精度,正因为如此我们只能信任安全值范围内的整数。

新热点:BigInt

BigInt是JavaScript中一种可以用来表示任意精度(arbitrary precision)整数的基本数据类型,使用BigInt可以安全的存储和操作任意大小的整数而不受Number类型的安全值范围的限制。

生成一个BigInt类型的值只需要在任意整数后加上n做后缀即可。例如:123BigInt类型表示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类型的超出安全范围的值,因此当混用BigIntNumber时会报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) BigInt64ArrayBigUint64Array

  1. BigInt函数,这个BigInt全局构造函数和Number的构造函数类似,将传入的参数转化为BigInt类型,如果转化失败,会报SyntaxError或者RangeError
    BigInt(123);
    // -> 123n
    BigInt(1.2);
    // -> RangeError
    BigInt('1.2');
    // -> SyntaxError
  1. 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
  1. BigInt使得准确表示其他编程语言中常用的64位有符号和无符号整数成为可能,其中BigInt64ArrayBigUint64Array可以使我们更加容易且有效地表示和操作此类值的列表。
    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正在实现中

原文链接:JavaScript基本类型之--BigInt


LinearEnter
96 声望1 粉丝

[链接]