lodash源码分析之NaN不是NaN
暗恋之纯粹,在于不求结果,完全把自己锁闭在一个单向的关系里面。
——梁文道《暗恋到偷窥》
本文为读 lodash 源码的第五篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
本篇分析的是 eq 函数。
作用与用法
eq 函数用来比较两个值是否相等。遵循的是 SameValueZero 规范。
var obj1 = {test: 1}
var obj2 = {test: 1}
var obj3 = obj1
_.eq(1,1) // true
_.eq(+0, -0) // true
_.eq(obj1, obj3) // true
_.eq(obj1, obj2) // false
_.eq(NaN, NaN) // false
几个比较规范
SameValueNonNumber
这个规范规定比较的值 x 和 y 都不为 Number 类型,照抄规范如下:
x的类型不为Number类型y的类型与x的类型一致- 如果
x的类型为Undefined,返回true - 如果
x的类型为Null,返回true - 如果
x的类型为String,并且x和y的长度及编码相同,返回true,否则返回false - 如果
x的类型为Boolean,并且x和y同为true或同为false,返回true,否则返回false - 如果
x的类型为Symbol,并且x和y具有相同的Symbol值,返回true,否则返回false - 如果
x和y指向同一个对象,返回true, 否则返回false
Strict Equality Comparison
js 中的全等(===)便是遵循这个规范,照搬规范如下:
- 如果
x和y的类型不同,返回false - 如果
x的为Number类型:- a. 如果
x为NaN,返回false - b. 如果
y为NaN,返回false - c. 如果
x和y的数值一致,返回true - d. 如果
x为+0并且y为-0,返回true - e. 如果
x为-0并且y为+0,返回true - f. 返回
false
- a. 如果
- 按照 SameValueNonNumber 的结果返回
SameValue
规范如下:
- 如果
x和y的类型不同,返回false - 如果
x的类型为Number- a. 如果
x为NaN并且y为NaN,返回true - b. 如果
x为+0并且y为-0,返回false - c. 如果
x为-0并且y为+0, 返回false - d. 如果
x和y的数值一致,返回true - e. 返回
false
- a. 如果
- 按照 SameValueNonNumber 的结果返回
SameValueZero
这个是 eq 遵循的规范,如下:
- 如果
x和y的类型不同,返回false - 如果
x的类型为Number- a. 如果
x为NaN并且y为NaN,返回true - b. 如果
x为+0并且y为-0,返回true - c. 如果
x为-0并且y为+0, 返回true - d. 如果
x和y的数值一致,返回true - e. 返回
false
- a. 如果
- 按照 SameValueNonNumber 的结果返回
小结:SameValueNonNumber 是基本,Strict Equality Comparison 、SameValue 和 SameValueZero 只是在对待 +0、-0 和 NaN 上有区别。
源码分析
来看下 eq 的源码:
function eq(value, other) {
return value === other || (value !== value && other !== other)
}
其实eq 的源码其实就只有这么一句。
既然 eq 遵循的是 SameValueZero 规范,那就将源码来拆解一下,看它是怎样符合规范的。
首先,看第一部分:
value === other
就是这么一段,符合的是 Strict Equality Comparison 规范,通过对比可以发现, Strict Equality Comparison 和 SameValueZero 只在对待 NaN 上有区别。
Strict Equality Comparison 规定就算 x 和 y 都为 NaN 时,返回的是 false, NaN === NaN 返回的就是 false。但是 SameValueZero 返回的是规定 x 和 y 都为 NaN 时返回的是 true。因此只需要在 Strict Equality Comparison 的基础上处理 NaN 就可以了。
下面这段便是处理 NaN 的:
(value !== value && other !== other)
在 js 中,只有 NaN 和自身是不相等的,当两个需要比较的值都是和自身不相等时,表明这两个值都为 NaN,返回 true。
这样便遵循了 SameValueZero 的比较实现。
可以用Object.is()吗?
Object.is(NaN, NaN) 返回的是 true ,所以 eq 同样可以改成:
function eq(value, other) {
return value === other || Object.is(value, other)
}
Object.is 同样是比较两个值是否一样,但是 Object.is(+0, -0) 返回的是 false, 它遵循是的 SameValue 规范,因此不可以直接用 Object.is 替代 eq 。
可以用isNaN()吗?
还有个 isNaN 的全局方法,可以用来判断一个值是否为 NaN。例如 isNaN(NaN) 会返回 true ,那 eq 是否可以改成以下形式呢?
function eq(value, other) {
return value === other || (isNaN(value) && isNaN(other))
}
答案是:不可以!
isNaN 有一个很怪异的行为,如果传入的参数不为 Number 类型,会尝试转换成 Number 类型之后再做是否为 NaN 的判断。所以类似 isNaN('notNaN') 返回的也是 true ,因为字符串 notNaN 会先被转换成 NaN 再做判断,这不是我们想要的结果。
可以用Number.isNaN()吗
为了修复 isNaN 的缺陷,es6 在 Number 对象上扩展了 isNaN 方法,只有是 NaN 时才会返回 true,因此用 Number.isNaN 来判断是安全的。所以 eq 同样可以改成以下形式:
function eq(value, other) {
return value === other || (Number.isNaN(value) && Number.isNaN(other))
}
参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见: 
作者:对角另一面
lodash源码分析之NaN不是NaN的更多相关文章
- lodash源码分析之自减的两种形式
这个世界需要一个特定的恶人,可以供人们指名道姓,千夫所指:"全都怪你". --村上春树<当我谈跑步时我谈些什么> 本文为读 lodash 源码的第六篇,后续文章会更新到 ...
- lodash源码分析之数组的差集
外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡. --卡尔维诺<烟云> 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodas ...
- lodash源码分析之List缓存
昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方 顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 --洛夫<因为风的缘故& ...
- lodash源码分析之缓存方式的选择
每个人心里都有一团火,路过的人只看到烟. --<至爱梵高·星空之谜> 本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitb ...
- lodash源码分析之缓存使用方式的进一步封装
在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...
- lodash源码分析之baseFindIndex中的运算符优先级
我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...
- lodash源码分析之compact中的遍历
小时候, 乡愁是一枚小小的邮票, 我在这头, 母亲在那头. 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头. 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头. 而现在, 乡愁是一湾浅 ...
- lodash源码分析之chunk的尺与刀
以不正义开始的事情,必须用罪恶使它巩固. --莎士比亚<麦克白> 最近很多事似乎印证了这句话,一句谎言最后要用一百句谎言来圆谎. 本文为读 lodash 源码的第二篇,后续文章会更新到这个 ...
- lodash源码分析之获取数据类型
所有的悲伤,总会留下一丝欢乐的线索,所有的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找希望的缺口,却在惊醒时,瞥见绝美的阳光! --几米 本文为读 lodash 源码的第十八篇,后续文章会更新到 ...
随机推荐
- Java数据结构和算法(五)——队列
前面一篇博客我们讲解了并不像数组一样完全作为存储数据功能,而是作为构思算法的辅助工具的数据结构——栈,本篇博客我们介绍另外一个这样的工具——队列.栈是后进先出,而队列刚好相反,是先进先出. 1.队列的 ...
- 第四届河南省ACM SUBSTRING 字符串处理
SUBSTRING 时间限制: 1 Sec 内存限制: 128 MB 提交: 17 解决: 5 [提交][状态][讨论版] 题目描述 You are given a string input. Y ...
- Nginx动静分离实现
Nginx动静分离实现: Nginx是一种轻量级,高性能,多进程的Web服务器,非常适合作为静态资源的服务器使用,而动态的访问操作可以使用稳定的Apache.Tomcat及IIS等来实现,这里就以Ng ...
- Mysql服务器SQL模式 (官方精译)
MySQL服务器可以在不同的SQL模式下运行,并且可以根据sql_mode系统变量的值对不同的客户端应用不同的模式.DBA可以设置全局SQL模式以匹配站点服务器操作需求,并且每个应用程序可以将其会话S ...
- 真正的精通Java是种什么样的境界?
会在不适合使用java的地方不用java! 作为一名软件开发者,要追求的,应该是不断地提升自己分析问题把握事物关键点,实事求是地给出切实可行且能"一剑封喉"的优雅解决方案的能力,再 ...
- Android异步消息机制
Android中的异步消息机制分为四个部分:Message.Handler.MessageQueue和Looper. 其中,Message是线程之间传递的消息,其what.arg1.arg2字段可以携 ...
- QScintilla 编译
直接上英文了,不翻译了.看不懂的可以留言哈. ———————————————————————————————————————— Installation As supplied QScintilla ...
- 将STM32 iap hex文件与app hex文件合并为一个hex文件
日前公司产品需要增加远程升级功能,boot loader程序写好后交予生产部门使用时他们反馈每个产品程序需要刷写两次(一个boot loader 一个app程序),生产进度变慢浪费时间,于是乎研究如何 ...
- 我理解的malloc()和free()。
关于malloc和free这两个函数,malloc的使用方法演示样例:int *p=(int *)malloc(2*sizeof(int)); 它表示在堆中开辟一块大小为2*sizeof(int)的一 ...
- 尝试造了个工具类库,名为 Diana
项目地址: diana 文档地址: http://muyunyun.cn/diana/ 造轮子的意义 为啥已经有如此多的前端工具类库还要自己造轮子呢?个人认为有以下几个观点吧: 定制性强,能根据自己的 ...