1. 程式人生 > >lodash源碼學習(2)

lodash源碼學習(2)

fine pop wid search log returns 2.3 不解釋 value

繼續學習lodash,依然是數組的方法

“Array” Methods

_.indexOf(array, value, [fromIndex=0])

獲取value在數組 array所在的索引值 使用 SameValueZero方式比較(第一個全等===的元素). 如果 fromIndex 值是負數, 則從array末尾起算

該方法依賴於strictIndexOf和baseIndexOf方法,先看它們的源碼

//_strictIndexOf.js

/**
 * _.indexOf的專業版本,對元素執行嚴格的比較方法(===)
 *
 * @param {Array} array 需要處理的數組.
 * @param {*} value 查找的值.
 * @param {number} fromIndex 查找的開始位置.
 * @returns {number} 返回第一個匹配的值的索引,或者-1.
 
*/ function strictIndexOf(array, value, fromIndex) { var index = fromIndex - 1,//數值索引 length = array.length;//數組長度 while (++index < length) {//遍歷,如果數組中的值和查找的值相同,返回對應的index if (array[index] === value) { return index; } } return -1;//返回-1 } module.exports = strictIndexOf;
//_baseIndexOf.js

var baseFindIndex = require(‘./_baseFindIndex‘),//baseFindIndex方法(見源碼學習(1))
    baseIsNaN = require(‘./_baseIsNaN‘),//判斷是不是NaN
    strictIndexOf = require(‘./_strictIndexOf‘);//strictIndexOf方法

/**
 * _.indexOf的基本實現,不對fromIndex的範圍進行檢查
 *
 * @param {Array} array 需要處理的數組.
 * @param {*} value 需要查找的值.
 * @param {number} fromIndex 查找的開始位置.
 * @returns {number} 返回第一個匹配的值的索引,或者-1.
 
*/ function baseIndexOf(array, value, fromIndex) { //如果value === value(也就是不為NaN),調用strictIndexOf方法,否則調用baseFindIndex方法,判斷條件為是否為NaN //並且將結果作為返回值返回 return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex); } module.exports = baseIndexOf;

然後再看indexOf方法的源碼

//indexOf.js

var baseIndexOf = require(‘./_baseIndexOf‘),//baseIndexOf方法
    toInteger = require(‘./toInteger‘);//轉化為整形

var nativeMax = Math.max;

/**
 *
 *
 * @param {Array} array 需要處理的數組.
 * @param {*} value 需要查找的值.
 * @param {number} [fromIndex=0] 查找的開始位置.
 * @returns {number} 返回第一個匹配的值的索引,或者-1.
 * @example
 *
 * _.indexOf([1, 2, 1, 2], 2);
 * // => 1
 *
 * // Search from the `fromIndex`.
 * _.indexOf([1, 2, 1, 2], 2, 2);
 * // => 3
 */
function indexOf(array, value, fromIndex) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回-1
    return -1;
  }
  var index = fromIndex == null ? 0 : toInteger(fromIndex);//查找的起始位置,如果沒有傳入fromIndex則為0,否則為fromIndex
  if (index < 0) {//如果index為負值,從末尾算起
    index = nativeMax(length + index, 0);
  }
  return baseIndexOf(array, value, index);//調用baseIndexOf方法,並將結果作為返回值返回
}

module.exports = indexOf;

_.initial(array)

得到數組的除了最後一個元素以外的所有元素.

//initial.js

var baseSlice = require(‘./_baseSlice‘);//同Array.slice方法

/**
 * 
 *
 * @param {Array} array 需要處理的數組.
 * @returns {Array} 返回切好的數組.
 * @example
 *
 * _.initial([1, 2, 3]);
 * // => [1, 2]
 */
function initial(array) {
  var length = array == null ? 0 : array.length;//數組長度
  return length ? baseSlice(array, 0, -1) : [];//如果不為空數組,調用baseSlice方法並將結果返回,否則返回空數組
}

module.exports = initial;

_.intersection([arrays])

取出各數組中都有的元素,使用 SameValueZero方式比較(===).

_.intersectionBy([arrays], [iteratee=_.identity])

