- Published on
typeof, NaN 与 -0 详解
typeof 运算符:不仅仅是类型检查
typeof 是一个一元运算符,用于返回一个表示其操作数类型的字符串。虽然它在大多数情况下表现得很直观,但在处理某些特殊值时,其行为需要特别注意。其决策流程可以被可视化:
typeof 的特殊情况
typeof nulltypeof null; // "object"这是一个从 JavaScript 诞生之初就存在的著名历史遗留问题。在逻辑上,
null代表一个原始的“空值”,但typeof却返回"object"。由于修改这个行为会破坏大量现有网站,因此它被永久保留了下来。在检查一个值是否为null时,应始终使用value === null。typeof function(){}typeof function() {}; // "function"虽然函数在 JavaScript 中本质上也是一种对象,但
typeof为其返回了专属的"function"字符串。ECMAScript 规范定义,任何实现了内部[[Call]]方法的对象,typeof都会返回"function"。这使得我们可以方便地用它来判断一个变量是否为可调用的函数。
NaN:一个不等于自己的“数字”
NaN (Not a Number) 是一个特殊的数值,它代表一个无法表示或未定义(但本应是数字)的计算结果。
定义与本质
NaN 通常在数学运算失败时出现,例如:
Math.sqrt(-1); // NaN
Number('hello'); // NaN
0 / 0; // NaN
NaN 最违反直觉的特性是:它虽然代表“不是一个数字”的失败状态,但它本身的类型却是 number。可以将其理解为“不是一个有效的数字 (Not a valid number)”。
typeof NaN; // "number"
独特的比较规则
NaN 是 JavaScript 中唯一一个不等于其自身的值。这一设定源于 IEEE 754 浮点数标准,其核心逻辑是:一个失败的运算结果(NaN)不应该等于另一个同样失败的运算结果,因为导致失败的原因可能是不同的(例如 0/0 和 Number('abc') 都得到 NaN,但其来源不同)。
NaN === NaN; // false
NaN !== NaN; // true
这意味着你永远不能通过 myVar === NaN 来判断一个变量是否为 NaN。
正确的 NaN 判断方法
| 方法 | Number.isNaN(value) (ES6, 推荐) | isNaN(value) (全局函数, 慎用) | Object.is(value, NaN) |
|---|---|---|---|
| 工作方式 | 仅当 value 的值就是 NaN 时才返回 true。它不会对参数进行类型转换。 | 在判断前,会尝试将 value 强制转换为 Number。 | 严格比较两个值是否都为 NaN,无类型转换。 |
'hello' | Number.isNaN('hello') // false | isNaN('hello') // true | Object.is('hello', NaN) // false |
NaN | Number.isNaN(NaN) // true | isNaN(NaN) // true | Object.is(NaN, NaN) // true |
始终使用 Number.isNaN()。它能精确地告诉你一个值是否就是 NaN 本身,而不会因强制类型转换产生像 isNaN('hello') 返回 true 这样的意外结果。
-0:带有方向的零
根据 IEEE 754 浮点数标准,JavaScript 同时存在 +0(或 0)和 -0。-0 的存在并非错误,而是一种有意义的设计,它主要用于表示数值从负数方向无限趋近于零的情况,这在一些科学计算和物理模拟中非常重要。
=== 与 Object.is 的差异
使用严格相等运算符 === 时,0 和 -0 被认为是相等的。因为它只比较数值大小,不考虑符号位。
-0 === 0; // true
Object.is() 方法则被设计为能够区分它们,因为它不进行类型转换,而是进行更严格的值本身(包括其符号位)的比较。这是区分 0 和 -0 最直接的现代方法。
Object.is(-0, 0); // false
如何确定一个值是否为 -0
区分 0 和 -0 的核心在于找到一个只对其中之一有特殊表现的操作。使用 Infinity 的技巧利用了浮点数除法的特性,是区分这两种零值的最可靠方法之一。
function isNegativeZero(value) {
// 只有当 value 是 -0 时,1 / value 才会得到 -Infinity
return value === 0 && (1 / value) === -Infinity;
}
isNegativeZero(-0); // true
isNegativeZero(0); // false