暗恋之纯粹,在于不求结果,完全把自己锁闭在一个单向的关系里面。

——梁文道《暗恋到偷窥》

本文为读 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

这个规范规定比较的值 xy 都不为 Number 类型,照抄规范如下:

  1. x 的类型不为 Number 类型
  2. y 的类型与 x 的类型一致
  3. 如果 x 的类型为 Undefined ,返回 true
  4. 如果 x 的类型为 Null ,返回 true
  5. 如果 x 的类型为 String,并且 xy 的长度及编码相同,返回 true,否则返回 false
  6. 如果 x 的类型为 Boolean ,并且 xy 同为 true 或同为false ,返回 true,否则返回 false
  7. 如果 x 的类型为 Symbol ,并且 xy 具有相同的 Symbol 值,返回 true,否则返回 false
  8. 如果 xy 指向同一个对象,返回 true, 否则返回 false

Strict Equality Comparison

js 中的全等(===)便是遵循这个规范,照搬规范如下:

  1. 如果 xy 的类型不同,返回 false
  2. 如果 x 的为 Number 类型:
    • a. 如果 xNaN ,返回 false
    • b. 如果 yNaN ,返回 false
    • c. 如果 xy 的数值一致,返回 true
    • d. 如果 x+0 并且 y-0 ,返回 true
    • e. 如果 x-0 并且 y+0 ,返回 true
    • f. 返回 false
  3. 按照 SameValueNonNumber 的结果返回

SameValue

规范如下:

  1. 如果 xy 的类型不同,返回 false
  2. 如果 x 的类型为 Number
    • a. 如果 xNaN 并且 yNaN ,返回 true
    • b. 如果 x+0 并且 y-0 ,返回 false
    • c. 如果 x-0 并且 y+0 , 返回 false
    • d. 如果 xy 的数值一致,返回 true
    • e. 返回 false
  3. 按照 SameValueNonNumber 的结果返回

SameValueZero

这个是 eq 遵循的规范,如下:

  1. 如果 xy 的类型不同,返回 false
  2. 如果 x 的类型为 Number
    • a. 如果 xNaN 并且 yNaN ,返回 true
    • b. 如果 x+0 并且 y-0 ,返回 true
    • c. 如果 x-0 并且 y+0 , 返回 true
    • d. 如果 xy 的数值一致,返回 true
    • e. 返回 false
  3. 按照 SameValueNonNumber 的结果返回

小结:SameValueNonNumber 是基本,Strict Equality ComparisonSameValueSameValueZero 只是在对待 +0-0NaN 上有区别。

源码分析

来看下 eq 的源码:

function eq(value, other) {
return value === other || (value !== value && other !== other)
}

其实eq 的源码其实就只有这么一句。

既然 eq 遵循的是 SameValueZero 规范,那就将源码来拆解一下,看它是怎样符合规范的。

首先,看第一部分:

value === other

就是这么一段,符合的是 Strict Equality Comparison 规范,通过对比可以发现, Strict Equality ComparisonSameValueZero 只在对待 NaN 上有区别。

Strict Equality Comparison 规定就算 xy 都为 NaN 时,返回的是 falseNaN === NaN 返回的就是 false。但是 SameValueZero 返回的是规定 xy 都为 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 的缺陷,es6Number 对象上扩展了 isNaN 方法,只有是 NaN 时才会返回 true,因此用 Number.isNaN 来判断是安全的。所以 eq 同样可以改成以下形式:

function eq(value, other) {
return value === other || (Number.isNaN(value) && Number.isNaN(other))
}

参考

  1. ECMAScript® 2016 Language Specification
  2. MDN:Number.isNaN()
  3. MDN:isNaN()

License

署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

