JavaScript 的 Math 对象是该语言中一个默默工作的工具箱。你不需要实例化它,也不需要导入它。它就存在于每一个运行时环境中,随时准备处理从舍入像素值到生成随机骰子点数的一切任务。因为 Math 是一个静态对象,它的每个方法和属性都直接挂载在命名空间下。你写的是 Math.PI,而不是 new Math().PI。这种设计选择保持了简洁,但也意味着该对象带有一些需要注意的“锋利边缘”,在将其用于生产环境的逻辑之前,值得深入了解。

没有构造函数的静态对象

大多数 JavaScript 内置对象都要求你创建实例。你会调用 new Date()new Array()。但 Math 拒绝这样做。它被设计为一个包含相关函数和常量的命名空间,这些函数和常量作用于原始的 Number 类型。调用 Math.sqrt(16) 你会得到 4。如果尝试使用 new 关键字,引擎会抛出 TypeError。这种模式保持了内存的整洁,并表明 Math 是纯函数式的。它接收输入,返回输出,且不改变任何状态。

有一点严格的限制需要记住:Math 处理的是标准的 IEEE 754 数字。它不支持 BigInt。将 BigInt 传递给 Math.round()Math.abs() 会导致 TypeError。如果你的应用程序处理的是 64 位整数或更大的数值,你需要将其向下转换,或者改用原生的 BigInt 运算符来处理算术运算。

你真正会用到的常量

Math 对象暴露了几个数值常量,可以让你免于硬编码“魔术数字”(magic numbers)。

  • Math.PI 是大家熟悉的 3.14159...,对于任何涉及圆、三角函数或角度转弧度的操作都至关重要。
  • Math.E 是欧拉数,约为 2.718。它出现在指数增长曲线、复利计算和对数刻度中。
  • Math.SQRT2 是 2 的平方根,约为 1.414。当你需要计算正方形的对角线或在简单几何中归一化向量时非常有用。
  • Math.LN2 是 2 的自然对数,约为 0.693。你会在处理二叉树、信息熵或涉及以 2 为底的对数数学的时间复杂度分析算法中看到它。

这些值都是全双精度浮点数。因为它们是属性而非方法,所以访问时不需要加括号。

舍入、符号与绝对值

最常见的 Math 任务涉及对数字进行清理。JavaScript 提供了四种舍入方法,它们的行为并不完全相同,尤其是在处理负数时。

Math.abs(x) 返回一个数的绝对值(大小)。传入 -42,你会得到 42。当你关心的是距离而非方向时,它非常有用。例如比较时间戳、计算 UI 中的拖拽增量,或者检查某个猜测值是否在误差范围内。

Math.round(x) 是学校里教的那种四舍五入。它取最接近的整数,对于 0.5 的情况则向上舍入到下一个整数。因此 4.4 变成 4,而 4.5 变成 5。

Math.ceil(x) 总是向上舍入到下一个整数。4.1 变成 5,-4.9 变成 -4。最后一种情况常会让人们感到意外。因为在数轴上 -4 大于 -4.9,所以对于负数,ceil 是向零的方向移动。

Math.floor(x) 则相反。它总是向下舍入。4.9 变成 4,-4.1 变成 -5。如果你正在对搜索结果进行分页或分配固定大小的内存块,floor 通常是你想要的结果。

Math.trunc(x) 完全去除小数部分,只留下整数部分。实际上,无论正负,它都会向零的方向移动。4.9 变成 4,-4.9 变成 -4。如果你正在从通过截断浮点数来转换为整数的语言移植逻辑,那么它会是你的好帮手。

最后,Math.sign(x) 告诉你一个值位于零的哪一侧。对于正数返回 1,负数返回 -1,正零返回 0,负零返回 -0,对于非数字则返回 NaN。这是一种简洁的方式,让你无需编写 if-else 链即可根据方向进行分支处理。

生成随机数

Math.random() 生成一个从 0(包含)到 1(不包含)之间的伪随机浮点数。它不是加密安全的,因此请不要将其用于密码生成、令牌(token)创建或任何涉及真实金钱的操作。