根据2025年Stack Overflow技术调查的结果,JavaScript是目前全球使用最广泛的编程语言之一。我们用它来开发前端应用程序、后端服务、支付系统、分析平台以及区块链应用等等。
然而,JavaScript存在一个许多开发者直到遇到实际生产问题时才真正了解的局限性。这个局限性被称为安全整数限制。
在本文中,你将了解到以下内容:
-
什么是安全整数限制
-
为什么JavaScript会有这种限制
-
精度错误是如何产生的
-
什么是
BigInt -
现代系统是如何使用
BigInt的 -
如何在生产环境中安全地处理大整数
目录
先决条件
要顺利阅读本文,你需要具备以下条件:
-
对JavaScript有基本的了解
-
拥有一款代码编辑器或浏览器控制台
-
熟悉变量和函数的概念
JavaScript中的安全整数限制是什么?
JavaScript使用Number类型来表示数值。
例如:
const age = 25;
const price = 99.99;
const count = 1000;
在内部实现层面,JavaScript使用IEEE 754双精度浮点数格式来存储数值。你不必记住整个规范细节,但应该了解其中一个重要的后果:JavaScript只能在一定程度上准确表示整数。
关键点在于:
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
这就是JavaScript使用Number类型能够安全表示的最大整数。
而最小的安全整数是:
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
为什么被称为“安全”整数?
“安全”这个词意味着JavaScript仍然能够准确表示这个整数,而不会丢失精度。但一旦超过了这个安全范围,JavaScript就会开始出现近似计算错误。
我们来看一个例子。
const max = Number.MAX_SAFE_INTEGER;
console.log(max + 1) // 9007199254740992
console.log(max + 2) // 9007199254740992
这种结果是错误的,因为1加上2应该得到不同的结果。但你知道为什么会发生这种情况吗?因为JavaScript已经无法准确区分这些相邻的巨大整数了。
如果你是新手,该如何理解这个问题呢?
想象你有一台相机。当你近距离放大时,所有的细节都能清晰地显示出来;但当你把焦距拉远后,细小的部分就会开始消失。
JavaScript中的数字也是类似的。较小的整数可以被精确地表示:
console.log(10)
console.log(100)
console.log(1000)
但极大的整数则会因为精度不足而失去细节。在这种情况下,多个数值在内部会被视为相同的值。因此,对极大整数的计算就会变得不可靠。
如何判断一个数字是否属于安全整数?
JavaScript提供了一个内置方法Number.isSafeInteger(),可以用来检测这一点。
示例:
console.log(Number.isSafeInteger(100)) // true
另一个例子:
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)) // true
但下面的代码会返回false:
console.log(
Number.isSafeInteger(Number.MAXSAFE_INTEGER + 1)
) // false
这个方法在验证来自API、数据库或用户输入的极大整数时非常有用。
不安全的整数会不会引发问题?
不安全的整数确实可能会造成严重的程序错误。例如,在金融计算中,如果一个支付平台需要处理极大的交易记录,精度问题就可能导致余额或对账结果出现错误。
const amount = 9007199254740993;
console.log(amount) // 9007199254740992
这个数值会突然发生变化,这对于金融系统来说是非常危险的。
另一个例子是分析系统。大规模的分析平台通常会追踪数十亿甚至数万亿条事件记录,如果使用不安全的整数类型来存储这些数据,就可能会导致计数结果和报告出现错误。
此外,分布式系统经常会产生非常大的标识符。例如数据库ID、事件ID、交易ID以及区块链交易的哈希值等。如果这些标识符的精度丢失,系统就可能会引用错误的记录。
区块链系统也常常使用极大的整数。以以太坊为例,它使用wei作为数值单位,1个以太币相当于:
1,000,000,000,000,000,000 wei
这个数值超出了JavaScript所能安全处理的整数范围。如果不进行适当的处理,账户余额就会出现误差。
JavaScript中的BigInt简介
为了解决这些问题,JavaScript引入了BigInt类型。BigInt可以让JavaScript准确地表示那些超过安全整数范围的数值。你可以通过在数字后面加上n>来创建一个BigInt对象。
示例:
const largeNumber = 9007199254740993n
console.log(largeNumber) // 9007199254740993n
可以看到,这个数值的精度得到了保持。你也可以使用BigInt()构造函数来创建BigInt对象。
const value = BigInt("9007199254740993123123123")
console.log(value)
如何对BigInt进行运算
你可以使用算术运算符来操作BigInt。
下面是一个示例:
const a = 1000000000000000000n
const b = 2n
console.log(a + b) // 1000000000000000002n
console.log(a - b) // 999999999999999998n
console.log(a * b) // 2000000000000000000n
console.log(a / b) // 500000000000000000n
BigInt与Number的区别
一个重要的规则是,你不能直接将BigInt和Number类型进行混合操作。
这样会引发错误:
const result = 1n + 1 // TypeError
你必须明确地进行类型转换,例如这样:
const result = 1n + BigInt(1)
console.log(result)
或者也可以这样做:
const result = Number(1n) + 1
console.log(result)
明确的类型转换可以避免精度丢失的问题。
现代软件如何使用BigInt
许多现代应用程序都依赖于BigInt。我们来看一个实际例子:区块链应用在很多地方都需要进行精确的整数运算。
示例:
const wei = 1000000000000000000n
const balance = 5000000000000000000n
console.log(balance / wei) // 5n
以太坊生态系统中的许多库在内部也会使用BigInt来处理代币余额和Gas费用的计算。
何时应使用 BigInt
在以下情况下应使用BigInt:
-
整数的精度非常重要时
-
数值超出了安全范围时
-
当你正在开发区块链应用程序时
-
当你处理财务账目时
-
当你需要处理庞大的计数数据时
-
当你处理大型数据库中的ID时
何时不应使用BigInt
在以下情况下应避免使用BigInt:
-
当你需要进行小数运算时
-
当你正在开发简单的前端交互界面时
-
精度并不是关键因素时
-
性能比对大整数的支持更为重要时
由于BigInt的操作需要使用任意精度的算术运算,因此其执行速度会比普通的Number操作慢。
总结
JavaScript所规定的安全整数范围并非只是一个理论概念,它实际上每天都在影响着各种实际系统。随着应用程序规模不断扩大且分布越来越广泛,开发人员们在支付系统、区块链平台、分析流程、数据库以及事件驱动架构等领域中越来越多地需要处理庞大的整数数据。
了解这一安全整数限制有助于你避免那些往往难以被发现的潜在错误。BigInt使JavaScript能够安全、准确地处理这些大整数。但就像任何强大的工具一样,它也必须被有意识地使用才行。
请记住:在日常计算中,应使用普通的Number类型;只有当精度成为关键因素时,才应该使用BigInt。
最重要的结论就是:在JavaScript中,大的数值并不总是安全的数值。