=== 无法可靠识别 -0,因为 -0 === 0 返回 true;object.is 是唯一能稳定区分 +0 和 -0 的标准方法,按 samevalue 算法将二者视为不等。

为什么 === 无法可靠识别 -0
JavaScript 中 -0 === 0 返回 true,这在浮点运算后容易掩盖符号异常。比如 1 / -Infinity 或 -1 * 0 都会产出 -0,但用严格相等判断时完全无法区分。这不是 bug,而是 IEEE 754 规定的语义 —— 但业务中有时必须知道符号位是否被翻转(例如坐标系方向、信号极性、金融符号约定)。
Object.is 是唯一能稳定区分 +0 和 -0 的标准方法
Object.is 按 SameValue 算法实现,把 +0 和 -0 视为不等。这是它和 === 的关键差异,也是它被设计出来的核心用途之一。
实操建议:
- 永远不要用
value === -0判断 —— 因为-0 === -0虽然为true,但0 === -0也为true,无法反向推断原始值符号 - 统一用
Object.is(value, -0)检测是否为负零;用Object.is(value, 0)检测是否为正零(注意:它不匹配-0) - 若需同时排除两种零,写成
!Object.is(value, 0) && !Object.is(value, -0),比value !== 0更安全(后者对-0仍返回false)
浮点计算中哪些操作容易产出 -0
不是所有负数运算都导致 -0,只有涉及「零」且符号传播明确的场景才发生。常见来源包括:
-
-1 * 0、0 * -5、Math.pow(-1, 1) * 0 -
1 / -Infinity、-2 / Infinity -
parseFloat("-0")(注意:字符串解析出的是-0,不是字符串"-0") - TypedArray(如
Float64Array)从二进制位直接构造时,若 sign bit = 1 且 exponent & mantissa 全 0,则为-0
容易踩的坑:Number("-0") 和 parseInt("-0") 都返回 -0,但很多人误以为它们和字符串 "0" 一样中性 —— 实际上符号已固化,后续参与运算可能继续传播。
配合 Object.is 做防御性校验的典型场景
当你的函数接收外部输入(如 API 返回坐标、传感器读数、用户输入的科学计数法),且下游逻辑依赖零的符号时,必须主动拦截。
示例:一个归一化函数,要求输入不能是 -0(否则旋转矩阵会翻转)
function normalize(x) {
if (Object.is(x, -0)) {
throw new Error("Input must not be -0; use +0 instead");
}
return x === 0 ? 0 : x / Math.abs(x);
}
另一个常见需求:序列化前清洗数据,把 -0 统一转为 0(JSON.stringify 本身会把 -0 输出为 0,但中间处理可能出错):
function sanitizeZero(x) {
return Object.is(x, -0) ? 0 : x;
}
注意:Object.is 本身无性能问题,现代引擎已对其内联优化;但别在热循环里反复调用它做非必要判断 —— 先确认是否真有 -0 流入风险。
真正难的不是检测,而是意识到某些浮点路径下 -0 是静默存在的,而且 console.log、JSON.stringify、甚至 Number.prototype.toString() 都默认隐藏它 —— 你得主动用 Object.is 才能把它揪出来。