和_.intersection很像,除了它接受一個遍歷器被每個元素調用,生成計算之後的屬性當他們比較的時候。遍歷器interatee只接收一個參數(value)

_.intersectionWith([arrays], [comparator])

和_.intersection很像,除了它接受一個比較規則被每個元素調用,比較方法接受兩個參數(arrVal,othVal)

這三個方法依賴於baseIntersection方法

//_baseIntersection.js

var SetCache = require(‘./_SetCache‘),//Set緩存數組
    arrayIncludes = require(‘./_arrayIncludes‘),//同Array.includes方法
    arrayIncludesWith = require(‘./_arrayIncludesWith‘),//同Array.includes方法,除了他接受一個比較方法
    arrayMap = require(‘./_arrayMap‘),//同Array.map
    baseUnary = require(‘./_baseUnary‘),//創建一個只有一個參數的方法,忽略其他的參數
    cacheHas = require(‘./_cacheHas‘);//判斷緩存中是否存在某個元素

var nativeMin = Math.min;

/**
 * _.intersection的基本實現, 不支持遍歷器的簡寫.
 *
 * @private
 * @param {Array} arrays 需要處理的數組.
 * @param {Function} [iteratee] 遍歷器,作用於每個元素.
 * @param {Function} [comparator] 比較器,作用於每個元素.
 * @returns {Array} 返回一個數組包含給定所有數組中共有的元素.
 */
function baseIntersection(arrays, iteratee, comparator) {
  var includes = comparator ? arrayIncludesWith : arrayIncludes,//如果有比較器,為arrayIncludesWith,否則為arrayIncludes
      length = arrays[0].length,//第一個數組的長度
      othLength = arrays.length,//給定的所有數組的長度
      othIndex = othLength,//給定所有數組的索引
      caches = Array(othLength),//創建一個緩存給定數組個數的數組
      maxLength = Infinity,//最大長度
      result = [];//返回結果
  //遍歷給定的數組
  while (othIndex--) {
    var array = arrays[othIndex];//當前數組
    if (othIndex && iteratee) {//如果不是第一個數組,並且有遍歷器,對當前數組調用遍歷器遍歷每個元素
      array = arrayMap(array, baseUnary(iteratee));//當前數組為調用之後的數組
    }
    maxLength = nativeMin(array.length, maxLength);//數組的最大長度
    //如果該數組長度超過120就創建Set緩存數組中並添加到caches中(用於優化)
    caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
      ? new SetCache(othIndex && array)
      : undefined;
  }
  array = arrays[0];//給定所有數組中的第一個

  var index = -1,//數組索引
      seen = caches[0];//第一個緩存數組,初始為undefined或者new SetCache(0)

  outer:
  //遍歷第一個數組
  while (++index < length && result.length < maxLength) {
    var value = array[index],//數組元素
        computed = iteratee ? iteratee(value) : value;//如果有遍歷器,對改元素調用,得到計算後的值

    value = (comparator || value !== 0) ? value : 0;//如果有比較器或者value不為0,value為value,否則為0
    if (!(seen//如果seen有值了,判斷是否含有computed,否則判斷結果中是否含有computed,如果都沒有computed
          ? cacheHas(seen, computed)
          : includes(result, computed, comparator)
        )) {
      othIndex = othLength;//其他數組的索引,初始為給定數組的長度
      //遍歷給定的數組,除了第一個
      while (--othIndex) {
        var cache = caches[othIndex];//緩存數組中的值
        //太長的數組就是cache,短的就是arrays[index],判斷是否含有computed,如果沒有,跳過此次循環
        if (!(cache
              ? cacheHas(cache, computed)
              : includes(arrays[othIndex], computed, comparator))
            ) {
          continue outer;
        }
      }
      if (seen) {//如果緩存存在,將computed添加到緩存中
        seen.push(computed);
      }
      result.push(value);//將value添加到結果數組中
    }
  }
  return result;//返回結果數組
}

module.exports = baseIntersection;

對應的方法在baseIntersection上進行處理

//intersection.js

