天天一個lodash方法-defference

difference方法

功能

建立一個具備惟一array值的數組,每一個值不包含在其餘給定的數組中。數組

使用方法

_.difference([3, 2, 1], [4, 2]);
// => [3, 1]

源碼分析

var difference = baseRest(function(array, values) {
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
    : [];
});

該方法用到了baseReset方法。它接收的參數是一個匿名函數,匿名函數內接收兩個參數,一個是array數組,另外一個就是defference的兩個參數緩存

/**
 * The base implementation of `_.rest` which doesn't validate or coerce arguments.
 * _.reset的基本實現,可是不會校驗或者corce參數。
 * @private
 * @param {Function} func The function to apply a rest parameter to.
 * @param {number} [start=func.length-1] The start position of the rest parameter.
 * @returns {Function} Returns the new function.
 */
function baseRest(func, start) { // start = undefined
  start = nativeMax(start === undefined ? (func.length - 1) : start, 0); // var nativeMax = Math.max;

  return function() {
    var args = arguments,
        index = -1,
        length = nativeMax(args.length - start, 0),
        array = Array(length);

    while (++index < length) {
      array[index] = args[start + index];
    }
    index = -1;
    var otherArgs = Array(start + 1);
    while (++index < start) {
      otherArgs[index] = args[index];
    }
    otherArgs[start] = array;
    return apply(func, this, otherArgs);
  };
}

baseReset_.rest方法(建立一個函數,調用func時,this綁定到建立的新函數,而且start以後的參數做爲數組傳入。)的基礎實現。閉包

看一下它執行的效果。app

var say = _.rest(function(what, names) {
  return what + ' ' + _.initial(names).join(', ') +
    (_.size(names) > 1 ? ', & ' : '') + _.last(names);
});
 
say('hello', 'fred', 'barney', 'pebbles');
// => 'hello fred, barney, & pebbles'

_.rest,傳入一個匿名函數,返回一個新函數,並賦值給了say,say('hello', 'fred', 'barney', 'pebbles'),其中hello對應的是what,剩餘的參數對應的是names函數

再回到baseReset.源碼分析

  • step1 start這裏,若是沒傳入start則start就是0
  • step2 baseReset執行後,返回了一個函數,因爲閉包,又保證了func,和start的訪問。

(往上看好煩啊,我copy了返回的函數到下邊)優化

// func函數 ,start=0
return  function() { // arguments 就像上文say函數調用時傳入的參數,假設此時傳入了6個參數,[a,b,c,d,e,f]
    var args = arguments,//args.length = 6  
        index = -1,
        length = nativeMax(args.length - start, 0),//6
        array = Array(length);// array爲長度爲6的數組,

    while (++index < length) { //   0,1,2,3,4,5 <6
      array[index] = args[start + index]; // arr[0] = args[0+0],arr[1]= args[0+1]
    }
    // finally  6個長度的數組`array` array = [a,b,c,d,e,f]
    index = -1; // 重置index = -1
    var otherArgs = Array(start + 1); // otherArgs 爲一個長度的數組 [] length = 1
    while (++index < start) {
      otherArgs[index] = args[index];// otherArgs  = 取出args[0]
    }
    
   
    otherArgs[start] = array;// otherArgs[0] = [a,b,c,d,e,f]
    return apply(func, this, otherArgs); // apply執行
  };

此處apply比原生的apply速度更快,做用跟原生js的apply用法和做用是同樣的。this

function apply(func, thisArg, args) {
  switch (args.length) {
    case 0: return func.call(thisArg);
    case 1: return func.call(thisArg, args[0]);
    case 2: return func.call(thisArg, args[0], args[1]);
    case 3: return func.call(thisArg, args[0], args[1], args[2]);
  }
  return func.apply(thisArg, args);
}

注意: while中的i++和 ++i的區別,++i是先算值再判斷符合條件,i++則是先判斷是否符合條件,再自增。rest

difference的關鍵代碼就是這段.code

baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))

接下來天然要看baseDifference,baseFlatten.

baseFlatten concat一篇中已經出現過了,是用來打平多維數組。

接下倆看baseDifference

**
 * -.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;
}

普通的處理方式相對好處理一些。isCommon用來標示是否使用普通處理。爲了作優化,做者顯然作了更多。有兩種狀況須要作優化

  • comparator函數傳入,此時incluedes=arrayIncludesWith
  • values數組太大。此時incluedes=cacheHas ,`values = new SetCache(values);
else if (!includes(values, computed, comparator)) {
      result.push(value);
    }

非正常狀況下作的處理代碼。

這塊詳細說,代碼就太長了再開一篇吧。

相關方法分析

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;
}

baseUnary

/**
 * 建立一個最多接受一個參數的函數,忽略多餘的參數。
 *
 * @private  
 * @param {Function} func The function to cap arguments for.
 * @returns {Function} Returns the new capped function.
 */
function baseUnary(func) {
  return function(value) {
    return func(value);
  };
}
相關文章
相關標籤/搜索