Back

如何在 JavaScript 中解析数字

如何在 JavaScript 中解析数字

当你从表单字段、URL 参数或 JSON API 响应中读取值时,它几乎总是以字符串形式到达。在对其进行任何数学运算之前,你需要将其转换为数字。JavaScript 为你提供了几种实现方法,它们的行为差异足以导致选择错误的方法会引发细微的 bug。本文介绍了主要方法、何时使用每种方法,以及最常困扰开发者的边界情况。

核心要点

  • parseInt()parseFloat() 执行部分解析,从左到右读取并在第一个无效字符处停止——适用于像 "24px" 这样的字符串。
  • Number() 和一元 + 运算符要求整个字符串有效,否则返回 NaN——非常适合从 API 数据进行严格转换。
  • 始终向 parseInt() 传递基数参数,并始终使用 Number.isNaN() 而不是全局 isNaN() 来验证结果。
  • 对于超出 Number.MAX_SAFE_INTEGER 的整数使用 BigInt(),并在解析前规范化区域格式化的字符串。

快速对比:JavaScript 字符串转数字的转换方法

方法输入 "42px"输入 "3.14"输入 ""输入 "abc"
parseInt("42px", 10)423NaNNaN
parseFloat("42px")423.14NaNNaN
Number("42px")NaN3.140NaN
+"42px"NaN3.140NaN

parseInt():整数的部分解析

parseInt(string, radix) 从左到右读取字符,并在遇到无法解析的内容时立即停止。这使得它对于像 "24px""10rem" 这样数字在前的字符串很有用。

parseInt("24px", 10)   // 24
parseInt("3.99", 10)   // 3  (小数点停止解析)
parseInt("abc", 10)    // NaN (第一个字符无效)

始终传递基数参数。 如果不传递,parseInt 会将以 0x 开头的字符串视为十六进制,否则以十进制解析。虽然现代引擎默认为十进制,但省略基数是一个众所周知的 bug 来源,并且会使你的意图不明确。

parseInt("0xFF", 16)   // 255 — 显式十六进制解析
parseInt("011", 10)    // 11  — 基数防止歧义

一个不明显的行为:parseInt"6.022e23" 的小数点处停止,因此 parseInt("6.022e23", 10) 返回 6,而不是 602200000000000000000000。不要使用 parseInt 来截断大型浮点数。请改用 Math.trunc()


parseFloat():小数的部分解析

parseFloat() 的工作方式与 parseInt 相同,但保留小数部分。它没有基数参数,始终以十进制解析。

parseFloat("3.14rem")  // 3.14
parseFloat("1e3")      // 1000 — 理解科学计数法
parseFloat("px10")     // NaN  — 第一个字符无效

当输入可能包含小数值并且你想保留它时,使用 parseFloat


Number() 和一元 +:严格转换

Number() 和一元 + 运算符都要求整个字符串是有效数字。如果其中任何部分无效,你会得到 NaN。在处理 API 响应或经过验证的表单数据时,这种严格性是一个优势,因为部分解析会是一个静默错误。

Number("42")     // 42
Number("3.14")   // 3.14
Number("42px")   // NaN — 比 parseInt 更严格
Number("")       // 0   — 注意这一点
+"42"            // 42  — 行为相同,语法更短

空字符串边界情况在表单处理中很重要。Number("") 返回 0 而不是 NaN,这可能会掩盖缺失值。在转换前始终检查空输入。


数字分隔符在运行时不起作用

JavaScript 源代码允许 1_000_000 作为数字字面量,但该语法不适用于字符串解析。

parseInt("1_000", 10)   // 1 — 解析在下划线处停止
Number("1_000")         // NaN

如果你的 API 或用户输入使用下划线作为千位分隔符,请先使用 str.replace(/_/g, "") 将其删除。


BigInt():解析大整数

JavaScript 的 Number 类型在超出 Number.MAX_SAFE_INTEGER(2⁵³ − 1)后会失去精度。对于大于该值的数字——数据库 ID、加密值、金融整数——使用 BigInt()

BigInt("9007199254740993")   // 9007199254740993n — 精确
Number("9007199254740993")   // 9007199254740992  — 差一

直接将字符串传递给 BigInt()。不要先通过 parseInt 传递,因为此时精度已经丢失。请注意,BigInt 值不能在算术运算中与常规 Number 值混合使用,除非进行显式转换。


区域感知解析

JavaScript 没有内置函数来解析区域格式化的数字,如 "1.234,56"(在德语或西班牙语区域设置中常见)。你需要在解析前手动规范化字符串:

const raw = "1.234,56"
const normalized = raw.replace(/\./g, "").replace(",", ".")
parseFloat(normalized) // 1234.56

你应该使用哪种方法?

  • 带单位的用户输入或 CSS 值parseInt(str, 10)parseFloat()
  • API 响应或严格验证的数据Number() 或一元 +
  • 超出 2⁵³ − 1 的整数BigInt()
  • 始终在使用结果前验证其不是 NaN:
const qty = parseInt(input, 10)
if (Number.isNaN(qty)) {
  // 处理无效输入
}

使用 Number.isNaN(),而不是全局 isNaN()。全局版本会先强制转换其参数,这意味着 isNaN("") 返回 false,即使 "" 不是数字。


结论

正确的 JavaScript 数字解析方法取决于你对输入的了解。当部分解析是可接受和预期的时,使用 parseIntparseFloat。当整个字符串必须是有效数字时,使用 Number() 或一元 +。当大整数的精度很重要时,使用 BigInt()。在所有情况下,在使用结果之前都要检查 NaN

常见问题

parseInt 从左到右读取字符串,并在遇到无法解释为整数一部分的第一个字符时停止。Number 要求整个字符串是有效的数字值。例如,parseInt('42px') 返回 42,而 Number('42px') 返回 NaN。使用 parseInt 进行部分解析,使用 Number 进行严格转换。

ECMAScript 规范定义空字符串在传递给 Number 或一元加运算符时转换为 0。这可能会掩盖缺失的表单值。在将输入字符串转换为数字之前,始终检查其是否为空,以避免将空白字段视为零。

当整数超过 Number.MAX_SAFE_INTEGER(即 2 的 53 次方减 1)时使用 BigInt。超过该阈值,Number 会失去精度并可能静默舍入值。直接将数字字符串传递给 BigInt,而不是先通过 parseInt 或 Number 转换,因为在该中间步骤期间精度会丢失。

全局 isNaN 在检查之前会强制转换其参数,这会产生误导性结果。例如,空字符串的 isNaN 返回 false,因为空字符串强制转换为 0。Number.isNaN 跳过强制转换,仅在值确实是 NaN 时返回 true,使其成为验证的可靠选择。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay