摘要:例如其中的為,但是數組中沒有元素,是稀疏數組而每個位置都是有元素的,雖然每個元素都為,為密集數組。那稀疏數組和密集數組有什么區別呢在中最主要考慮的是兩者在迭代器中的表現。截取并返回新數組為新數組容器。
卑鄙是卑鄙者的通行證,高尚是高尚者的墓志銘。
——北島《回答》
看北島就是從這兩句詩開始的,高尚者已死,只剩卑鄙者在世間橫行。
本文為讀 lodash 源碼的第一篇,后續文章會更新到這個倉庫中,歡迎 star:pocket-lodash
gitbook也會同步倉庫的更新,gitbook地址:pocket-lodash
引言你可能會有點奇怪,原生的 slice 方法基本沒有兼容性的問題,為什么 lodash 還要實現一個 slice 方法呢?
這個問題,lodash 的作者已經在 why not the "baseslice" func use Array.slice(), loop faster than slice? 的 issue 中給出了答案:lodash 的 slice 會將數組當成密集數組對待,原生的 slice 會將數組當成稀疏數組對待。
密集數組VS稀疏數組我們先來看看犀牛書是怎樣定義稀疏數組的:
稀疏數組就是包含從0開始的不連續索引的數組。通常,數組的length屬性值代表數組中元素的個數。如果數組是稀疏的,length屬性值大于元素的個數。
如果數組是稀疏的,那么這個數組中至少有一個以上的位置不存在元素(包括 undefined )。
例如:
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參數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參數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?
Array.prototype.slice()
JavaScript: sparse arrays vs. dense arrays
【譯】JavaScript中的稀疏數組與密集數組
License署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)
最后,所有文章都會同步發送到微信公眾號上,歡迎關注,歡迎提意見:
作者:對角另一面
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89896.html
摘要:萬條數據依賴讀源碼之從看稀疏數組與密集數組原理的原理歸結起來就是切割和放置。尺在切割之前,需要用尺確定切割的數量。容器的長度剛好與塊的數量一致。當與塊的數量相等時,表示已經切割完畢,停止切割,最后將結果返回。 以不正義開始的事情,必須用罪惡使它鞏固。——莎士比亞《麥克白》 最近很多事似乎印證了這句話,一句謊言最后要用一百句謊言來圓謊。 本文為讀 lodash 源碼的第二篇,后續文章會...
摘要:本文首發于技術風暴源碼講解這是我們閱讀源碼的第篇博客,這一篇博客主要介紹的函數,這個函數內部的實現沒有依賴別的函數我們這篇博客就來講解一下這個函數。 本文首發于技術風暴-Lodash源碼講解 這是我們閱讀源碼的第1篇博客,這一篇博客主要介紹Lodash的slice函數,這個函數內部的實現沒有依賴別的函數;我們這篇博客就來講解一下這個slice函數。 我們首先來看一下這個函數的源碼,源碼...
摘要:數組是數據的有序列表,與其他語言不同的是,數組的每一項可以保存任何類型的數據。如下的代碼創建的就是一個密集數組稀疏數組與密集數組相反,并不強制要求數組元素是緊密相連的,即允許間隙的存在。 數組是數據的有序列表,與其他語言不同的是,ECMAScript 數組的每一項可以保存任何類型的數據。也就是說,可以用數組的第一個位置來保存字符串,用第二位置來保存數值,用第三個位置來保存對象, 以此類...
摘要:文檔地址中文文檔英文文檔源碼地址第一個函數是,不過源碼中依賴了,所以第一篇文章就從開始。這個函數的作用就是裁剪數組,從下標開始,到下標結束,但是并不包含,并將結果作為一個數組返回。并且注明了這個方法用于代替來確保數組正確返回。 百忙之中(閑來無事)想抽點時間好好讀一下源碼,于是就選了Lodash來寫一個系列罷。讀源碼順序就按照loadsh文檔順序來。 文檔地址:中文文檔?? 英文文檔源...
摘要:文檔地址中文文檔英文文檔源碼地址將數組拆分成多個長度的區塊,并將這些區塊組成一個新數組。如果無法被分割成全部等長的區塊,那么最后剩余的元素將組成一個區塊。 百忙之中(閑來無事)想抽點時間好好讀一下源碼,于是就選了Lodash來寫一個系列罷。讀源碼順序就按照loadsh文檔順序來。 文檔地址:中文文檔?? 英文文檔源碼地址:gayhub _.chunk(array, [size...
閱讀 3110·2021-11-10 11:36
閱讀 3312·2021-10-13 09:40
閱讀 6051·2021-09-26 09:46
閱讀 662·2019-08-30 15:55
閱讀 1409·2019-08-30 15:53
閱讀 1580·2019-08-29 13:55
閱讀 2997·2019-08-29 12:46
閱讀 3204·2019-08-29 12:34