lodash源码分析之NaN不是NaN

暗恋之纯粹,在于不求结果,彻底把本身锁闭在一个单向的关系里面。javascript

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

本文为读 lodash 源码的第五篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodashgit

gitbook也会同步仓库的更新,gitbook地址:pocket-lodashes6

本篇分析的是 eq 函数。github

做用与用法

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)

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

做者:对角另外一面

相关文章
相关标签/搜索