JS數組專題1️⃣_番外篇 ➖ lodash中的flatten

專題一已經給你們介紹了數組扁平化,本篇將給你們介紹 lodash 中的 flatten 是如何實現的。數組

1、lodash源碼

1.基礎函數

  • isFlattenable.js
// isFlattenable.js
import isArguments from '../isArguments.js' // 檢查 value 是不是一個類 arguments 對象,在本篇不予講解。

// ES6中內置屬性,可用於判斷數組是否可展開. 具體可見 MDN
const spreadableSymbol = Symbol.isConcatSpreadable;
/* value[Symbol.isConcatSpreadable] === true時可展開, 可手動設置爲false value[Symbol.isConcatSpreadable] = false */

/** * 檢查值是否可展開. * * @private * @param {*} value 要檢查的值. * @returns {boolean} 返回布爾值,可展開 -> true, 不可展開 -> false. */
function isFlattenable(value) {
  return Array.isArray(value) || isArguments(value) ||
    !!(spreadableSymbol && value && value[spreadableSymbol]);
}

export default isFlattenable;
複製代碼
  • baseFlatten.js
// baseFlatten.js
import isFlattenable from './isFlattenable.js' // 是否能夠扁平化

/** * _.flatten 的基本實現與支持限制扁平化。 * * @private * @param {Array} array 須要扁平化的數組. * @param {number} depth 扁平化的深度. * @param {boolean} [predicate=isFlattenable] 該函數每次迭代判斷是否可展開操做. * @param {boolean} [isStrict] 是否可經過 predicate 檢查. * @param {Array} [result=[]] 初始結果數組. * @returns {Array} 返回新的扁平數組. */
function baseFlatten(array, depth, predicate, isStrict, result) {
  predicate || (predicate = isFlattenable);
  result || (result = []);

  if (array == null) {
    return result; // 若是數組爲空,則直接返回初始化結果
  }

  for (const value of array) {
    if (depth > 0 && predicate(value)) { // 判斷深度和是否可展開
      if (depth > 1) {
        // 若是深度大於1,繼續遞歸扁平化數組.
        baseFlatten(value, depth - 1, predicate, isStrict, result);
      } else {
        // 不然 push 進結果集中
        result.push(...value);
      }
    } else if (!isStrict) {
      // 若是不檢查數組,直接不展開放進結果集中
      result[result.length] = value;
    }
  }
  return result;
}

export default baseFlatten;
複製代碼
  • map.js
