flatten()、flattenDeep()、flattenDepth()
每天更新一個lodash方法源碼解析
flatten()、flattenDeep()、flattenDepth()都是用於對數組的扁平化處理,不同之處在於扁平化的層級,flatten()是對數組進行一層扁平化處理,flattenDeep()是對數組完全扁平化處理,flattenDepth()是對數組進行指定層級的扁平化處理,其內部的實現都是基於baseFlatten()方法。
flatten()方法源碼:
// flatten.js
// flatten方法用於對數組進行扁平化一層的操作
function flatten(array) {
// 取數組的length
const length = array == null ? 0 : array.length
// 當length大於0時,調用baseFlatten方法,扁平化層級爲1
return length ? baseFlatten(array, 1) : []
}
flattenDeep()方法源碼:
// flattenDeep.js
// flattenDeep方法用於對數組完全扁平化處理
function flattenDeep(array) {
// 取數組的length
const length = array == null ? 0 : array.length
// 當length大於0時,調用baseFlatten方法,扁平化層級爲INFINITY(無限大),意思就是將數組中的元素扁平化到不能扁平化了爲止
return length ? baseFlatten(array, INFINITY) : []
}
flattenDepth()方法源碼:
// flattenDepth.js
// flattenDepth方法通過指定扁平化層級depth來實現數組扁平化到指定層
function flattenDepth(array, depth) {
// 取數組的length
const length = array == null ? 0 : array.length
// length爲0,直接返回空數組
if (!length) {
return []
}
// 判斷depth,當depth爲undefined時取1,否則通過加運算符對其進行Number類型轉換(PS:對任意值前通過一個加運算符操作可以自動實現其Number類型轉換)
depth = depth === undefined ? 1 : +depth
// 調用baseFlatten方法,扁平化層級爲depth
return baseFlatten(array, depth)
}
baseFlatten()方法源碼:
// baseFlatten.js
// baseFlatten方法用於對數組扁平化處理,從源碼中可以看出baseFlatten可提供的扁平化處理方式有三種,一種是完全扁平化,一種是扁平化指定層,最後一種是僅扁平化一層
// baseFlatten有5個參數:array:等待扁平化處理的數組,depth:扁平化的層級,predicate:數組中每個元素都需要調用的迭代器(扁平化時需根據predicate返回結果來決定處理方式),isStrict:是否嚴格模式(用於當depth小於0或者調用predicate返回值爲false時的處理方式),result:最終返回的數組
function baseFlatten(array, depth, predicate, isStrict, result) {
predicate || (predicate = isFlattenable)
result || (result = [])
// 如果array爲null,返回空數組
if (array == null) {
return result
}
// 遍歷array數組
/*
遍歷array數組的時候,在想一個問題,如果array數組中的一個元素是個對象,那它可以展開嗎?
predicate方法很好的解決了我這個疑問,predicate方法默認是isFlattenable方法,isFlattenable方法就是用來判斷一個值是否可以展開進行扁平化處理
*/
for (const value of array) {
// 遍歷array數組時,如果depth大於0且當前元素可扁平化,那麼繼續判斷depth是否大於1,大於1,則對當前元素繼續調用baseFlatten方法,也就是採用遞歸的方式進行完全展開
// 如果depth等於1,那麼採用es6擴展運算符(擴展運算符用三個點號表示,功能是把數組或類數組對象展開成一系列用逗號隔開的值)處理並添加到result中
// 如果depth小於0或者predicate方法返回false時,此時需要判斷isStrict是否爲true,爲true則直接返回result,否則將當前值添加到result中
if (depth > 0 && predicate(value)) {
if (depth > 1) {
// Recursively flatten arrays (susceptible to call stack limits).
// 遞歸扁平化數組(容易受調用堆棧限制)
baseFlatten(value, depth - 1, predicate, isStrict, result)
} else {
result.push(...value)
}
} else if (!isStrict) {
result[result.length] = value
}
}
// 最後返回result結果
return result
}
isFlattenable()方法源碼:
// isFlattenable.js
// isConcatSpreadable是定義的一個只讀屬性,類型爲布爾值,當一個對象擁有該屬性且其值爲true時,表明該對象可以調用Array.prototype.concat()方法來將其內部所有的元素組合到一個數組中
// 在isFlattenable方法中,它可以用來判斷value是否可扁平化
const spreadableSymbol = Symbol.isConcatSpreadable
// isFlattenable方法用於判斷value是否可扁平化
// 可扁平化的條件是value爲類數組(數組或arguments對象)或者value對象存在isConcatSpreadable屬性,且其值爲true
function isFlattenable(value) {
return Array.isArray(value) || isArguments(value) ||
!!(value && value[spreadableSymbol])
}
看了上面的源碼解析後可以通過下面例子加深一下:
example:
_.flatten([1, 2, [3, [4, 5]], 6])
// [1, 2, 3, [4, 5], 6]
_.flatten([[1, [2, [3, 4, [5, [6]]]]], [7, [9, [10, 11]]]])
// [1, [2, [3, 4, [5, [6]]]], 7, [9, [10, 11]]]
_.flattenDeep([[1, [2, [3, 4, [5, [6]]]]], [7, [9, [10, 11]]]])
// [1, 2, 3, 4, 5, 6, 7, 9, 10, 11]
_.flattenDepth([[1, [2, [3, 4, [5, [6]]]]], [7, [9, [10, 11]]]], 3)
// [1, 2, 3, 4, [5, [6]], 7, 9, 10, 11]
實現思路:
flatten()、flattenDeep()、flattenDepth()都是用於對數組的扁平化處理,其內部的實現都是基於baseFlatten()方法;
baseFlatten()在實現時會對需要進行扁平化處理的數組進行遍歷,對於遍歷的每個元素都會根據扁平化層級depth以及當前元素是否可扁平化進行處理,當depth爲1,則直接使用es6的擴展運算符將當前值添加到返回值result數組中,當depth大於1且當前元素可扁平話,則遞歸調用baseFlatten()方法,遞歸調用baseFlatten()方法時depth需不斷減1;
2019/6/21