你可能不知道的 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 吧?
你可能不知道的 NaN 以及 underscore 1.8.3 _.isNaN 的一个 BUG的更多相关文章
- 你所不知道的setTimeout
JavaScript提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成.它们向任务队列添加定时任务.初始接触它的人都觉得好简单 ...
- js值----你所不知道的JavaScript系列(6)
1.数组 在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串.数字.对象(object),甚至是其他数组(多维数组就是通过这种方式来实现的) .----<你所不知道的JavaS ...
- JavaScript 优雅的实现方式包含你可能不知道的知识点
有些东西很好用,但是你未必知道:有些东西你可能用过,但是你未必知道原理. 实现一个目的有多种途径,俗话说,条条大路通罗马.很多内容来自平时的一些收集以及过往博客文章底下的精彩评论,收集整理拓展一波,发 ...
- 关于setTimeout()你所不知道的地方,详解setTimeout()
关于setTimeout()你所不知道的地方,详解setTimeout() 前言:看了这篇文章,1.注意setTimeout引用的是全部变量还是局部变量了,当直接调用外部函数方法时,实际上函数内部的变 ...
- 你所不知道的setInterval
在你所不知道的setTimeout记载了下setTimeout相关,此篇则整理了下setInterval:作为拥有广泛应用场景(定时器,轮播图,动画效果,自动滚动等等),而又充满各种不确定性的这set ...
- 你可能不知道的陷阱, IEnumerable接口
1. IEnumerable 与 IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...
- 你真的会玩SQL吗?你所不知道的 数据聚合
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- 你所不知道的linq(二)
上一篇说了from in select的本质,具体参见你所不知道的linq.本篇说下from...in... from... in... select 首先上一段代码,猜猜结果是什么? class P ...
- swift与OC之间不得不知道的21点
swift与OC之间不得不知道的21点 自6月的WWDC大会上由苹果的大神Chris Lattner向我们首次展示swift至今已经大半年时间了,虽然绝大部分软件公司代码里还都见不到一丁点swif ...
随机推荐
- javascript的列表切换
演示地址:http://wjf444128852.github.io/demo/Carousel/index.html IE兼容性没处理,确切的说不太会,还望指点一二 思路: 1.js获取要给定点击事 ...
- Dog_Hybird的诞生
起因 开玩笑说“iOS搞不动了”,另外一方面iOS组的哥哥们给力,少一个我也妥妥的.又听闻web前端组来了一个不得了的人物,“老司机,带带我”这种机会不能错过,1个多月前就申请转web前端了.开始是苦 ...
- Hui之Hui.js 官方文档
基础 // 判断值是否是指定数据类型 var result = hui.isTargetType("百签软件", "string"); //=>true ...
- SharePoint 2013 图文开发系列之网站栏
网站栏的本质,就是一个xml的描述文件,所以创建过程,基本就是通过Feature部署一个Xml文件,然后修改Xml文件的网站栏描述. 1.添加新项目,选择SharePoint 2013 空项目,如下图 ...
- 通过API执行AutoCAD命令来…
大家知道AutoCAD功能丰富,而更可贵的是,这么多丰富的功能背后都有一个命令,有些东西,直接用API调用写起来可能很费劲或者无法实现,可如果能用命令的话却很简单,这时候我们就可以通过API来调用Au ...
- Android—Socket中关闭IO流后导致Socket关闭不能再收发数据的解决办法
以Socket发送数据为例: 发送数据时候要声明:DataOutputStream os = new DataOutputStream(socket.getOutputStream()); 最近开发遇 ...
- 关于UIScollView 中的contentOffset 的理解
大家对UIScollView 中的contentOffset 一直有疑问, 虽然看是一个简单的问题, 实际上并不简单. 当时我也有好多疑问, 后来在网上找了一下资料, 发现没有找到合理的解释, 因此自 ...
- 【从零开始学习Hadoop】--1.Hadoop的安装
第1章 Hadoop的安装1. 操作系统2. Hadoop的版本3. 下载Hadoop4. 安装Java JDK5. 安装hadoop6. 安装rsync和ssh7. 启动hadoop8. 测试had ...
- 深入解析Windows操作系统笔记——CH2系统结构
2.系统结构 本章主要介绍系统的总体结构,关键部件之间的交互,以及运行在什么环境. 2.系统结构 2.1 需求和设计目标 2.2 操作系统模型 2.3 总体结构 2.3.1 可移植性 2.3.2 对称 ...
- mysql中类似indexOf的方法LOCATE()
LOCATE(substr,str), LOCATE(substr,str,pos) 第一个语法返回substr在字符串str 的第一个出现的位置. 第二个语法返回子符串 substr 在字符串st ...