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 源码的第十八篇,后续文章会更新到 ...
随机推荐
- 前端自动化测试漫长路之——Selenium初探
引言 最近想解决前端开发或测试中的两个问题:一是界面UI的布局适配,能否在测试的过程中,通过命令操作真机打开相应页面然后截屏,通过对图片识别分类,发现有问题的图片,然后及时修复:二是页面性能分析,很多 ...
- 用phpmailer发送邮件提示SMTP Error: Could not connect to SMTP host解决办法
之前做项目的时候做了一个用phpmailer发送邮件的功能<CI框架结合PHPmailer发送邮件>,昨天步署上线(刚开始用新浪云,嫌贵,换成阿里了),测试的时候,发送邮件却意外报错了.. ...
- javassist:字节码编辑器工具
简介: javassist是一款可以在运行时生成字节码的工具,可以通过它来构造一个新的class对象.method对象,这个class是运行时生成的.可以通过简短的几行代码就可以生成一个新的class ...
- 关于hue安装后出现KeyError: "Couldn't get user id for user hue"的解决方法
首先说明出现此问题的原因是因为你使用的root用户安装了hue,然后在root用户下使用的build/env/bin/supervisor,如下图所示那样: 知道了原因,就容易解决问题了.首先要创建个 ...
- ssm开发关于web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" ...
- 【NOIP2016提高组】换教室
https://www.luogu.org/problem/show?pid=1850 题面很长,实质很水的一道期望DP题.题面自带劝退效果. 首先用Floyd算出任意两点的最短路径.然后设f(i,j ...
- c++用类写栈和队列的简单基本操作(实验)
继续更文.这次用类来写栈和队列,都是用数组模拟的: 以下是栈和队列的定义: 然后分别是栈和队列的类: 完整代码贴上: 栈 //使用类来定义栈 class ZHAN { public: STACK s; ...
- 基于python2.7通过boto3实现ec2表格化
#!/usr/bin/env python import xlwt,xlrd,datetime,json,os,xlutils.copy a={ 'VpcPeeringConnection': { ' ...
- pl/sql中if的用法
/*判断用户从键盘输入的数字1.如何使用if语句2.接收一个键盘输入(字符串)*/set serveroutput on--接收一个键盘输入--num:一个地址值:含义:在该地址上保存了输入的值acc ...
- [开源项目] Android校验库 - FireEye
简单易用的Android校验库. 这是一个简单Android校验库,按配置来验证用户输入的表单信息. 仅仅须要几行代码,就可以验证用户输入,而且将验证错误反馈给用户. 它内置了大量经常使用的验证类型, ...