卑鄙是卑鄙者的通行證,高尚是高尚者的墓誌銘。javascript
——北島《回答》html
看北島就是從這兩句詩開始的,高尚者已死,只剩卑鄙者在世間橫行。前端
本文爲讀 lodash 源碼的第一篇,後續文章會更新到這個倉庫中,歡迎 star:pocket-lodashjava
gitbook也會同步倉庫的更新,gitbook地址:pocket-lodashgit
你可能會有點奇怪,原生的 slice 方法基本沒有兼容性的問題,爲何 lodash 還要實現一個 slice 方法呢?github
這個問題,lodash 的做者已經在 why not the 'baseslice' func use Array.slice(), loop faster than slice? 的 issue 中給出了答案:lodash 的 slice 會將數組當成密集數組對待,原生的 slice 會將數組當成稀疏數組對待。數組
咱們先來看看犀牛書是怎樣定義稀疏數組的:微信
稀疏數組就是包含從0開始的不連續索引的數組。一般,數組的length屬性值表明數組中元素的個數。若是數組是稀疏的,length屬性值大於元素的個數。oop
若是數組是稀疏的,那麼這個數組中至少有一個以上的位置不存在元素(包括 undefined
)。spa
例如:
var sparse = new Array(10) var dense = new Array(10).fill(undefined)
其中 sparse
的 length
爲10,可是 sparse
數組中沒有元素,是稀疏數組;而 dense
每一個位置都是有元素的,雖然每一個元素都爲undefined
,爲密集數組 。
那稀疏數組和密集數組有什麼區別呢?在 lodash 中最主要考慮的是二者在迭代器中的表現。
稀疏數組在迭代的時候會跳過不存在的元素。
sparse.forEach(function(item){ console.log(item) }) dense.forEach(function(item){ console.log(item) })
sparse
根本不會調用 console.log
打印任何東西,可是 dense
會打印出10個 undefined
。
固然,除了對待稀疏數組跟原生的 slice 不一致外,其餘的規則仍是同樣的,下面是 lodash 實現 slice 的源碼。
function slice(array, start, end) { let length = array == null ? 0 : array.length if (!length) { return [] } start = start == null ? 0 : start end = end === undefined ? length : end if (start < 0) { start = -start > length ? 0 : (length + start) } end = end > length ? length : end if (end < 0) { end += length } length = start > end ? 0 : ((end - start) >>> 0) start >>>= 0 let index = -1 const result = new Array(length) while (++index < length) { result[index] = array[index + start] } return result }
let length = array == null ? 0 : array.length if (!length) { return [] }
不傳參時,length
默認爲0,不然獲取數組的長度。注意這裏用的是 array == null
,非 array === null
,包含了 undefined
的判斷。
因此在不傳參調用 lodash 的 slice 時,返回的是空數組,而原生的 slice 沒有這種調用方式。
start
參數用來指定截取的開始位置。
先來看下 MDN 對該參數的描述:
若是該參數爲負數,則表示從原數組中的倒數第幾個元素開始提取。
若是省略,則從索引0開始
start = start == null ? 0 : start
所以這段是處理省略的狀況,省略時,默認值爲0。
if (start < 0) { start = -start > length ? 0 : (length + start) }
這段是處理負數的狀況。
若是負數取反後比數組的長度還要大,即超出了數組的範圍,則取值爲0,表示從開始的位置截取,不然用 length + start
,即向後倒數。
start >>>= 0
最後,用在 >>>
來確保 start
參數爲整數或0。
由於 lodash 的 slice 除了能夠處理數組外,也能夠處理類數組,所以第一個參數 array
可能爲一個對象, length
屬性不必定爲數字。
end
參數用來指定截取的結束位置。
一樣來看下 MDN 對些的描述:
若是該參數爲負數,則它表示在原數組中的倒數第幾個元素結束製取。
若是end被省略,則slice會一直提取到原數組的末尾。
若是end大於數組長度,slice也會一直提取到原數組末尾。
end = end === undefined ? length : end
這段是處理 end
被省略的狀況,省略時,end
默認爲爲 length
,即截取到數組的末尾。
end = end > length ? length : end
這是處理 end
比數組長度大的狀況,若是被數組長度大,也會截取到數組的末尾。
if (end < 0) { end += length }
這段是處理負值的狀況,若是爲負值,則從數組末尾開始向前倒數。
這裏沒有像 start
同樣控制 end
的向前倒數完後是否爲負數,由於後面還有一層控制。
length = start > end ? 0 : ((end - start) >>> 0)
新數組的長度計算方式很簡單,就是用 edn - start
便可得出。
上面說到,沒有控制最終 end
是否爲負數的狀況。這裏用的是 start
和 end
的比較,若是 start
比 end
大,則新數組長度爲0,即返回一個空數組。不然用 end - start
來計算。
這裏一樣用了無符號右移位運算符來確保 length
爲正數或0。
let index = -1 const result = new Array(length) while (++index < length) { result[index] = array[index + start] } return result
result
爲新數組容器。
用 while
循環,從 start
位置開始,獲取原數組的值,依次存入新的數組中。
由於是經過索引取值,若是遇到稀疏數組,對應的索引值上沒有元素時,經過數組索引取值返回的是 undefined
, 但這並非說稀疏數組中該位置的值爲 undefined
。
最後將 result
返回。
javascript權威指南(第6版), David Flanagan著,淘寶前端團隊譯,機械工業出版社
why not the 'baseslice' func use Array.slice(), loop faster than slice?
署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)
最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:
做者:對角另外一面