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)

最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:

做者:對角另外一面

相關文章
相關標籤/搜索