作者简介:
李中凯老师,8年前端开发,前端负责人,擅长JavaScript/Vue。
公众号:1024译站
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
主要分享:Vue.js, JavaScript,CSS

BigInt数据类型是为了让JavaScript程序能表示超出Number 类型支持的数值范围。在对大整数进行数学运算时,以任意精度表示整数的能力尤为重要。有了BigInt,整数溢出将不再是一个问题。

此外,你可以安全地使用高精度时间戳、大整数 ID 等,而不必使用任何变通方法。BigInt目前处于 stage 3 提案阶段。一旦加入到规范中,它将成为JavaScript中的第二种数字数据类型,这将使支持的数据类型总数达到8个:

  • Boolean

  • Null

  • Undefined

  • Number

  • BigInt

  • String

  • Symbol

  • Object

在本文中,我们将仔细研究BigInt,并了解它如何帮助克服JavaScript中Number类型的限制。

问题

对于来自其他语言的程序员来说,JavaScript中缺乏显式整数类型常常令人困惑。许多编程语言支持多种数字类型,如float、double、integer和bignum,但JavaScript不是这样。在JavaScript中,所有数字都以双精度64位浮点格式表示,这是由IEEE 754-2008标准定义的。

在此标准下,无法精确表示的非常大的整数将自动四舍五入。准确地说,JavaScript中的Number 类型只能安全地表示 -9007199254740991 (-(253-1))和 9007199254740991 (253-1)之间的整数。任何超出此范围的整数值都可能丢失精度。

这个很容易验证,执行以下代码:

console.log(9999999999999999);    // → 10000000000000000

该整数大于JavaScript可以用 Number原始类型表示的最大数字。因此,它被舍入了。意外的舍入可能会损害程序的可靠性和安全性。这是另一个例子:

// 注意最后一位
9007199254740992 === 9007199254740993;    // → true

JavaScript提供了Number.MAX_SAFE_INTEGER 常量,允许你在JavaScript中快速获得最大安全整数。类似地,你可以通过使用Number.MIN_SAFE_INTEGER常量获得最小安全整数:

const minInt = Number.MIN_SAFE_INTEGER;console.log(minInt);         // → -9007199254740991console.log(minInt - 5);     // → -9007199254740996// 注意它是如何输出与上面相同的值的
console.log(minInt - 4);     // → -9007199254740996

解决办法

为了解决这些限制,一些JavaScript开发人员使用String类型表示大整数。例如,Twitter API在使用JSON响应时向对象添加了一个id的字符串版本。此外,还开发了一些库,如bignumber.js,以便更容易地处理大整数。

有了 BigInt,应用程序不再需要一个变通方法或库来安全地表示Number.MAX_SAFE_INTEGER 和Number.Min_SAFE_INTEGER之外的整数。现在可以在标准JavaScript中执行对大整数的算术操作,而不会有丢失精度的风险。在第三方库上使用原生数据类型的好处是更好的运行时性能。

要创建BigInt,只需将n附加到整数的末尾。对比一下:

console.log(9007199254740995n);    // → 9007199254740995n
console.log(9007199254740995);     // → 9007199254740996

或者,你可以调用BigInt()构造函数:

BigInt("9007199254740995");    // → 9007199254740995n

BigInt 字面量也可以写成二进制、八进制或十六进制形式:

// 二进制
console.log(0b100000000000000000000000000000000000000000000000000011n);
// → 9007199254740995n// 十六进制
console.log(0x20000000000003n);
// → 9007199254740995n// 八进制
console.log(0o400000000000000003n);
// → 9007199254740995n//注意,不支持旧式八进制语法
console.log(0400000000000000003n);
// → SyntaxError

记住,不能使用严格的相等运算符来比较BigInt和普通数字,因为它们不是同一类型的:

console.log(10n === 10);    // → falseconsole.log(typeof 10n);    // → bigint
console.log(typeof 10);     // → number

相反,你可以使用相等运算符,它在处理操作数之前执行隐式类型转换:

console.log(10n == 10);    // → true

所有算术运算符都可以在BigInt上使用,除了一元加号(+)运算符:

10n + 20n;    // → 30n
10n - 20n;    // → -10n
+10n;         // → TypeError: Cannot convert a BigInt value to a number
-10n;         // → -10n
10n * 20n;    // → 200n
20n / 10n;    // → 2n
23n % 10n;    // → 3n
10n ** 3n;    // → 1000nlet x = 10n;
++x;          // → 11n
--x;          // → 10n

不支持一元加号(+)运算符的原因是,有些程序可能依赖于这样的结果:+总是产生Number类型的值,或者抛出异常。改变+ 的行为也会破坏asm.js代码。