lodash源码分析之NaN不是NaN的更多相关文章

  1. lodash源码分析之自减的两种形式

    这个世界需要一个特定的恶人,可以供人们指名道姓,千夫所指:"全都怪你". --村上春树<当我谈跑步时我谈些什么> 本文为读 lodash 源码的第六篇,后续文章会更新到 ...

  2. lodash源码分析之数组的差集

    外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡. --卡尔维诺<烟云> 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodas ...

  3. lodash源码分析之List缓存

    昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方 顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 --洛夫<因为风的缘故& ...

  4. lodash源码分析之缓存方式的选择

    每个人心里都有一团火,路过的人只看到烟. --<至爱梵高·星空之谜> 本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitb ...

  5. lodash源码分析之缓存使用方式的进一步封装

    在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...

  6. lodash源码分析之baseFindIndex中的运算符优先级

    我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...

  7. lodash源码分析之compact中的遍历

    小时候, 乡愁是一枚小小的邮票, 我在这头, 母亲在那头. 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头. 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头. 而现在, 乡愁是一湾浅 ...

  8. lodash源码分析之chunk的尺与刀

    以不正义开始的事情,必须用罪恶使它巩固. --莎士比亚<麦克白> 最近很多事似乎印证了这句话,一句谎言最后要用一百句谎言来圆谎. 本文为读 lodash 源码的第二篇,后续文章会更新到这个 ...

  9. lodash源码分析之获取数据类型

    所有的悲伤,总会留下一丝欢乐的线索,所有的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找希望的缺口,却在惊醒时,瞥见绝美的阳光! --几米 本文为读 lodash 源码的第十八篇,后续文章会更新到 ...

随机推荐

  1. Python测试开发之---list、str、dict、tuple小结

    str的常用方法有: str.digits 0-9的表示str.uppercase 大写字母str.lowercase 小写字母str.letters 所有字母" ".join(s ...

  2. 使用python

    最近看视频学习,老师布置了个作业,关于如何使用python将多个excel进行合并,老师写的代码我感觉比较复杂,下面是我自己改良之后较简单的方式. 实现这个功能主要有两种方法,一种是用xlwd,xls ...

  3. C#在自定义事件里传递自定义数据,使用EventArgs的姿势

    EventArgs是包含事件数据的类的基类,用于传递事件的细节.今天分享的是使用泛型来约束EventArgs,在事件里传递自定义数据的例子. 正题 由于这个关注点很小,直接上代码了. 定义泛型类TEv ...

  4. python中字母与ascii码的相互转换

    在做python编程时,碰到了需要将字母转换成ascii码的,原本以为用Int()就可以直接将字符串转换成整形了,可是int()带了一个默认参数,base=10,这里表示的是十进制,若出现字母,则会报 ...

  5. java学习总结篇一--写在正式成为码农一年后

    一直想写一写工作了一年多的总结与感悟,今天正好有时间,也有这个兴致,随手总结一下这一年来学习及工作的情况. 大学时很无奈地被选择了计算机专业,本人对计算机,不讨厌,也算不上多喜欢.只是当惯了好学生,好 ...

  6. Ansible - 简介和应用自动化基础实践

    installAnsible简介和应用自动化基础实践 一.引入: 1.1  如官方定义,Ansible is The simplest way to automate apps and IT infr ...

  7. vue2的keep-alive的总结

    vue2的keep-alive的总结 keep-alive 是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM.结合vue-router中使用,可以缓存某个view的整个内容 ...

  8. 安装phpnow服务[Apache_pn]提示失败的解决方法

    win 7/win 8/Win10 phpnow提示"安装服务[Apache_pn]失败"错误解决办法汇总 常常在安装phpnow的时候,提示"安装服务 [ Apache_pn ] 失败&q ...

  9. 从无到有<前端异常监控系统>落地

    导火索 有一天一个测试同事的一个移动端页面白屏了,看样子是页面哪里报错了.  我自己打开页面并没有报错,最后发现报错只存在于他的手机,移动端项目又是在微信环境下,调试起来会比较麻烦,最后用他手机调试才 ...

  10. nginx防恶意域名解析

    今天无意间查看访问日志发现一个fhxywh.com的域名居然解析到了我的服务器,也就是说通过这个域名也能访问我的博客,这个就是赤裸裸的恶意域名解析了. 这个危害非常大,不仅会影响用户,而且不利于SEO ...