你知道 +0 与 -0 的区别吗?

JavaScript 的数值 Number 用 64 位的浮点数表示,首位是符号位,然后是 52 位的整数位和 11 位的小数位。如果符号位为 1,其他各位均为 0,那么这个数值会被表示成 -0

所以 JavaScript 的 “0” 值有两个,+0-0

const num = -0;
console.log(num); // -0

我们可以用二进制位构造出 -0 来:

// 首先创建一个8位的ArrayBuffer
const buffer = new ArrayBuffer(8);
// 创建DataView对象操作buffer
const dataView = new DataView(buffer);

// 将第1个字节设置为0x80,即最高位为1
dataView.setUint8(0, 0x80);

// 将buffer内容当做Float64类型返回
console.log(dataView.getFloat64(0)); // -0

在一般的运算中,我们也有可能产生 -0 值,比如运算时将 -Infinity 作为分母,负数除法超过最小可表示数等:

console.log(1 / -Infinity); // -0
console.log(-1 / Infinity); // -0
console.log(-Number.MIN_VALUE / 2); // -0
console.log(-1e-1000); // -0

一般情况下 -0 视同于 0,运算并没有什么问题:

console.log(-0 === 0); // true

const num = -0;
if (!num) {
  // !num 为 true
  // do sth.
}

除非在分母中有 -0 存在,恰好又要判断符号的时候,可能会出问题:

const num = -0;

console.log(1 / num > 0); // false
console.log(Math.sign(1 / num)); // -1

所以如果你知道分母中可能出现 0 的时候,就要小心 -0

-0 也有好处,我们可以利用 -0 来表示状态,比如我们在寻路的时候,把地图的障碍物用 1 表示,空地用 0 表示,那么我们就可以把未搜索过的用 -1 和 -0 表示,这样也挺方便的。

另外比如我们要存 mousemove 的坐标位置,那么当鼠标移动出检测范围的时候,可以把 mousePosition 设为 -0 来表示当前鼠标不在范围内。

如果要使用 -0,我们就得区分它与 0。

num === -0 是不行的,因为 0 === -0 也会返回 true

Math.sign 也是不行的,Math.sign 一共会返回 4 种值:

console.log(Math.sign(0)); // 0
console.log(Math.sign(-0)); // -0
console.log(Math.sign(0.01)); // 1
console.log(Math.sign(-0.01)); // -1

实际上在 ES2015 中提供了 Object.is 方法,可以判断 -0.

👉🏻 【冷知识】Object.is(value1, value2) 比较两个值是否相同。它与 === 的区别是,Object.is(-0, 0) 返回 falseObject.is(NaN, NaN) 返回 true

如果在早期的 JavaScript 版本中,我们也可以利用 1/-0-Infinity 的特点来判断:

function isNegativeZero(num) {
  return num === 0 && 1 / num < 0;
}

或者我们可以写一个 Object.is 的 polyfill:

if (!Object.is) {
  Object.is = function(x, y) {
    // SameValue algorithm
    if (x === y) {
      // Steps 1-5, 7-10
      // Steps 6.b-6.e: +0 != -0
      return x !== 0 || 1 / x === 1 / y;
    } else {
      // Step 6.a: NaN == NaN
      return x !== x && y !== y;
    }
  };
}

补充,所有位运算都会把 -0 转为 0。因为位运算首先会转成 int32,而 int32 是没有 -0 的。另外 bigint 里也是没有 -0 的,所以 Object.is(0n, -0n) 返回 true

再补充一个序列化有关的坑:JSON.parse("-0") 返回 -0 然而 JSON.stringify(-0) 返回 "0",所以是不对称的。