当然,当与BigInt操作数一起使用时,算术运算符应该返回一个BigInt值。因此,除法(/)运算符的结果会自动四舍五入到最接近的整数。例如:

25 / 10;      // → 2.5
25n / 10n;    // → 2n

隐式类型转换

因为隐式类型转换可能丢失信息,所以不允许BigIntNumber之间的混合操作。当混合使用大整数和浮点数时,结果值可能无法用BigIntNumber准确表示。看看下面的例子:

(9007199254740992n + 1n) + 0.5

这个表达式的结果在BigIntNumber的范围之外。带有小数部分的Number不能准确地转换为BigInt。大于253的BigInt不能准确转换为Number

由于这个限制,不能使用Number和 BigInt操作数的组合来执行算术运算。你也不能将 BigInt传递给Web API和期望Number类型参数的内置JavaScript函数。试图这样做会导致TypeError:

10 + 10n;    // → TypeError
Math.max(2n, 4n, 6n);    // → TypeError

注意,关系运算符不遵循此规则,如下例所示:

10n > 5;    // → true

如果希望使用BigInt和 Number执行算术计算,首先需要确定应该在哪个域中执行操作。为此,只需通过调用Number()BigInt()来转换操作数:

BigInt(10) + 10n;    // → 20n
// or
10 + Number(10n);    // → 20

当遇到Boolean上下文时,BigInt被视为类似于Number。换句话说,只要不是0n, BigInt就被认为是一个布尔真值:

if (5n) {// 这个代码块将被执行
}if (0n) {// 但这个不会
}

BigInt和 Number进行排序时,不会发生隐式类型转换:

const arr = [3n, 4, 2, 1n, 0, -1n];arr.sort();    // → [-1n, 0, 1n, 2, 3n, 4]

按位运算符如|&<<>>和 ^ 操作 BigIntNumber类似。负数被解释为无穷长二进制补码。不允许混合操作数。以下是一些例子:

90 | 115;      // → 123
90n | 115n;    // → 123n
90n | 115;     // → TypeError

BigInt 构造函数

与其他基本类型一样,可以使用构造函数创建BigInt。如果可能,传递给BigInt的参数会自动转换为BigInt:

BigInt("10");    // → 10n
BigInt(10);      // → 10n
BigInt(true);    // → 1n

无法转换的数据类型和值会抛出异常:

BigInt(10.2);     // → RangeError
BigInt(null);     // → TypeError
BigInt("abc");    // → SyntaxError

你可以直接对通过BigInt 构造函数创建的变量执行算术运算:

BigInt(10) * 10n;    // → 100n

当用作严格相等运算符的操作数时,使用构造函数创建的BigInt操作数与常规操作数类似:

BigInt(true) === 1n;    // → true

库函数

JavaScript提供了两个库函数来把BigInt值表示为有符号或无符号整数:

  • BigInt.asUintN(width, BigInt): 包装一个介于 0 和 2width-1之间的 BigInt

  • BigInt.asIntN(width, BigInt): 包装一个介于 -2width-1 和2width-1-1之间的BigInt

这些函数在执行64位算术操作时特别有用。这样你就可以保持在预定的范围内。

浏览器支持及转换

在撰写本文时,Chrome +67和Opera +54完全支持BigInt 数据类型。不幸的是,Edge和Safari还没有实现它。火狐默认不支持BigInt ,但可以通过在about:config中将javascript.options.bigint设置为true来启用。支持的浏览器的最新列表可以在Can I use…上找到。

不幸的是,转换BigInt 是一个极其复杂的过程,这会导致严重的运行时性能损失。也不可能直接填充BigInt ,因为该提议改变了几个现有操作符的行为。目前,更好的选择是使用JSBI库,它是BigInt 建议的纯JavaScript 实现。

这个库提供了一个与内置BigInt 行为完全相同的API。下面是使用JSBI的方法:

import JSBI from './jsbi.mjs';const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER);
const b2 = JSBI.BigInt('10');const result = JSBI.add(b1, b2);console.log(String(result));    // → '9007199254741001'

使用JSBI的一个优点是,一旦浏览器支持得到改进,你就不需要重写代码了。相反,您可以使用babel plugin将你的JSBI代码自动编译成本地的BigInt 代码。此外,JSBI的性能与内置的BigInt 实现相当。你可以期待更广泛的浏览器支持BigInt 。

结论

BigInt是一种新的数据类型,用于当整数值大于Number 数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高精度时间戳,使用大整数 ID 等等,而不需要使用库。

