这篇文章并不在我的 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 吧?