外部世界那些破舊與貧困的樣子,可使我心裏世界獲得平衡。javascript
——卡爾維諾《煙雲》java
本文爲讀 lodash 源碼的第十七篇,後續文章會更新到這個倉庫中,歡迎 star:pocket-lodashgit
gitbook也會同步倉庫的更新,gitbook地址:pocket-lodashgithub
baseDifference
能夠用來獲取指定數組與另外一個數組的差集。segmentfault
這個函數是內部函數,是後面實現其它比較函數的核心函數。數組
baseDifference
的方法簽名以下:緩存
baseDifference(array, values, iteratee, comparator)
第一和第二個參數是須要比較的兩個數組;iteratee
能夠返回一值映射值,比較時,可使用映射的值來進行比較; comparator
是自定義比較函數,若是有傳遞,則調用自定義的比較函數來進行交集的比較。性能優化
import SetCache from './SetCache.js' import arrayIncludes from './arrayIncludes.js' import arrayIncludesWith from './arrayIncludesWith.js' import map from '../map.js' import cacheHas from './cacheHas.js'
const LARGE_ARRAY_SIZE = 200 function baseDifference(array, values, iteratee, comparator) { let includes = arrayIncludes let isCommon = true const result = [] const valuesLength = values.length if (!array.length) { return result } if (iteratee) { values = map(values, (value) => iteratee(value)) } if (comparator) { includes = arrayIncludesWith isCommon = false } else if (values.length >= LARGE_ARRAY_SIZE) { includes = cacheHas isCommon = false values = new SetCache(values) } outer: for (let value of array) { const computed = iteratee == null ? value : iteratee(value) value = (comparator || value !== 0) ? value : 0 if (isCommon && computed === computed) { let valuesIndex = valuesLength while (valuesIndex--) { if (values[valuesIndex] === computed) { continue outer } } result.push(value) } else if (!includes(values, computed, comparator)) { result.push(value) } } return result }
if (iteratee) { values = map(values, (value) => iteratee(value)) }
若是有傳遞 iteratee
,則先調用 map
,使用 iteratee
生成要比較數組的映射數組 values
。
由於後面會有嵌套循環,避免重複調用 iteratee
,影響性能,因此一開始就須要生成 values
的映射數組。
這裏使用了 isCommon
來標誌是否使用普通方式來處理。
if (comparator) { includes = arrayIncludesWith isCommon = false }
若是有傳遞比較函數,則將 isCommon
標記爲 false
,表示不用普通的方式來處理,後面能夠看到,最後會使用 includes
方法來處理,也即 arrayIncludesWith
方法。
else if (values.length >= LARGE_ARRAY_SIZE) { includes = cacheHas isCommon = false values = new SetCache(values) }
若是不須要使用自定義的比較方式,而且數組較大時(這裏限定了200),則使用 SetCache
類來緩存數組。
SetChche
其實使用的是 Map/Set
或者對象的方式來存儲,避免大數組嵌套循環時形成的性能損耗。
接下來就遍歷第一個數組 array
,將數組中的每一項和第二個數組的每一項比較。
if (isCommon && computed === computed) { let valuesIndex = valuesLength while (valuesIndex--) { if (values[valuesIndex] === computed) { continue outer } } result.push(value) } else if (!includes(values, computed, comparator)) { result.push(value) }
能夠看到,若是 isCommon
沒有標記爲 false
, 或者須要比較的值 computed
不爲 NaN
時,都採用嵌套循環的方式來比較。循環完畢,沒有在第二個數組中發現相同的項時,將該項存入數組 result
中。
若是 isCommon
爲 false
或者須要比較的值爲 NaN
時,則調用 includes
方法來比較。
由以前的分析得知:
comparator
,則 includes
爲 arrayIncludesWith
values
的長度超過 200
,則 includes
爲 cacheHas
includes
爲 arrayIncludes
在看代碼的時候,有一段十分奇怪:
value = (comparator || value !== 0) ? value : 0
這段代碼的意思是,在沒有提供 comparator
的狀況下,若是 value === 0
,則將 value
賦值爲 0
。
value === 0
時,可能爲 +0
、-0
和 0
,lodash 爲何要將它們都轉爲 0
呢?
後來看到 lodash 做者在 issue 中說,由於比較會用到 Set
,而 Set
是不能區分 +0
和 -0
的。
value = (comparator || value !== 0) ? value : 0; does it work?
署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)
最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:
做者:對角另外一面