/** * 經過 iteratee 操做的每一個元素,建立一個新數組. * iteratee 有三個參數 (value, index, array). * * @since 5.0.0 * @category Array * @param {Array} array 要操做的數組. * @param {Function} iteratee 操做函數. * @returns {Array} 返回新的數組. * @example * * function square(n) { * return n * n * } * * map([4, 8], square) * // => [16, 64] */
function map(array, iteratee) {
  let index = -1;
  const length = array == null ? 0 : array.length;
  const result = new Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

export default map;
複製代碼

2.運用函數

實際上全部的 flatten 都是依賴上面的基礎函數。函數

  • flatten.js
// flatten.js
import baseFlatten from './.internal/baseFlatten.js'

/** * 扁平化一級數組. * * @since 0.1.0 * @category Array * @param {Array} array 將扁平化的數組. * @returns {Array} 返回扁平化後的數組. * @see flatMap, flatMapDeep, flatMapDepth, flattenDeep, flattenDepth * @example * * flatten([1, [2, [3, [4]], 5]]) * // => [1, 2, [3, [4]], 5] */
function flatten(array) {
  const length = array == null ? 0 : array.length;
  return length ? baseFlatten(array, 1) : [];
}

export default flatten;
複製代碼
  • flattenDeep.js
// flattenDeep.js
import baseFlatten from './.internal/baseFlatten.js'

/** 使用一個無限大的數值. */
const INFINITY = 1 / 0

/** * 遞歸最終扁平爲只有一級的數組. * * @since 3.0.0 * @category Array * @param {Array} array 將扁平化的數組. * @returns {Array} 返回扁平化後的數組. * @see flatMap, flatMapDeep, flatMapDepth, flatten, flattenDepth * @example * * flattenDeep([1, [2, [3, [4]], 5]]) * // => [1, 2, 3, 4, 5] */
function flattenDeep(array) {
  const length = array == null ? 0 : array.length;
  return length ? baseFlatten(array, INFINITY) : [];
}

export default flattenDeep;
複製代碼
  • flattenDepth.js
// flattenDepth.js
import baseFlatten from './.internal/baseFlatten.js'

/** * 遞歸將數組扁平化至指定深度. * * @since 4.4.0 * @category Array * @param {Array} array 將扁平化的數組. * @param {number} [depth=1] 最大扁平化深度. * @returns {Array} 返回扁平化後的數組. * @see flatMap, flatMapDeep, flatMapDepth, flattenDeep * @example * * const array = [1, [2, [3, [4]], 5]] * * flattenDepth(array, 1) * // => [1, 2, [3, [4]], 5] * * flattenDepth(array, 2) * // => [1, 2, 3, [4], 5] */
function flattenDepth(array, depth) {
  const length = array == null ? 0 : array.length;
  if (!length) {
    return [];
  }
  depth = depth === undefined ? 1 : +depth; // 沒有depth參數時爲1,有的時候轉化爲正數.
  return baseFlatten(array, depth);
}

export default flattenDepth;
複製代碼
  • flatMap.js
import baseFlatten from './.internal/baseFlatten.js'
import map from './map.js' // 封裝map函數

/** * 使用 map 從新建立數組,而後使用 baseFlatten 扁平化一級. * * @since 4.0.0 * @category Collection * @param {Array|Object} collection 將處理的數組. * @param {Function} iteratee 數組遍歷處理函數 * @returns {Array} 返回新的扁平化後的數組. * @see flatMapDeep, flatMapDepth, flatten, flattenDeep, flattenDepth, map, mapKeys, mapValues * @example * * function duplicate(n) { * return [n, n] * } * * flatMap([1, 2], duplicate) * // => [1, 1, 2, 2] */
function flatMap(collection, iteratee) {
  return baseFlatten(map(collection, iteratee), 1);
}

export default flatMap;
複製代碼
  • flatMapDeep.js

flattenDeep.js 相似,只不過扁平化以前和 flatMap.js 同樣先 map 一遍數組,而後進行扁平化處理。ui

  • flatMapDepth.js

flattenDepth.js 相似,只不過扁平化以前和 flatMap.js 同樣先 map 一遍數組,而後進行扁平化處理。this

2、數組原生函數

1.Array.prototype.flat

  • 本函數和 flattenDepth 相似,只不過是多了一個刪除空項的功能,掛載在 Array 實例下的函數。
// 判斷是否可展開
function isFlattenable(value) {
  const spreadableSymbol = Symbol.isConcatSpreadable;
  return Array.isArray(value) || _.isArguments(value) ||
    !!(spreadableSymbol && value && value[spreadableSymbol]);
}

// 判斷空
function isEmpty(value) {
  if (value === undefined || value === null) {
    return true;
  }
  return false;
}

function baseFlatten(array, depth, predicate, isStrict, result) {
  predicate || (predicate = isFlattenable);
  result || (result = []);

  if (array == null) {
    return result; // 若是數組爲空,則直接返回初始化結果
  }

  for (const value of array) {
    if (depth > 0 && predicate(value)) { // 判斷深度和是否可展開
      if (depth > 1) {
        // 若是深度大於1,繼續遞歸扁平化數組.
        baseFlatten(value, depth - 1, predicate, isStrict, result);
      } else {
        // 不然 push 進結果集中
        result.push(...value);
      }
    } else if (!isStrict && !isEmpty(value)) {
      // 若是不檢查數組而且不爲空,直接不展開放進結果集中
      result[result.length] = value;
    }
  }
  return result;
}

/** * 遞歸將數組扁平化至指定深度. * * @param {number} [depth=1] 最大扁平化深度. * @returns {Array} 返回扁平化後的數組. * @example * * const array = [1, [2, [3, [4]], 5]] * * array.flat(1) * // => [1, 2, [3, [4]], 5] * * array.flat(2) * // => [1, 2, 3, [4], 5] */
Array.prototype.flat = function flattenDepth(depth) {
  const array = this;
  const length = array.length;
  if (!length) {
    return [];
  }
  depth = depth === undefined ? 1 : +depth; // 沒有depth參數時爲1,有的時候轉化爲正數.
  return baseFlatten(array, depth);
}
複製代碼
相關文章
相關標籤/搜索