在服務(wù)端使用 64 位長整型(Int64)數(shù)字,而前端通過 JavaScript 的number
類型接收時,若數(shù)值超過2^53 - 1
(即 9007199254740991),會出現(xiàn)數(shù)值不相等的問題。這一現(xiàn)象的核心原因是 JavaScript 中number
類型的精度限制,而雪花算法生成的 ID(通常為 64 位)恰好屬于這類場景,因此需要特別處理。
一、問題根源:JavaScript 中number的精度限制
JavaScript 的number
類型基于IEEE 754 雙精度浮點(diǎn)數(shù)標(biāo)準(zhǔn)實(shí)現(xiàn),其底層存儲結(jié)構(gòu)決定了它的精度范圍:
雙精度浮點(diǎn)數(shù)的 “尾數(shù)(mantissa)” 部分占 52 位,加上隱藏的 1 位 “整數(shù)位”,總共可表示53 位有效精度。
這意味著:對于整數(shù),number
類型能精確表示的范圍是[-2^53 + 1, 2^53 - 1]
,這個范圍外的整數(shù)無法被精確存儲,會出現(xiàn) “精度丟失”。
當(dāng)服務(wù)端返回的 Int64 數(shù)值超過2^53 - 1
時,JavaScript 的number
會因無法精確表示該值,導(dǎo)致存儲的結(jié)果與實(shí)際值不一致(例如末尾幾位被截斷或四舍五入)。
二、典型場景:雪花算法 ID 的問題
雪花算法(Snowflake)生成的 ID 是 64 位整數(shù),結(jié)構(gòu)通常為:
1 位符號位(固定為 0,保證正數(shù));
41 位時間戳;
10 位機(jī)器 ID;
12 位序列號。
顯然,64 位整數(shù)的最大值為2^63 - 1
(約 9e18),遠(yuǎn)超過2^53 - 1
,因此用number
接收時必然會丟失精度,導(dǎo)致前端存儲的 ID 與服務(wù)端實(shí)際值不匹配(例如服務(wù)端 ID 為1234567890123456789
,前端接收后可能變成1234567890123456700
)。
三、解決方法:如何避免精度丟失?
解決的核心思路是繞開 JavaScriptnumber
類型的精度限制,常見方案如下:
1. 以字符串形式傳遞大整數(shù)
最直接的方式是服務(wù)端在返回數(shù)據(jù)時,將 Int64 類型的字段轉(zhuǎn)換為字符串,前端接收后以字符串形式存儲和使用。
- 服務(wù)端處理:例如在 Java 中,將
Long
類型的雪花 ID 轉(zhuǎn)換為String
后再序列化到 JSON 中; - 前端接收:直接使用字符串類型的 ID(無需轉(zhuǎn)換為
number
),避免精度丟失。
2. 前端使用BigInt類型處理大整數(shù)
BigInt
是 JavaScript 中專門用于表示任意精度整數(shù)的類型,可精確存儲超過2^53 - 1
的數(shù)值。
- 服務(wù)端:保持 Int64 類型的數(shù)值(例如 JSON 中直接存儲數(shù)字);
- 前端處理:
示例:
服務(wù)端返回 JSON:{ "id": 1234567890123456789 }
前端處理:
javascript
const numId = response.id;
const bigIntId = BigInt(response.id);
3. 框架 / 序列化工具的配置優(yōu)化
在前后端數(shù)據(jù)交互中(尤其是 JSON 序列化 / 反序列化),可通過工具配置自動處理大整數(shù):
例如,使用JSON.parse
時,通過reviver
函數(shù)將大整數(shù)轉(zhuǎn)換為BigInt
:
javascript
const data = JSON.parse(response, (key, value) => {
if (key === 'id' && typeof value === 'number' && value > 2**53 - 1) {
return BigInt(value);
}
return value;
});
部分框架(如 Vue、React)或 HTTP 客戶端(如 Axios)可配置攔截器,自動將大整數(shù)字段轉(zhuǎn)換為BigInt
或字符串。
四、注意事項(xiàng)
- 兼容性:
BigInt
在 IE 瀏覽器中不支持,但現(xiàn)代瀏覽器(Chrome、Firefox、Edge 等)均已支持; - 字符串傳遞的優(yōu)勢:若前端僅需展示或傳遞 ID(無需數(shù)值運(yùn)算),字符串形式更簡單,避免
BigInt
的類型轉(zhuǎn)換成本; - 數(shù)據(jù)庫交互:若前端需將 ID 回傳到服務(wù)端或存儲到數(shù)據(jù)庫,保持字符串或
BigInt
類型即可(服務(wù)端可再轉(zhuǎn)換為 Int64 處理)。
總結(jié)
當(dāng)服務(wù)端的 Int64 數(shù)值超過2^53 - 1
時,JavaScript 的number
類型無法精確表示,導(dǎo)致數(shù)值不匹配。解決核心是避開number
的精度限制,推薦通過 “字符串傳遞” 或 “前端BigInt
處理” 兩種方式,尤其在雪花算法 ID 等 64 位整數(shù)場景中必須使用。
?轉(zhuǎn)自https://juejin.cn/post/7524645466745815059
該文章在 2025/7/10 9:46:53 編輯過