重要的是要记住,不能使用NumberBigInt操作数的组合来执行算术运算。你需要通过显式转换操作数来确定操作应该在哪个域中执行。此外,出于兼容性的原因,不允许在BigInt上使用一元加号(+)操作符。

本文已经获得李中凯老师授权转发,其他人若有兴趣转载,请直接联系作者授权。

作者简介:
李中凯老师,8年前端开发,前端负责人,擅长JavaScript/Vue。
公众号:1024译站
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
主要分享:Vue.js, JavaScript,CSS

一文搞懂 JavaScript 新数据类型 BigInt相关推荐

  1. 一文搞懂Java8新特性

    文章目录 导语 函数式接口 Lambda表达式 Stream API Optional类 新的日期时间API 方法引用 接口中定义默认方法 导语 Java8发布时被誉为Java里程碑式的一次更新,是J ...

  2. 5.一文搞懂MySQL的数据类型

  3. 一文搞懂Elasticsearch索引的mapping与setting

    目录 Elasticsearch索引结构 Mapping Setting Elasticsearch索引结构 一个Elasticsearch索引的主要结构如下: {"test_index&q ...

  4. 带你一文读懂Javascript中ES6的Symbol

    带你一文读懂Javascript中ES6的Symbol 前言 基础类型 Symbol Symbol.for 与 Symbol.keyFor Symbol.iterator Symbol.search ...

  5. ES6学习——一文搞懂ES6

    ES6学习--一文搞懂ES6 es6介绍 ES全称EcmaScript,是脚本语言的规范,而平时经常编写的EcmaScript的一种实现,所以ES新特性其实就是指JavaScript的新特性. 为什么 ...

  6. 一文搞懂指针,指针的指针,悬浮指针,野指针

    一文搞懂指针,指针的指针,悬浮指针,野指针 学习C语言过程中,指针的概念往往是重难点,伴随着时隐时显的*,令人头晕.实际上指针并非如此复杂,掌握最基础概念,有利于我们更深刻的理解指针. [百科概念] ...

  7. 都2021年了,再不学ES6你就out了 —— 一文搞懂ES6

    JS干货分享 -- 一文搞懂ES6 导语:ES6是什么?用来做什么? 1. let 与 const 2. 解构赋值 3. 模板字符串 4. ES6 函数(升级后更爽) 5. Class类 6. Map ...

  8. 一文搞懂什么是 PostCSS

    一文搞懂什么是 PostCSS 在 Web 应用开发中,CSS 代码的编写是重要的一部分.CSS 规范从最初的 CSS1 到现在的 CSS3,再到 CSS 规范的下一步版本,规范本身一直在不断的发展演 ...

  9. 一文搞懂 Python 的 import 机制

    一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...

最新文章

  1. SQLServer中Case的用法
  2. 干掉MessageBox,自定义弹出框JMessbox (WindowsPhone)
  3. python爬虫---从零开始(一)初识爬虫
  4. mac apache 和 nginx ssl 配置自签名的SSL
  5. 洛谷P1474 [USACO 2.3]货币系统 Money Systems [2017年4月计划 动态规划04]
  6. I love exam HDU - 6968
  7. 操作系统之进程管理:6、调度算法(先来先服务FCFS、最短作业优先SJF、最高响应比优先HRRN、时间片轮转法、优先级调度、多级反馈队列)
  8. 2020-08-07
  9. 小区移动基站能否杀人于无形?
  10. 5个让前端代码变得简洁的最佳实践
  11. python 字典列表指定key排序
  12. 我认为测试应该掌握的SQL语句
  13. 数字电平(一):TTL和CMOS
  14. ai/ml_您应该在本周(7月11日)阅读有趣的AI / ML文章
  15. php 在线api文档生成,一键生成API文档
  16. [算法]详解关键路径算法
  17. mysql一个汉字是几个字节_mysql里一个中文汉字占多少字节数?
  18. 获取局域网远程主机ipv6地址
  19. 2022年9月青少年软件编程(图形化)等级考试试卷--三级--跳高比赛
  20. rust多行字符串字面量

热门文章

  1. Nutrispoter: Eat like a sporter
  2. win10 连接打印机报0x00000709
  3. docker Centos 7 安装 xfce4 桌面 + x11vnc + novnc
  4. (翻译)声誉模式(Reputation)
  5. 2022年全国职业院校技能大赛:网络系统管理项目-模块A-真题 卷I 全套视频讲解及无线地勘
  6. MY-HuaWei-1
  7. 手机电容TP和电阻TP原理简介
  8. 执子之手,将子拖走。子若不走,拍晕了继续拖走~!
  9. 购买恩智浦的NFC标签NFC支付系统由中国RFID
  10. 【UC浏览器】魅族M8专版7.0 beta1发布啦