各位观众老爷,晚上好!我是你们的老朋友,今天咱们聊聊 JavaScript 里那些让人又爱又恨的数字:BigInt
和 Number
。
别看它们都叫数字,但关系可复杂着呢!就像家里的大哥(Number
)和小弟(BigInt
),大哥啥都能干,但力气有限;小弟力大无穷,但规矩多。如果让这俩家伙一块儿干活,那可得小心了,一不留神就得出岔子。
今天我就来给大家扒一扒 BigInt
和 Number
混合运算的那些坑,以及如何优雅地避开它们。
第一幕:数字世界的二元对立
首先,咱们得搞清楚 Number
和 BigInt
到底有啥区别。
特性 | Number |
BigInt |
---|---|---|
类型 | 浮点数 (IEEE 754) | 整数 |
精度 | 只能精确表示 -(2^53 – 1) 到 2^53 – 1 的整数 | 可以精确表示任意大小的整数 |
字面量表示 | 直接写,比如 123 , 3.14 |
数字后面加 n ,比如 123n |
用途 | 常规计算,小数运算 | 需要精确表示大整数的场景,比如 ID,加密,金融 |
简单来说,Number
是“万金油”,啥都能算,但有精度限制;BigInt
专门用来算大整数,精度没问题,但不能算小数。
第二幕:水火不容?混合运算的禁忌
重点来了,Number
和 BigInt
不能直接混合运算!就像油和水,硬要混在一起,只会得到一团糟。
let num = 10;
let bigInt = 100n;
// 错误!TypeError: Cannot mix BigInt and other types, use explicit conversions
// console.log(num + bigInt);
// 错误!TypeError: Cannot mix BigInt and other types, use explicit conversions
// console.log(num * bigInt);
JavaScript 引擎会毫不留情地抛出一个 TypeError
,告诉你:“别瞎搞,这俩不能直接算!”
为啥会这样呢?因为 Number
是浮点数,而 BigInt
是整数,它们在内存中的存储方式完全不同。如果直接混合运算,引擎不知道该怎么处理,所以干脆就报错了。
第三幕:打破次元壁:类型转换大法
既然不能直接算,那就只能想办法把它们变成同一种类型。这时候就要用到类型转换了。
-
Number
转BigInt
: 使用BigInt()
函数let num = 10; let bigInt = 100n; let numToBigInt = BigInt(num); // 将 Number 转换为 BigInt console.log(numToBigInt + bigInt); // 输出 110n
BigInt()
函数可以把Number
、字符串等类型转换为BigInt
。但是要注意,如果Number
是小数,转换后会直接舍去小数部分。let floatNum = 3.14; let floatToBigInt = BigInt(floatNum); // 将 3.14 转换为 BigInt console.log(floatToBigInt); // 输出 3n,小数部分被舍弃了
-
BigInt
转Number
: 使用Number()
函数let bigInt = 100n; let num = 10; let bigIntToNum = Number(bigInt); // 将 BigInt 转换为 Number console.log(bigIntToNum + num); // 输出 110
Number()
函数可以把BigInt
转换为Number
。但是要注意,如果BigInt
的值超过了Number
的精度范围,转换后可能会丢失精度。let hugeBigInt = 9007199254740992n; // 超过 Number 最大安全整数的值 let hugeToNum = Number(hugeBigInt); console.log(hugeToNum); // 输出 9007199254740992,看似没问题,但实际上已经丢失精度 console.log(hugeToNum === hugeBigInt); // 输出 false,验证了精度丢失
重要警告: 将
BigInt
转换为Number
时,一定要确保BigInt
的值在Number
的安全整数范围内,否则可能会导致精度丢失,出现意想不到的错误。
第四幕:运算符的爱恨情仇
即使转换了类型,有些运算符的行为也可能和你想象的不一样。
-
比较运算符 (
==
,===
,<
,>
,<=
,>=
):==
会进行隐式类型转换,可能会导致一些奇怪的结果。console.log(10 == 10n); // 输出 true,因为 10n 会被转换为 Number 类型进行比较 console.log(10 === 10n); // 输出 false,因为类型不同
所以,建议使用
===
进行严格比较,避免隐式类型转换带来的麻烦。对于大小比较,
BigInt
和Number
可以直接比较,引擎会自动进行类型转换。console.log(10n > 5); // 输出 true console.log(5 > 10n); // 输出 false
-
*算术运算符 (
+
,-
, `,
/,
%,
`):只有
+
运算符在字符串连接时会表现出特殊行为,其他算术运算符都必须保证操作数类型一致。// 错误!TypeError: Cannot mix BigInt and other types, use explicit conversions // console.log(10n + 5.5); console.log(10n + BigInt(5.5)); // 这样也不行,BigInt(5.5) 会直接舍去小数部分
如果要进行混合运算,必须先将
Number
转换为BigInt
,并且要注意精度问题。 -
位运算符 (
|
,&
,^
,~
,<<
,>>
,>>>
):位运算符只能用于整数,所以
BigInt
可以使用位运算符,但Number
不行(除非先转换为整数)。而且,位运算符会把Number
当作 32 位整数处理,可能会导致一些意想不到的结果。console.log(10n << 2n); // 输出 40n,左移 2 位 // 错误!TypeError: BigInts have no unsigned right shift //console.log(10n >>> 2n); // BigInt 不支持无符号右移
重点:
BigInt
不支持无符号右移运算符 (>>>
)。
第五幕:实战演练:优雅地处理混合运算
理论讲了一堆,现在咱们来点实际的,看看在实际开发中如何优雅地处理 BigInt
和 Number
的混合运算。
场景一:处理 ID
在很多系统中,ID 都是用大整数表示的。如果 ID 来自后端,通常是字符串或 BigInt
类型;如果 ID 来自前端,可能是 Number
类型。
// 假设从后端获取的 ID 是 BigInt 类型
let userId = 12345678901234567890n;
// 假设前端需要将 ID 传递给某个函数,该函数接受 Number 类型的 ID
function processUserId(id) {
// ...
}
// 错误!TypeError: Cannot mix BigInt and other types, use explicit conversions
// processUserId(userId);
// 正确的做法:将 BigInt 转换为 Number
let userIdNum = Number(userId);
if (userId > Number.MAX_SAFE_INTEGER) {
console.warn("User ID is too large to be represented accurately as a Number.");
}
processUserId(userIdNum);
最佳实践:
- 尽量避免在前端使用
Number
类型来存储 ID,如果必须使用,一定要进行安全检查,确保 ID 的值在Number
的安全整数范围内。 - 在传递 ID 时,最好使用字符串类型,避免类型转换带来的精度问题。
场景二:金融计算
在金融计算中,精度至关重要。如果使用 Number
类型进行计算,可能会出现精度丢失,导致严重的错误。
let price = 10.05;
let quantity = 100;
let total = price * quantity;
console.log(total); // 输出 1004.9999999999999,精度丢失了!
// 使用 BigInt 进行计算
let priceBigInt = BigInt(Math.round(price * 100)); // 将价格乘以 100,转换为整数
let quantityBigInt = BigInt(quantity);
let totalBigInt = priceBigInt * quantityBigInt;
console.log(totalBigInt); // 输出 100500n
// 将结果转换为字符串,保留两位小数
let totalStr = (Number(totalBigInt) / 100).toFixed(2);
console.log(totalStr); // 输出 "1005.00"
最佳实践:
- 在进行金融计算时,尽量使用
BigInt
类型,避免精度丢失。 - 将所有涉及金额的数字都乘以一个合适的倍数(比如 100),转换为整数进行计算。
- 在显示结果时,再将结果除以相应的倍数,并使用
toFixed()
方法保留指定位数的小数。
第六幕:总结与展望
今天咱们一起探讨了 BigInt
和 Number
混合运算的各种坑,以及如何优雅地避开它们。
记住,BigInt
和 Number
虽然都是数字,但类型不同,不能直接混合运算。如果需要进行混合运算,必须进行类型转换,并且要注意精度问题。
BigInt
的出现,为 JavaScript 带来了更强大的整数处理能力。随着 JavaScript 的不断发展,BigInt
的应用场景也会越来越广泛。
希望今天的分享对大家有所帮助。下次再见!