每天一個lodash方法(2)

周末過得有點頹,沒有讀源碼。
現(xiàn)在補(bǔ)上。

difference && differenceBy && differenceWith

正文開始前,只想吐槽lodash這波方法封裝的操作絲毫沒有提高代碼的可讀性,反而讀的更費力了。把功能無限拆分到最小,每個子功能都是一個方法,真的很差評了。
吐槽下這些方法結(jié)構(gòu)如下:


lodash-difference

想想吧,這么多種方法,這么多文件,暈不暈?。?!
吐槽完畢,回歸正題。

difference

difference的作用用集合簡單描述就是:給定兩個集合A、B,difference(A) = CA(A∩B)
以下是源碼。

difference.js
// 首先判斷兩個集合是否是數(shù)組。如果不是數(shù)組直接出門不送,是數(shù)組的話進(jìn)行核心方法baseDifference的比較。
function difference(array, ...values) {
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
    : []
}

主文件就是一波天秀。isArrayLikeObject封裝了多個方法,核心意思就是判斷是不是數(shù)組。嚴(yán)謹(jǐn)如lodash,對于判斷數(shù)組長度還封裝了isLength方法來判定數(shù)組長度是否為數(shù)字。
baseFlatten是用來將數(shù)組扁平化的,我的理解是如果數(shù)組層次太深,對數(shù)組進(jìn)行一個扁平化處理。
然而對于簡單的數(shù)組來說,這一步操作就是走過場。尤其是在主代碼中,baseFlatten的傳參是depth=1,簡直多此一舉。
需要注意的是,在主方法中,傳的兩個參數(shù)并不是array和values,而是解構(gòu)后的array和...values。
這意味著。

...values = [2,3]
value = [[2,3]]

接下來是核心代碼basedifference
核心中的核心只有以下幾行

for (let value of array) {
    const computed = iteratee == null ? value : iteratee(value)

    value = (comparator || value !== 0) ? value : 0
    if (isCommon && computed === computed) {
      let valuesIndex = valuesLength
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer
        }
      }
      result.push(value)
    }
}

沒錯用了雙層遍歷來判斷。
為了實現(xiàn)這雙層遍歷,對于邊界進(jìn)行了種種處理。
arrayIncludes&arrayIncludesWith方法用來判斷數(shù)組是否包含某個元素。對于大數(shù)組,baseDifference提供了SetCache、CacheHas方法。

/**
 * -.difference的基本實現(xiàn)。
 *
 * @private
 * @param {Array} array The array to inspect. 需要被檢查的數(shù)組
 * @param {Array} values The values to exclude. 需要被排除出去的數(shù)組
 * @param {Function} [iteratee] The iteratee invoked per element. 迭代器函數(shù),調(diào)用每個element
 * @param {Function} [comparator] The comparator invoked per element. 比較器,也會調(diào)用每個ele
 * @returns {Array} Returns the new array of filtered values. 返回被過濾調(diào)的新的數(shù)組
 */
function baseDifference(array, values, iteratee, comparator) {
  var index = -1,
      includes = arrayIncludes,//方法。
      isCommon = true,
      length = array.length,
      result = [],
      valuesLength = values.length;

  if (!length) {
    return result;
  }
  if (iteratee) { // 迭代器函數(shù)。 這里需要研究arrayMap 和baseUnary方法了

    values = arrayMap(values, baseUnary(iteratee)); // 這里提前處理下  while循環(huán)里邊有個判段,這里提前就遍歷,處理數(shù)據(jù)。
  }
  if (comparator) { //comparator存在,includes從arrayIncludes =》arrayIncludesWith,
    includes = arrayIncludesWith;
    isCommon = false;
  }
  else if (values.length >= LARGE_ARRAY_SIZE) {
    //- 大型數(shù)組的優(yōu)化,這里默認(rèn)理解為超過200就是大數(shù)組。大的數(shù)組啟用緩存。
    includes = cacheHas; // includes方法設(shè)置為cacheHas處理,這里也是做緩存
    isCommon = false;//標(biāo)示  不是普通方式處理了
    values = new SetCache(values);
  }
  outer:
   //切記,比對的是array,values
  while (++index < length) {
    var value = array[index],//array的一個element
        computed = iteratee ? iteratee(value) : value;//如果有迭代器,就處理成為computed

    value = (comparator || value !== 0) ? value : 0;
    if (isCommon && computed === computed) { // 取出來的數(shù)據(jù)不是NaN
      var valuesIndex = valuesLength;
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer; //跳會outer,繼續(xù)執(zhí)行  因為是求差集,也就是value中的元素,在array不能存在。 這里有相同,array中的當(dāng)前元素就不應(yīng)該在結(jié)果集里出現(xiàn)。
        }
      }
      result.push(value);
    }
    else if (!includes(values, computed, comparator)) {
      result.push(value);
    }
  }
  return result;
}

相關(guān)方法分析
arrayIncludes

/**
 * 數(shù)組中Array是否包含 value ,
 * @private
 * @param {Array} [array] The array to inspect.
 * @param {*} target The value to search for.
 * @returns {boolean} Returns `true` if `target` is found, else `false`.
 */
function arrayIncludes(array, value) {
  var length = array ? array.length : 0;
  return !!length && baseIndexOf(array, value, 0) > -1;
}
// arrayIncludes(["123"],"123") => true

baseIndexOf

/**
 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
 * _.indexOf的基本實現(xiàn),沒有fromIndex的邊界檢查
 * @private
 * @param {Array} array The array to inspect. 被檢查的數(shù)組
 * @param {*} value The value to search for.  被查找的值
 * @param {number} fromIndex The index to search from. 從哪個位置開始search
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseIndexOf(array, value, fromIndex) {
  if (value !== value) { //NaN就調(diào)用baseFindIndex
    return baseFindIndex(array, baseIsNaN, fromIndex);
  }
  var index = fromIndex - 1, // -1
      length = array.length; //

  while (++index < length) {
    if (array[index] === value) {
      return index; //遍歷判斷結(jié)果,有符合條件 返回index,否則為-1
    }
  } 
  return -1;
}

arrayMap

/**
 * A specialized version of `_.map` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over. 用來被遍歷的數(shù)組
 * @param {Function} iteratee The function invoked per iteration. 迭代器
 * @returns {Array} Returns the new mapped array. 
 */
function arrayMap(array, iteratee) {
  var index = -1,
      length = array ? array.length : 0,
      result = Array(length); //結(jié)果數(shù)組

  while (++index < length) {
    result[index] = iteratee(array[index], index, array); // 這里就遍歷了數(shù)據(jù),返回遍歷的結(jié)果。
  }
  return result;
}

difference方法系列還有另外兩個方法:differenceBy和differenceWith
相比之下,differenceBy多了迭代器的方法,differenceWith增加比較器。
但是核心方法都是baseDifference,此處不再贅述。

BTW,baseDifference中arrayIncludesWith本質(zhì)上是個比較方法。
在普通Difference方法中,實現(xiàn)思路是判斷相等。因為differenceWith方法提供了比較器,因而需要使用另外一套比較思路,即arrayIncludesWith方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容