美文网首页
每天一个lodash方法(2)

每天一个lodash方法(2)

作者: 公子七 | 来源:发表于2018-08-20 15:07 被阅读100次

周末过得有点颓,没有读源码。
现在补上。

difference && differenceBy && differenceWith

正文开始前,只想吐槽lodash这波方法封装的操作丝毫没有提高代码的可读性,反而读的更费力了。把功能无限拆分到最小,每个子功能都是一个方法,真的很差评了。
吐槽下这些方法结构如下:


lodash-difference

想想吧,这么多种方法,这么多文件,晕不晕!!!
吐槽完毕,回归正题。

difference

difference的作用用集合简单描述就是:给定两个集合A、B,difference(A) = CA(A∩B)
以下是源码。

difference.js
// 首先判断两个集合是否是数组。如果不是数组直接出门不送,是数组的话进行核心方法baseDifference的比较。
function difference(array, ...values) {
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
    : []
}

主文件就是一波天秀。isArrayLikeObject封装了多个方法,核心意思就是判断是不是数组。严谨如lodash,对于判断数组长度还封装了isLength方法来判定数组长度是否为数字。
baseFlatten是用来将数组扁平化的,我的理解是如果数组层次太深,对数组进行一个扁平化处理。
然而对于简单的数组来说,这一步操作就是走过场。尤其是在主代码中,baseFlatten的传参是depth=1,简直多此一举。
需要注意的是,在主方法中,传的两个参数并不是array和values,而是解构后的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)
    }
}

没错用了双层遍历来判断。
为了实现这双层遍历,对于边界进行了种种处理。
arrayIncludes&arrayIncludesWith方法用来判断数组是否包含某个元素。对于大数组,baseDifference提供了SetCache、CacheHas方法。

/**
 * -.difference的基本实现。
 *
 * @private
 * @param {Array} array The array to inspect. 需要被检查的数组
 * @param {Array} values The values to exclude. 需要被排除出去的数组
 * @param {Function} [iteratee] The iteratee invoked per element. 迭代器函数,调用每个element
 * @param {Function} [comparator] The comparator invoked per element. 比较器,也会调用每个ele
 * @returns {Array} Returns the new array of filtered values. 返回被过滤调的新的数组
 */
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) { // 迭代器函数。 这里需要研究arrayMap 和baseUnary方法了

    values = arrayMap(values, baseUnary(iteratee)); // 这里提前处理下  while循环里边有个判段,这里提前就遍历,处理数据。
  }
  if (comparator) { //comparator存在,includes从arrayIncludes =》arrayIncludesWith,
    includes = arrayIncludesWith;
    isCommon = false;
  }
  else if (values.length >= LARGE_ARRAY_SIZE) {
    //- 大型数组的优化,这里默认理解为超过200就是大数组。大的数组启用缓存。
    includes = cacheHas; // includes方法设置为cacheHas处理,这里也是做缓存
    isCommon = false;//标示  不是普通方式处理了
    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) { // 取出来的数据不是NaN
      var valuesIndex = valuesLength;
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer; //跳会outer,继续执行  因为是求差集,也就是value中的元素,在array不能存在。 这里有相同,array中的当前元素就不应该在结果集里出现。
        }
      }
      result.push(value);
    }
    else if (!includes(values, computed, comparator)) {
      result.push(value);
    }
  }
  return result;
}

相关方法分析
arrayIncludes

/**
 * 数组中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的基本实现,没有fromIndex的边界检查
 * @private
 * @param {Array} array The array to inspect. 被检查的数组
 * @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就调用baseFindIndex
    return baseFindIndex(array, baseIsNaN, fromIndex);
  }
  var index = fromIndex - 1, // -1
      length = array.length; //

  while (++index < length) {
    if (array[index] === value) {
      return index; //遍历判断结果,有符合条件 返回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. 用来被遍历的数组
 * @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); //结果数组

  while (++index < length) {
    result[index] = iteratee(array[index], index, array); // 这里就遍历了数据,返回遍历的结果。
  }
  return result;
}

difference方法系列还有另外两个方法:differenceBy和differenceWith
相比之下,differenceBy多了迭代器的方法,differenceWith增加比较器。
但是核心方法都是baseDifference,此处不再赘述。

BTW,baseDifference中arrayIncludesWith本质上是个比较方法。
在普通Difference方法中,实现思路是判断相等。因为differenceWith方法提供了比较器,因而需要使用另外一套比较思路,即arrayIncludesWith方法。

相关文章

网友评论

      本文标题:每天一个lodash方法(2)

      本文链接:https://www.haomeiwen.com/subject/hbetiftx.html