differenceBy

用法

_.differenceBy(array, [values], [iteratee=_.identity])

功能

与_.difference相比,它多接收一个iteratee参数。暂且称之为迭代器。

demo

_.differenceBy([3.1, 2.2, 1.3], [4.4, 2.5], Math.floor);

源码

上两篇已经涉及过相关的代码,仍是在baseDifference

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) { // 迭代器函数。 

    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 = values.length;
      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;
}

需要注意的代码片段

 if (iteratee) { // 迭代器函数。 

    values = arrayMap(values, baseUnary(iteratee)); // 
  }

便于思考,此时的differenceBy传入了三个参数array,values,iteratee,在当前代码片段已经通过arrayMap(values, baseUnary(iteratee)),已经遍历使用iteratee,返回的values更像是一个结果集。

为何提前调用。在最终的while循环中,还要遍历array,调用iteratee 进行比对。显然内层仍然需要一个遍历,但是我们不希望每个遍历再去调用一次迭代器。在外层一次处理,开销远远每次在其中调用。

筛选出结果的部分,

 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 = values.length;
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer; //跳会outer,继续执行  因为是求差集,也就是value中的元素,在array不能存在。 这里有相同,array中的当前元素就不应该在结果集里出现。
        }
      }
      result.push(value);
    }
    //不会执行下边的逻辑。
    else if (!includes(values, computed, comparator)) {
      result.push(value);
    }
  }

这不是结束的好时候

写到这,在官网提供的demo里,有如下的

_.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
// => [{ 'x': 2 }]

这里的iteratee,传入的是一个字符串x,我们大概知道它是什么什么意思,比对,对象的x的值。

我们可以推想,对于iteratee,我们会根据它不同的类型包装成不同的函数,lodash中相关代码如下

baseIteratee(iteratee, 2)
function baseIteratee(value) {
  // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
  // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
  if (typeof value == 'function') {
    return value;
  }
  if (value == null) {
    return identity;
  }
  if (typeof value == 'object') {
    return isArray(value)
      ? baseMatchesProperty(value[0], value[1])
      : baseMatches(value);
  }
  return property(value);
}

显然是如下的几种解决

  • value如果是函数,直接返回
  • 如果是null,返回identity
  • 如果是Object 又会根据是否是数组,返回不同的。
  • 以上都不属于,直接返回property(value)

identity,返回第一个参数。

function identity(value) {
  return value;
}

property,返回属性的值。

它的作用是

 * var objects = [
 *   { 'a': { 'b': 2 } },
 *   { 'a': { 'b': 1 } }
 * ];
 *
 * _.map(objects, _.property('a.b'));
 * // => [2, 1]

创建一个返回给定对象的 path 的值的函数。

这些又可以总结开一章'lodash中的迭代器',我们稍后再写。


最普通的一个
301 声望41 粉丝

永远不要做你擅长的事。


引用和评论

0 条评论