var arrayMap = require(‘./_arrayMap‘),//同Array.map
    baseIntersection = require(‘./_baseIntersection‘),//baseIntersection方法
    baseRest = require(‘./_baseRest‘),//創建可以使用rest參數的方法
    castArrayLikeObject = require(‘./_castArrayLikeObject‘);//創建一個類似數組的對象,如果不是為空數組

/**
 * 
 * @param {...Array} [arrays] 需要處理的所有數組.
 * @returns {Array} 返回一個數組包含給定所有數組中共有的元素.
 * @example
 *
 * _.intersection([2, 1], [2, 3]);
 * // => [2]
 */
var intersection = baseRest(function(arrays) {//創建具備rest參數的方法
  var mapped = arrayMap(arrays, castArrayLikeObject);//將所有數組轉化為類似數組的對象
  return (mapped.length && mapped[0] === arrays[0])//如果傳入的數組個數不為0,並且第一個參數為數組,調用baseIntersection方法傳入mapped,並將結果作為返回值返回,否則返回空數組
    ? baseIntersection(mapped)
    : [];
});

module.exports = intersection;
//intersectionBy.js

var arrayMap = require(‘./_arrayMap‘),//同Array.map
    baseIntersection = require(‘./_baseIntersection‘),//baseIntersection方法
    baseIteratee = require(‘./_baseIteratee‘),//遍歷器封裝
    baseRest = require(‘./_baseRest‘), //創建具有rest參數的方法
    castArrayLikeObject = require(‘./_castArrayLikeObject‘),//創建類似數組的對象
    last = require(‘./last‘);//取得數組的最後一個元素

/**
 * 
 * @param {...Array} [arrays] 需要處理的數組.
 * @param {Function} [iteratee=_.identity] 遍歷器被每個元素調用.
 * @returns {Array} 返回一個數組包含給定所有數組中共有的元素.
 * @example
 *
 * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
 * // => [2.1]
 *
 * // The `_.property` iteratee shorthand.
 * _.intersectionBy([{ ‘x‘: 1 }], [{ ‘x‘: 2 }, { ‘x‘: 1 }], ‘x‘);
 * // => [{ ‘x‘: 1 }]
 */
var intersectionBy = baseRest(function(arrays) {//創建具備rest參數的方法
  var iteratee = last(arrays),//遍歷器為最後一個參數
      mapped = arrayMap(arrays, castArrayLikeObject);//將所有數組轉化為類似數組的對象

  if (iteratee === last(mapped)) {//如果遍歷器和mapped最後一個一樣(也就是最後一個參數為數組)
    iteratee = undefined;//不存在遍歷器
  } else {
    mapped.pop();//否則將mapped最後一個元素去掉(遍歷器轉化的元素)
  }
  return (mapped.length && mapped[0] === arrays[0])
  //如果傳入的數組個數不為0,並且第一個參數為數組,調用baseIntersection方法傳入mapped和iteratee,並且支持屬性的簡寫,並將結果作為返回值返回,否則返回空數組
    ? baseIntersection(mapped, baseIteratee(iteratee, 2))
    : [];
});

module.exports = intersectionBy;
//intersectionWith.js

var arrayMap = require(‘./_arrayMap‘),//同Array.map
    baseIntersection = require(‘./_baseIntersection‘),//baseIntersection方法
    baseRest = require(‘./_baseRest‘),//創建具有rest參數的方法
    castArrayLikeObject = require(‘./_castArrayLikeObject‘),//創建類似數組的對象
    last = require(‘./last‘);//取得數組的最後一個元素

/**
 *
 * @param {...Array} [arrays] 需要處理的數組.
 * @param {Function} [comparator] 比較方法.
 * @returns {Array} 返回一個數組包含給定所有數組中共有的元素.
 * @example
 *
 * var objects = [{ ‘x‘: 1, ‘y‘: 2 }, { ‘x‘: 2, ‘y‘: 1 }];
 * var others = [{ ‘x‘: 1, ‘y‘: 1 }, { ‘x‘: 1, ‘y‘: 2 }];
 *
 * _.intersectionWith(objects, others, _.isEqual);
 * // => [{ ‘x‘: 1, ‘y‘: 2 }]
 */
