flatten()、flattenDeep()、flattenDepth()

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章