你能够不知道的 NaN 以及 underscore 1.8.3 _.isNaN 的一个 BUG

这篇文章并不在我的 underscore 源码解读设计中,直到 @pod4g 同砚复兴了我的 issue(详见 https://github.com/hanzichi/underscore-analysis/issues/2#issuecomment-227361035)。实在之前也有同砚提出 isNaN 有 native 的 function,恰好借此文辨析下几个罕见的观点、要领,她们是 NaN,Number.NaN,isNaN,Number.isNaN,以及 underscore 中的 _.isNaN,趁便揪出了一个 BUG。

趁便安利,完整的 underscore 源码解读系列文章请戳 https://github.com/hanzichi/underscore-analysis

NaN & Number.NaN

ok,起首来了解下 NaN 和 Number.NaN 两个属性。

全局属性 NaN 示意 Not-A-Number 的值,望文生义,就是示意 不是一个数字

在编码中很少直接使用到 NaN。一般都是在盘算失利时,作为 Math 的某个要领的返回值涌现的(比方:Math.sqrt(-1))或许尝试将一个字符串剖析成数字但失利了的时刻(比方:parseInt(“blabla”))。如许做的优点是,不会抛出毛病,只需要在下一步的运算中推断上个步骤的运算效果是不是是 NaN 即可。

接着来看 Number.NaN,这货和 NaN 完整一样。实在,归根结柢这俩货都是属于 Number 范例:

Object.prototype.toString.call(NaN)
// "[object Number]"
Object.prototype.toString.call(Number.NaN)
// "[object Number]"

isNaN & Number.isNaN

接着来聊 isNaN 和 Number.isNaN 俩要领。

我们都晓得,虽然 NaN 作为 Number 范例,然则她不等于她自己, NaN == NaN 或许 NaN === NaN 都邑返回 false,那末怎样检测一个 NaN 值呢?答案人人都晓得了,isNaN 要领。

isNaN(NaN)
// true
isNaN(undefined)
// true
isNaN({})
// true
isNaN("abc")
// true

很多东西传入 isNaN 的效果都是 true,并不只是 NaN,为何?由于参数会先被强迫转换成 Number 范例,然后再举行推断。

Number(NaN)
// NaN
Number(undefined)
// NaN
Number({})
// NaN
Number("abc")
// NaN

ok,强迫转换后实在都变成了 NaN。

那末 Number.isNaN 和 isNaN 有何区分呢?和全局函数 isNaN() 比拟,该要领不会强迫将参数转换成数字,只要在参数是真正的数字范例,且值为 NaN 的时刻才会返回 true。

isNaN = function(value) {
    Number.isNaN(Number(value));
}

Number.isNaN = Number.isNaN || function(value) {
    return typeof value === "number" && isNaN(value);
}

值得注意的是,Number.isNaN 是 ES6 引入的,能够用上面的 Polyfill。

_.isNaN

末了来看看 underscore 关于 _.isNaN 的完成。

写代码起首得看需求,我们先看看 _.isNaN 的作用,查阅 API 文档 http://underscorejs.org/#isNaN

this is not the same as the native isNaN function, which will also return true for many other not-number values, such as undefined.

文档指出,_.isNaN 和 native 的 isNaN 并不一样,必需是个 Number 范例(才够返回 true),等等,好像和 Number.isNaN 一样?且慢下结论。

我们来看看 edge 版本对其的完成(https://github.com/jashkenas/underscore/blob/master/underscore.js):

// Is the given value `NaN`?
_.isNaN = function(obj) {
  return _.isNumber(obj) && isNaN(obj);
};

obj 得是个 Number 范例,并且能经由过程 isNaN 函数的推断,才返回 true。实在能经由过程这个函数的,只要两个值,NaN 和 new Number(NaN)(固然另有 Number.NaN,前面说了,NaN 和 Number.NaN 是一样的东西,下同)。

而能经由过程 Number.isNaN 函数的只要 NaN。(Number.isNaN(new Number(NaN) 会返回 false)

然则我看的 1.8.3 实际上是如许完成的:

_.isNaN = function(obj) {
  return _.isNumber(obj) && obj !== +obj;
};

实在这是有 BUG 的,很显然 new Number(0) 并不应当是 Not-A-Number。

_.isNaN(new Number(0));
// true

为何会如许写?这引发了我的猎奇,找了下历史记录,是为了修复这个 issue https://github.com/jashkenas/underscore/issues/749。该 issue 以为,_.isNaN(new Number(NaN)) 应当返回 true。

我们能够看下再之前的版本关于 _.isNaN 的完成(https://github.com/jashkenas/underscore/commit/6ebb43f9b3ba88cc0cca712383534619b82f7e9b):

_.isNaN = function(obj) {      
   return obj !== obj;   
};

我又翻了下当时的测试数据(https://github.com/jashkenas/underscore/blob/6ebb43f9b3ba88cc0cca712383534619b82f7e9b/test/objects.js),发明当时没有相似 new Number(0) 的测试数据(如今已经有了)。

总结

关于 NaN 的推断,假如只针对 Number 范例,用 underscore 最新版的 _.isNaN 推断完整没有问题,或许用 ES6 的 Number.isNaN,二者的区分就在于一个 new Number(NaN),不过话又说返来,没人会这么蛋疼去如许 new 一个 NaN 吧?

    原文作者:fish
    原文地址: https://segmentfault.com/a/1190000005770185
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