var intersectionWith = baseRest(function(arrays) {//創建具有rest參數的方法
  var comparator = last(arrays),//比較器為最後一個元素
      mapped = arrayMap(arrays, castArrayLikeObject);//將所有數組轉化為類似數組的對象

  comparator = typeof comparator == ‘function‘ ? comparator : undefined;//比較器如果不為function,則為undefined
  if (comparator) {//如果有比較器,將mapped最後一個元素去掉(比較器轉化的元素)
    mapped.pop();
  }
  //如果傳入的數組個數不為0,並且第一個參數為數組,調用baseIntersection方法傳入mapped和comparator,並將結果作為返回值返回,否則返回空數組
  return (mapped.length && mapped[0] === arrays[0])
    ? baseIntersection(mapped, undefined, comparator)
    : [];
});

module.exports = intersectionWith;

_.join(array, [separator=‘,‘])

轉化數組為字符串,用指定的分割符將每個元素連接起來.

//join.js

var arrayProto = Array.prototype;//數組的原型

var nativeJoin = arrayProto.join;//原生join方法

/**
 *
 * @param {Array} array 需要轉化的數組.
 * @param {string} [separator=‘,‘] 分割符.
 * @returns {string} 返回連接起來的字符串.
 * @example
 *
 * _.join([‘a‘, ‘b‘, ‘c‘], ‘~‘);
 * // => ‘a~b~c‘
 */
function join(array, separator) {
  return array == null ? ‘‘ : nativeJoin.call(array, separator);//如果數組為空,返回空字符串,否則調用原生的join方法,並將結果返回
}

module.exports = join;

_.last(array)

得到數組的最後一個元素

//last.js

/**
 *
 * @param {Array} array 需要處理的數組.
 * @returns {*} 返回數組的最後一個元素.
 * @example
 *
 * _.last([1, 2, 3]);
 * // => 3
 */
function last(array) {
  var length = array == null ? 0 : array.length;
  return length ? array[length - 1] : undefined;//不解釋
}

module.exports = last;

_.lastIndexOf(array, value, [fromIndex=array.length-1])

和_.indexOf很像,但是是從右到左開始遍歷

該方法依賴於strictLastIndexOf方法

//_strictLastIndexOf.js

/**
 * _.lastIndexOf的專業版本,對元素執行嚴格的比較方法(===).
 *
 * @param {Array} array 需要處理的數組.
 * @param {*} value 需要查找的值.
 * @param {number} fromIndex 查找的開始位置.
 * @returns {number} 返回第一個匹配的元素的索引,或者-1.
 */
function strictLastIndexOf(array, value, fromIndex) {
  var index = fromIndex + 1;//數值索引
  while (index--) {
    if (array[index] === value) {//遍歷,如果數組中的值和查找的值相同,返回對應的index
      return index;
    }
  }
  return index;//返回index(如果沒有找到,index的值為-1)
}

module.exports = strictLastIndexOf;

再看lastIndexOf方法的源碼

//lastIndexOf.js

var baseFindIndex = require(‘./_baseFindIndex‘),//baseFindIndex方法(見源碼學習(1))
    baseIsNaN = require(‘./_baseIsNaN‘),//判斷是否為NaN
    strictLastIndexOf = require(‘./_strictLastIndexOf‘),//strictLastIndexOf方法
    toInteger = require(‘./toInteger‘);//轉化為整型

var nativeMax = Math.max,//原生最大值方法
    nativeMin = Math.min;//原生最小值方法

/**
 *
 * @param {Array} array 需要處理的數組.
 * @param {*} value 需要查找的值.
 * @param {number} [fromIndex=array.length-1] 查找的開始位置.
 * @returns {number} 返回匹配的第一個元素的索引,或者-1.
 * @example
 *
 * _.lastIndexOf([1, 2, 1, 2], 2);
 * // => 3
 *
 * // Search from the `fromIndex`.
 * _.lastIndexOf([1, 2, 1, 2], 2, 2);
 * // => 1
 */
