填补 window.isNaN 工具的缺陷

内容来自: You-dont-know-JS中类型与文法的第二章(值)

不是数字的数字

如果你不使用同为 number(或者可以被翻译为十进制或十六进制的普通 number 的值)的两个操作数进行任何算数操作,那么操作的结果将失败而产生一个不合法的 number,在这种情况下你将得到 NaN 值。

NaN 在字面上代表“不是一个 number(Not a Number)”,但是正如我们即将看到的,这种文字描述十分失败而且容易误导人。将 NaN 考虑为“不合法数字”,“失败的数字”,甚至是“坏掉的数字”都要比“不是一个数字”准确得多。

举例来说:

var a = 2 / "foo";      // NaN

typeof a === "number";  // true

换句话说:“‘不是一个数字’的类型是‘数字’”!为这使人糊涂的名字和语义欢呼吧。

NaN 是一种“哨兵值”(一个被赋予了特殊意义的普通的值),它代表 number 集合内的一种特殊的错误情况。这种错误情况实质上是:“我试着进行数学操作但是失败了,而这就是失败的 number 结果。”

那么,如果你有一个值存在某个变量中,而且你想要测试它是否是这个特殊的失败数字 NaN,你也许认为你可以直接将它与 NaN 本身比较,就像你能对其它的值做的那样,比如 nullundefined。不是这样。

var a = 2 / "foo";

a == NaN;   // false
a === NaN;  // false

NaN 是一个非常特殊的值,它从来不会等于另一个 NaN 值(也就是,它从来不等于它自己)。实际上,它是唯一一个不具有反射性的值(没有恒等性 x === x)。所以,NaN !== NaN。有点奇怪,对吧?

那么,如果不能与 NaN 进行比较(因为这种比较将总是失败),我们该如何测试它呢?

var a = 2 / "foo";

isNaN( a ); // true

够简单的吧?我们使用称为 isNaN(..) 的内建全局工具,它告诉我们这个值是否是 NaN。问题解决了!

别高兴得太早。

isNaN(..) 工具有一个重大缺陷。它似乎过于按照字面的意思(“不是一个数字”)去理解 NaN 的含义了 —— 它的工作基本上是:“测试这个传进来的东西是否不是一个 number 或者是一个 number”。但这不是十分准确。

var a = 2 / "foo";
var b = "foo";

a; // NaN
b; // "foo"

window.isNaN( a ); // true
window.isNaN( b ); // true -- 噢!

很明显,"foo" 根本 不是一个 number,但它也绝不是一个 NaN 值!这个 bug 从最开始的时候就存在于 JS 中了(存在超过了十九年的坑)。

在 ES6 中,终于提供了一个替代它的工具:Number.isNaN(..)。有一个简单的填补,可以让你即使是在前 ES6 的浏览器中安全地检查 NaN 值:

if (!Number.isNaN) {
    Number.isNaN = function(n) {
        return (
            typeof n === "number" &&
            window.isNaN( n )
        );
    };
}

var a = 2 / "foo";
var b = "foo";

Number.isNaN( a ); // true
Number.isNaN( b ); // false -- 咻!

实际上,通过利用 NaN 与它自己不相等这个特殊的事实,我们可以更简单地实现Number.isNaN(..) 的填补。在整个语言中 NaN 是唯一一个这样的值;其他的值都总是 等于它自己。

所以:

if (!Number.isNaN) {
    Number.isNaN = function(n) {
        return n !== n;
    };
}

怪吧?但是好用!

不管有意还是无意,在许多真实世界的 JS 程序中 NaN 可能是一个现实的问题。使用 Number.isNaN(..)(或者它的填补)这样的可靠测试来正确地识别它们是一个非常好的主意。

如果你正在程序中仅使用 isNaN(..),悲惨的现实是你的程序 有 bug,即便是你还没有被它咬到!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值