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。
实际上在ES2015中提供了Object.is方法,可以判断-0.
?? 【冷知识】Object.is(value1, value2) 比较两个值是否相同。它与=== 的区别是,Object.is(-0, 0)返回false,Object.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的问题,你还有什么想法,欢迎在issue中讨论。