function lastIndexOf(array, value, fromIndex) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回-1
    return -1;
  }
  var index = length;//數組索引
  if (fromIndex !== undefined) {//如果有起始位置
    index = toInteger(fromIndex);//轉換為整形
    index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);//判斷起始位置,在0到length-1之間,如果為負,從末尾算起
  }
  return value === value//如果不是NaN,調用strictLastIndexOf方法,否則調用baseFindIndex方法,並且傳入判斷條件為baseIsNaN
    ? strictLastIndexOf(array, value, index)
    : baseFindIndex(array, baseIsNaN, index, true);
}

module.exports = lastIndexOf;

_.nth(array, [n=0])

得到數組指定索引的值,如果索引為負值,從數組末尾算起.

該方法依賴於baseNth方法

//_baseNth.js

var isIndex = require(‘./_isIndex‘);//是否為正確的索引

/**
 * _.nth的基本實現,不強制參數
 *
 * @private
 * @param {Array} array 需要查詢的數組
 * @param {number} n 查找元素的index.
 * @returns {*} 返回查找到的元素.
 */
function baseNth(array, n) {
  var length = array.length;//數組長度
  if (!length) {//如果為空數組,返回空數組
    return;
  }
  n += n < 0 ? length : 0;//n<0從末尾算,否則就是n
  return isIndex(n, length) ? array[n] : undefined;//如果是正確的索引,返回索引對應的值,否則返回undefined
}

module.exports = baseNth;

再看nth.js

//nth.js

var baseNth = require(‘./_baseNth‘),//baseNth方法
    toInteger = require(‘./toInteger‘);//轉化為整形

/**
 *
 * @param {Array} array 需要查詢的數組.
 * @param {number} [n=0] 查找元素的index..
 * @returns {*} 返回查找到的元素.
 * @example
 *
 * var array = [‘a‘, ‘b‘, ‘c‘, ‘d‘];
 *
 * _.nth(array, 1);
 * // => ‘b‘
 *
 * _.nth(array, -2);
 * // => ‘c‘;
 */
function nth(array, n) {
  return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;//不解釋
}

module.exports = nth;

_.pull(array, [values])

移除數組array中所有和 values 相等的元素,使用 SameValueZero 進行全等比較.

_.pullAll(array, values)

和_.pull很像除了它接受一個需要移除的元素的數組

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

這個方法和_.pullAll很像除了它接受一個遍歷方法被每個元素調用,遍歷方法接受一個參數(value)

_.pullAllWith(array, values, [comparator])

這4個方法都依賴於basePullAll方法

//_basePullAll.js

var arrayMap = require(‘./_arrayMap‘),//同Array.map
    baseIndexOf = require(‘./_baseIndexOf‘),//同Array.indexOf,上面已分析
    baseIndexOfWith = require(‘./_baseIndexOfWith‘),//類似baseIndexOf,除了接受一個比較方法
    baseUnary = require(‘./_baseUnary‘),//創建一個只有一個參數的方法,忽略其他的參數
    copyArray = require(‘./_copyArray‘);//拷貝數組

var arrayProto = Array.prototype;//數組原形

var splice = arrayProto.splice;//原生splice方法

/**
 * _.pullAllBy的基本實現,不支持遍歷器的簡寫
 *
 * @private
 * @param {Array} array 需要修改的數組.
 * @param {Array} values 需要移除的元素.
 * @param {Function} [iteratee] 遍歷器,對每個元素調用.
 * @param {Function} [comparator] 比較器,對每個元素調用.
 * @returns {Array} 返回修改之後的數組.
 */
function basePullAll(array, values, iteratee, comparator) {
  var indexOf = comparator ? baseIndexOfWith : baseIndexOf,//選擇indexOf方法,如果傳入比較器為baseIndexOfWith,否則為baseIndexOf
      index = -1,//數值索引
      length = values.length,//數組長度
      seen = array;//獲取一個array的引用

  if (array === values) {//如果需要移除的元素和需要修改的數組一樣,拷貝一份values
    values = copyArray(values);
  }
  if (iteratee) {//如過存在遍歷器,對數組進行遍歷操作
    seen = arrayMap(array, baseUnary(iteratee));
  }
  //遍歷需要移除的數組
  while (++index < length) {
    var fromIndex = 0,//元素在數組中的開始位置
        value = values[index],//需要移除的元素
        computed = iteratee ? iteratee(value) : value;//如果有遍歷器對value進行調用,得到 計算後的value(也就是computed)
    //循環執行indexOf方法,如果seen中,直到找不到computed為止
    while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
      if (seen !== array) {//seen和array不相等即存在遍歷器,對seen執行splice方法移除fromIndex的元素
        splice.call(seen, fromIndex, 1);
      }
      //對array執行splice方法移除fromIndex的元素
      splice.call(array, fromIndex, 1);
    }
  }
  return array;//返回修改後的array
}

