资讯专栏INFORMATION COLUMN

lodash源码分析之NaN不是NaN

gaomysion / 3030人阅读

摘要:梁文道暗恋到偷窥本文为读源码的第五篇,后续文章会更新到这个仓库中,欢迎也会同步仓库的更新,地址本篇分析的是函数。源码分析来看下的源码其实的源码其实就只有这么一句。但是返回的是规定和都为时返回的是。

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

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

本文为读 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 类型,照抄规范如下:

x 的类型不为 Number 类型

y 的类型与 x 的类型一致

如果 x 的类型为 Undefined ,返回 true

如果 x 的类型为 Null ,返回 true

如果 x 的类型为 String,并且 xy 的长度及编码相同,返回 true,否则返回 false

如果 x 的类型为 Boolean ,并且 xy 同为 true 或同为false ,返回 true,否则返回 false

如果 x 的类型为 Symbol ,并且 xy 具有相同的 Symbol 值,返回 true,否则返回 false

如果 xy 指向同一个对象,返回 true, 否则返回 false

Strict Equality Comparison

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

如果 xy 的类型不同,返回 false

如果 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

按照 SameValueNonNumber 的结果返回

SameValue

规范如下:

如果 xy 的类型不同,返回 false

如果 x 的类型为 Number

a. 如果 xNaN 并且 yNaN ,返回 true

b. 如果 x+0 并且 y-0 ,返回 false

c. 如果 x-0 并且 y+0 , 返回 false

d. 如果 xy 的数值一致,返回 true

e. 返回 false

按照 SameValueNonNumber 的结果返回

SameValueZero

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

如果 xy 的类型不同,返回 false

如果 x 的类型为 Number

a. 如果 xNaN 并且 yNaN ,返回 true

b. 如果 x+0 并且 y-0 ,返回 true

c. 如果 x-0 并且 y+0 , 返回 true

d. 如果 xy 的数值一致,返回 true

e. 返回 false

按照 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))
}
参考

ECMAScript® 2016 Language Specification

MDN:Number.isNaN()

MDN:isNaN()

License

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

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

作者:对角另一面

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90643.html

相关文章

  • lodash源码分析自减的两种形式

    摘要:作用与用法是的内部函数,之前在源码分析之缓存介绍过一种这样的数据结构这是一个二维数组,每项中的第一项作为缓存对象的,第二项为缓存的值。 这个世界需要一个特定的恶人,可以供人们指名道姓,千夫所指:全都怪你。——村上春树《当我谈跑步时我谈些什么》 本文为读 lodash 源码的第六篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitbook也会同步仓库的更新...

    Keven 评论0 收藏0
  • lodash源码分析数组的差集

    摘要:依赖源码分析之缓存使用方式的进一步封装源码分析之源码分析之源码分析之的实现源码分析之源码分析的调用如果有传递,则先调用,使用生成要比较数组的映射数组。循环完毕,没有在第二个数组中发现相同的项时,将该项存入数组中。 外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡。——卡尔维诺《烟云》 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocke...

    Noodles 评论0 收藏0
  • lodash源码分析compact中的遍历

    摘要:到这里,源码分析完了。但是,有两个致命的特性的遍历不能保证顺序会遍历所有可枚举属性,包括继承的属性。的遍历顺序依赖于执行环境,不同执行环境的实现方式可能会不一样。 小时候,乡愁是一枚小小的邮票, 我在这头, 母亲在那头。 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头。 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头。 而现在, 乡愁是一湾浅浅的海峡, 我在这头, 大...

    dmlllll 评论0 收藏0
  • 我来阅读lodash源码——Math(一)

    摘要:首先是通过创建一个函数,然后向其传递参与计算的值,也就是说应该是返回一个函数,并且有两个参数。事实上就是这样,返回值是一个,是被加数,是加数。 源码注释版 这一组函数都是数学计算相关的,主要分为三类: 加减乘除:add、subtract、multiply、divide 求最大最小平均值:max、maxBy、min、minBy、sum、sumBy、mean、meanBy 小数的四舍...

    levinit 评论0 收藏0
  • 「读懂源码系列2」我从 lodash 源码中学到的几个知识点

    摘要:今天要讲的,是我从的源码实现文件中学到的几个很基础,却又容易被忽略的知识点。在函数式编程中,函数是一等公民,它可以只是根据参数,做简单的组合操作,再作为别的函数的返回值。所以,阅读源码,是一种很棒的重温基础知识的方式。 showImg(https://segmentfault.com/img/bVbpTSY?w=750&h=422); 前言 上一篇文章 「前端面试题系列8」数组去重(1...

    Amio 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<