module.exports = basePullAll;

相對應的方法的源碼

//pullAll.js

var basePullAll = require(‘./_basePullAll‘);//basePullAll方法

/**
 *
 * @param {Array} array 需要修改的數組.
 * @param {Array} values 需要移除的值.
 * @returns {Array} 返回修改後的數組.
 * @example
 *
 * var array = [‘a‘, ‘b‘, ‘c‘, ‘a‘, ‘b‘, ‘c‘];
 *
 * _.pullAll(array, [‘a‘, ‘c‘]);
 * console.log(array);
 * // => [‘b‘, ‘b‘]
 */
function pullAll(array, values) {
  return (array && array.length && values && values.length)
    ? basePullAll(array, values)
    : array;//不解釋
}

module.exports = pullAll;
//pull.js

var baseRest = require(‘./_baseRest‘),//創建具有rest參數的方法
    pullAll = require(‘./pullAll‘);

/**
 *
 * @param {Array} array 需要修改的數組.
 * @param {...*} [values] The values to remove.
 * @returns {Array} Returns `array`.
 * @example
 *
 * var array = [‘a‘, ‘b‘, ‘c‘, ‘a‘, ‘b‘, ‘c‘];
 *
 * _.pull(array, ‘a‘, ‘c‘);
 * console.log(array);
 * // => [‘b‘, ‘b‘]
 */
var pull = baseRest(pullAll);//將pullAll轉換成可以使用rest參數的方法(pull(array,...values))

module.exports = pull;
//pullAllBy.js

var baseIteratee = require(‘./_baseIteratee‘),//封裝遍歷器
    basePullAll = require(‘./_basePullAll‘);//basePullAll方法

/**
 *
 * @param {Array} array 需要修改的數組.
 * @param {Array} values 需要移除的值.
 * @param {Function} [iteratee=_.identity] 遍歷器,被每個元素調用.
 * @returns {Array} 返回修改後的數組.
 * @example
 *
 * var array = [{ ‘x‘: 1 }, { ‘x‘: 2 }, { ‘x‘: 3 }, { ‘x‘: 1 }];
 *
 * _.pullAllBy(array, [{ ‘x‘: 1 }, { ‘x‘: 3 }], ‘x‘);
 * console.log(array);
 * // => [{ ‘x‘: 2 }]
 */
function pullAllBy(array, values, iteratee) {
  return (array && array.length && values && values.length)
    ? basePullAll(array, values, baseIteratee(iteratee, 2))
    : array;//不解釋
}

module.exports = pullAllBy;
//pullWidth.js

var basePullAll = require(‘./_basePullAll‘);//basePullAll方法

/**
 *
 * @param {Array} array 需要修改的數組.
 * @param {Array} values 需要移除的元素.
 * @param {Function} [comparator] 比較器被每個元素調用.
 * @returns {Array} 返回修改後的數組.
 * @example
 *
 * var array = [{ ‘x‘: 1, ‘y‘: 2 }, { ‘x‘: 3, ‘y‘: 4 }, { ‘x‘: 5, ‘y‘: 6 }];
 *
 * _.pullAllWith(array, [{ ‘x‘: 3, ‘y‘: 4 }], _.isEqual);
 * console.log(array);
 * // => [{ ‘x‘: 1, ‘y‘: 2 }, { ‘x‘: 5, ‘y‘: 6 }]
 */
function pullAllWith(array, values, comparator) {
  return (array && array.length && values && values.length)
    ? basePullAll(array, values, undefined, comparator)
    : array;//不解釋
}

module.exports = pullAllWith;

一天一點進步,一步一個腳印~~~

lodash源碼學習(2)