lodash源碼學習(1)


  前端開發這個行業這幾年發展速度太快,各種新技術不斷更新,從es5到es6再到es7,從grunt,browserify到webpack,gulp,rollup,還有什么postcss,typescript,flow...,一直都在學習新技術,作為一個才工作不久的新人,感覺內心有點浮躁了,想鞏固一下基礎,之前聽別人說lodash的源碼很不錯,所以學習學習。我不是什么大牛,如果有什么分析得不對的,大家請務必要原諒我。。。。話不多說,lodash版本4.17.4,開始!。

1.“Array” Methods

_.chunk(array, [size=1])

將一個數組拆分成多個數組的塊,然后把這些塊組成新的數組

//chunk.js

//同Array.slice方法
var baseSlice = require('./_baseSlice'), 
    //是否是一個遍歷方法的參數,也就是需要和Array.map的參數一樣,第一個是值,第二個是索引,第三個是對象本身
    isIterateeCall = require('./_isIterateeCall'), 
    //轉化成整型
    toInteger = require('./toInteger');

var nativeCeil = Math.ceil,//原生上舍入方法
    nativeMax = Math.max;//原生最大值方法


/**
 * @param {Array} array 需要處理的數組
 * @param {Number} size 每個數組塊的長度
 * @param {Object} guard 讓chunk方法可以作為一個遍歷方法,比如作為Array.map的參數(不知道有什么用) 
 * @returns {Array} 返回處理后的數組 
 * @example
 *
 * _.chunk(['a', 'b', 'c', 'd'], 2);
 * // => [['a', 'b'], ['c', 'd']]
 *
 * _.chunk(['a', 'b', 'c', 'd'], 3);
 * // => [['a', 'b', 'c'], ['d']]
 * 
 */
function chunk(array, size, guard) {

  //判斷是否傳入guard,如果傳入,判斷是否是遍歷方法的參數,如果是size=1,否則為傳入size和0的最大值
  //如果沒傳,判斷是否傳入size,如果沒傳,size=1,否則為傳入size和0的最大值
  if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
    size = 1;
  } else {
    size = nativeMax(toInteger(size), 0);
  }
  var length = array == null ? 0 : array.length;//數組長度
  if (!length || size < 1) {  //如果為空,或者傳入負的size,返回空數組
    return [];
  }
  var index = 0,//切數組塊的的起始位置
      resIndex = 0,//返回的數組的索引
      result = Array(nativeCeil(length / size));//返回的數組
  //循環,每次向result添加一個size的數組塊,並且將index向后移size個位置,直到index到了原始數組的末尾
  while (index < length) {
    result[resIndex++] = baseSlice(array, index, (index += size));
  }
  return result;//返回切好的數組
}

module.exports = chunk;

 _.compact(array)

創建一個新數組並包含原數組中所有的非假值元素。

//compact.js

/**
 * @param {Array} array 需要處理的數組
 * @returns {Array} 返回處理后的數組
 * @example
 *
 *_.compact([0, 1, false, 2, '', 3]);
 * // => [1, 2, 3]  
 */
function compact(array) {
  var index = -1,//數組索引
      length = array == null ? 0 : array.length,//數組長度
      resIndex = 0,//結果數組索引
      result = [];//結果數組
     
  while (++index < length) {//遍歷原數組
    var value = array[index];
    if (value) {//如果是真值,就將它加入到結果數組中,並且讓resIndex加1
      result[resIndex++] = value;
    }
  }
  return result;//返回結果數組
}

module.exports = compact; 

_.concat(array, [values])

創建一個新數組包含原來的數組和所有添加的元素和數組

//concat.js

var arrayPush = require('./_arrayPush'),//同Array.push方法,第一個參數是原數組,第二個是需要添加的值得數組集合
    baseFlatten = require('./_baseFlatten'),//數組扁平化,后面再分析,比如[1,[2,3],[4,5,[6]]] => [1,2,3,4,5,6]
    copyArray = require('./_copyArray'),//拷貝數組
    isArray = require('./isArray');//Array.isArray方法的引用。

/**
 * @param {Array} array 需要處理的數組
 * @param {...*} [values] 需要添加的元素或數組
 * @returns {Array} 返回處理后的數組
 * @example
 *
 * var array = [1];
 * var other = _.concat(array, 2, [3], [[4]]);
 *
 * console.log(other);
 * // => [1, 2, 3, [4]]
 *
 * console.log(array);
 * // => [1]  
 */
function concat() {
  var length = arguments.length;//參數個數
  if (!length) {//沒有參數,返回空數組
    return [];
  }
  var args = Array(length - 1), //包含需要添加的數組或元素的數組
      array = arguments[0],//原數組
      index = length;//參數索引

  while (index--) {//遍歷參數,將除了第一個參數的其他參數加入args中
    args[index - 1] = arguments[index];
  }
  //如果第一個參數是數組,先復制一份(這樣就不會修改原數組),然后將args扁平化一級([1,[2,[3]]] => [1,2,[3]])之后添加進拷貝的數組中,並返回添加之后的數組
  //如果第一個參數不是數組,直接將其作為空數組的第一個元素([array]),然后將args扁平化一級([1,[2,[3]]] => [1,2,[3]])之后添加進該數組,並返回添加之后的數組
  return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
}

module.exports = concat;

_.difference(array, [values])

創建一個新數組,其結果為原數組中不包含過濾數組中的值。

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

_.difference很像,除了他接受一個遍歷函數被每個原數組和過濾數組中的值調用,調用之后再進行過濾。

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

_.difference很像,除了他接受一個比較方法被每個原數組和過濾數組中的值調用,比較方法接受兩個參數(arrVal原數組的值,othVal過濾數組的值),比較結果為true則過濾掉。

這三個方法都依賴於baseDifference方法,先看源碼。

//_baseDifference.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 LARGE_ARRAY_SIZE = 200;//數組最大長度

/**
 * @param {Array} array 需要處理的數組.
 * @param {Array} values 需要過濾的數組.
 * @param {Function} [iteratee] 遍歷器,被每個元素調用.
 * @param {Function} [comparator] 比較器,被每個元素調用.
 * @returns {Array} 返回過濾后的數組.
 */
function baseDifference(array, values, iteratee, comparator) {
  var index = -1,//原數組索引
      includes = arrayIncludes,//引用arrayIncludes
      isCommon = true,//是否正常過濾
      length = array.length,//數組長度
      result = [],//返回結構
      valuesLength = values.length;//過濾數組長度

  if (!length) {//如果原數組為空或者空數組,返回空數組
    return result;
  }
  if (iteratee) {//如果有遍歷器,先對過濾數組進行遍歷操作
    values = arrayMap(values, baseUnary(iteratee));
  }
  if (comparator) {//如果有比較器,includes就引用arrayIncludesWith,並且不正常過濾
    includes = arrayIncludesWith;
    isCommon = false;
  }
  else if (values.length >= LARGE_ARRAY_SIZE) {//如果過濾數組的長度大於最大數組長度
    includes = cacheHas;//includes引用cacheHas
    isCommon = false;//不正常過濾
    values = new SetCache(values);//過濾數組等於緩存之后的數組(用於優化,暫時不分析)
  }
  //遍歷原數組
  outer:
  while (++index < length) {
    var value = array[index],//每次遍歷的原數組中的元素
        computed = iteratee == null ? value : iteratee(value);//如果有遍歷器,對該元素調用一次,得到計算后的cumputed,否則computed和value一樣

    value = (comparator || value !== 0) ? value : 0;
    if (isCommon && computed === computed) {//正常過濾並且computed不為NaN
      var valuesIndex = valuesLength;//過濾數組的索引
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {//如果這個元素在過濾數組中存在,跳過,
          continue outer;
        }
      }
      result.push(value);//如果不存在,添加添加進結果數組中
    }
    else if (!includes(values, computed, comparator)) {//非正常過濾,調用includes方法,如果通過比較器的規則不包含,將該元素添加進結果數組
      result.push(value);
    }
  }
  return result;//返回過濾后的數組
}

module.exports = baseDifference;

相對應的方法都是在baseDiffrerece的基礎上進行擴展的

//difference.js

var baseDifference = require('./_baseDifference'),//baseDifference方法
    baseFlatten = require('./_baseFlatten'),//數組扁平化
    baseRest = require('./_baseRest'),//創建可以使用rest參數的方法
    isArrayLikeObject = require('./isArrayLikeObject');//是否是一個類似數組的對象

/**
 *
 * @param {Array} 需要處理的數組.
 * @param {...Array} [values] 需要過濾的值.
 * @returns {Array} 返回過濾后的數組.
 * @example
 *
 * _.difference([2, 1], [2, 3]);
 * // => [1]
 */
var difference = baseRest(function(array, values) {//創建一個具備rest參數的方法
  //如果array是一個類似數組的對象,調用baseDifference方法,並且將所有過濾數組扁平化一級,比如difference(arr,[1,2],[3,4]) => baseDifference(arr,[1,2,3,4])
  //如果不是,返回一個空數組
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
    : [];
});

module.exports = difference;
//differenceBy.js

var baseDifference = require('./_baseDifference'),//baseDifference方法
    baseFlatten = require('./_baseFlatten'),//數組扁平化
    baseIteratee = require('./_baseIteratee'),//封裝遍歷器(讓遍歷器不僅可以是函數,還可以是屬性或者對象)
    baseRest = require('./_baseRest'),//創建可以使用rest參數的方法
    isArrayLikeObject = require('./isArrayLikeObject'),//是否是一個類似數組的對象
    last = require('./last');//得到數組的最后一個元素

/**
 * @param {Array} 需要處理的數組.
 * @param {...Array} [values] 需要過濾的值.
 * @param {Function} [iteratee=_.identity] 遍歷器,對每個元素進行調用.
 * @returns {Array} 返回過濾后的數組.
* @example
*
*_.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
*// => [1.2]
*
* 遍歷器簡寫(寫屬性值)
*_.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
*// => [{ 'x': 2 }]
*/ var differenceBy = baseRest(function(array, values) {//創建一個具備rest參數的方法 var iteratee = last(values);//遍歷器為values的最后一個參數 if (isArrayLikeObject(iteratee)) {//如果這個參數是類似數組的對象,遍歷器為undefined(也就是並沒有傳入遍歷器) iteratee = undefined; } //如果array是類似數組的對象,調用baseDifference,並且將所有過濾數組扁平化一級,再傳入創建的遍歷器 //如果不是,返回一個空數組 return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), baseIteratee(iteratee, 2)) : []; }); module.exports = differenceBy;
//differenceWith.js

var baseDifference = require('./_baseDifference'),//同上
    baseFlatten = require('./_baseFlatten'),
    baseRest = require('./_baseRest'),
    isArrayLikeObject = require('./isArrayLikeObject'),
    last = require('./last');

/**
 * @param {Array} array 需要處理的數組.
 * @param {...Array} [values] 需要過濾的值.
 * @param {Function} [comparator] 比較器,對每個元素進行調用.
 * @returns {Array} 返回過濾后的數組.
 * @example
 *
 * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
 *
 * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
 * // => [{ 'x': 2, 'y': 1 }]
 */
var differenceWith = baseRest(function(array, values) {//創建一個具備rest參數的方法
  var comparator = last(values);//比較器為values的最后一個參數
  if (isArrayLikeObject(comparator)) {//如果這個參數是類似數組的對象,比較器為undefined(也就是並沒有傳入比較器)
    comparator = undefined;
  }
  //如果array是類似數組的對象,調用baseDifference,並且將所有過濾數組扁平化一級,並且不傳遍歷器,再傳入比較器
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)
    : [];
});

module.exports = differenceWith;

_.drop(array, [n=1])

將 array 中的前 n 個元素去掉,然后返回剩余的部分

//drop.js

var baseSlice = require('./_baseSlice'),//同Array.slice
    toInteger = require('./toInteger');//轉化為整型

/**
 * 
 * @param {Array} array 需要處理的數組.
 * @param {number} [n=1] 需要去掉的個數.
 * @param- {Object} [guard] 使這個方法能夠作為遍歷器被調用,比如_.map(drop).
 * @returns {Array} 返回處理后的數組.
 * @example
 *
 * _.drop([1, 2, 3]);
 * // => [2, 3]
 *
 * _.drop([1, 2, 3], 2);
 * // => [3]
 *
 * _.drop([1, 2, 3], 5);
 * // => []
 *
 * _.drop([1, 2, 3], 0);
 * // => [1, 2, 3]
 */
function drop(array, n, guard) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回空數組
    return [];
  }
  n = (guard || n === undefined) ? 1 : toInteger(n);//將n轉為整型
  return baseSlice(array, n < 0 ? 0 : n, length);//調用baseSlice對數組從n位置進行切割,並返回切好的數組
}

module.exports = drop;

_.dropRight(array, [n=1])

將 array 尾部的 n 個元素去除,並返回剩余的部分。

//dropRight.js

var baseSlice = require('./_baseSlice'),//同Array.slice
    toInteger = require('./toInteger');//轉化為整型

/**
 * 
 * @param {Array} array 需要處理的數組.
 * @param {number} [n=1] 需要去掉的個數.
 * @param- {Object} [guard] 使這個方法能夠作為遍歷器被調用,比如_.map(dropRight).
 * @returns {Array} 返回處理后的數組.
 * @example
 *
 * _.dropRight([1, 2, 3]);
 * // => [1, 2]
 *
 * _.dropRight([1, 2, 3], 2);
 * // => [1]
 *
 * _.dropRight([1, 2, 3], 5);
 * // => []
 *
 * _.dropRight([1, 2, 3], 0);
 * // => [1, 2, 3]
 */
function dropRight(array, n, guard) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回空數組
    return [];
  }
  n = (guard || n === undefined) ? 1 : toInteger(n);//將n轉為整型
  n = length - n;//剩下的個數
  return baseSlice(array, 0, n < 0 ? 0 : n);//調用baseSlice對數組從0位置進行切割n個,並返回切好的數組
}

module.exports = dropRight;

_.dropRightWhile(array, [predicate=_.identity])

 從末尾對數組進行截取,從第一個不滿足predicate 條件的元素開始截取數組。predicate接受三個參數(value,index,array)

_.dropWhile(array, [predicate=_.identity])

從開始對數組進行截取,從第一個不滿足predicate 條件的元素開始截取數組。predicate接受三個參數(value,index,array)

這兩個方法依賴於baseWhile方法,先看源碼

//_baseWhile.js

var baseSlice = require('./_baseSlice');//同Array.slice

/**
 *_.dropWhile和_.takeWhile的基本實現,但是不支持遍歷器的簡寫(不能直接寫一個屬性或對象進行遍歷)
 *
 * @param {Array} array 需要處理的數組.
 * @param {Function} predicate 迭代判斷條件.
 * @param {boolean} [isDrop] 指定是移除還是獲取這些元素.
 * @param {boolean} [fromRight] 指定從開始還是末尾開始判斷.
 * @returns {Array} 返回處理后的數組.
 */
function baseWhile(array, predicate, isDrop, fromRight) {
  var length = array.length,//數組長度
      index = fromRight ? length : -1;//數組索引,如果是從末尾判斷,則為length,否則為0
  //遍歷每個元素,並且調用判斷方法,直到判斷結果為false,然后得到此時的index
  while ((fromRight ? index-- : ++index < length) &&
    predicate(array[index], index, array)) {}
  
  //如果是刪除元素,如果從末尾開始,調用baseSlice(0,index+1),否則調用baseSlice(index,length)
  //如果是獲取元素,如果從末尾開始,調用baseSlice(index+1,length),否則調用baseSlice(0,index)
  //最后返回切好的數組
  return isDrop
    ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
    : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));
}

module.exports = baseWhile;

相對應的方法都是在baseWhile的基礎上進行擴展的

//dropRightWhile.js

var baseIteratee = require('./_baseIteratee'),//封裝遍歷器
    baseWhile = require('./_baseWhile');//baseWhile方法

/**
 * 
 * @param {Array} array 需要處理的數組.
 * @param {Function} [predicate=_.identity] 迭代判斷條件.
 * @returns {Array} 返回切好的數組.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'active': true },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': false }
 * ];
 *
 * _.dropRightWhile(users, function(o) { return !o.active; });
 * // => objects for ['barney']
 */
function dropRightWhile(array, predicate) {
  //如果array存在,並且不為空數組,調用baseWhile方法,傳入該數組,遍歷器,然后指定從右邊開始移除
  //否則返回空數組
  return (array && array.length)
    ? baseWhile(array, baseIteratee(predicate, 3), true, true)
    : [];
}

module.exports = dropRightWhile;
//dropWhile.js

var baseIteratee = require('./_baseIteratee'),//封裝遍歷器
    baseWhile = require('./_baseWhile');//baseWhile方法

/**
 * 
 * @param {Array} array 需要處理的數組.
 * @param {Function} [predicate=_.identity] 迭代判斷條件.
 * @returns {Array} 返回切好的數組.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'active': false },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': true }
 * ];
 *
 * _.dropWhile(users, function(o) { return !o.active; });
 * // => objects for ['pebbles']
 */

function dropWhile(array, predicate) {
  //如果array存在,並且不為空數組,調用baseWhile方法,傳入該數組,遍歷器,然后指定從左邊開始移除
  //否則返回空數組
  return (array && array.length)
    ? baseWhile(array, baseIteratee(predicate, 3), true)
    : [];
}

module.exports = dropWhile;

_.fill(array, value, [start=0], [end=array.length])

使用 value 值來填充(也就是替換) array,從start位置開始, 到end位置結束(但不包含end位置)

此方法依賴於baseFill方法,先看源碼

//_baseFill.js

var toInteger = require('./toInteger'),//轉換為整型
    toLength = require('./toLength');//轉換為可以使用的length

/**
 * 使用 value 值來填充(也就是替換) array,從start位置開始, 到end位置結束(但不包含end位置) 
 *
 * @private
 * @param {Array} array 需要處理的數組.
 * @param {*} value 填充的值.
 * @param {number} [start=0] 填充的開始位置
 * @param {number} [end=array.length] 填充的結束位置.
 * @returns {Array} 返回處理后的數組.
 */
function baseFill(array, value, start, end) {
  var length = array.length;//數組長度

  start = toInteger(start);//轉換為整型
  if (start < 0) {//如果start為負值,從末尾開始算,-1就是最后一個元素,依次計算,如果最后大於數組的長度,就是0
    start = -start > length ? 0 : (length + start);
  }
  //對end進行判斷,取得合適的end
  end = (end === undefined || end > length) ? length : toInteger(end);
  if (end < 0) {//如果start為負值,從末尾開始算,-1就是最后一個元素,依次計算,如果最后大於數組的長度,就是0
    end += length;
  }
  //如果start大於end,end等於0,否則將end轉為可用的length
  end = start > end ? 0 : toLength(end);
  //循環,每次start+1直到start=end
  while (start < end) {
    array[start++] = value;//將對應索引的元素替換為value
  }
  return array;//返回該數組
}

module.exports = baseFill;

再看fill.js

//fill.js


var baseFill = require('./_baseFill'),//baseFill方法
    isIterateeCall = require('./_isIterateeCall');//判斷是否為遍歷器的參數(value,index,array)

/** 
 *
 * @param {Array} array 需要處理的數組.
 * @param {*} value 填充的值.
 * @param {number} [start=0] 填充的開始位置
 * @param {number} [end=array.length] 填充的結束位置.
 * @returns {Array} 返回處理后的數組.
 * @example
 *
 * var array = [1, 2, 3];
 *
 * _.fill(array, 'a');
 * console.log(array);
 * // => ['a', 'a', 'a']
 *
 * _.fill(Array(3), 2);
 * // => [2, 2, 2]
 *
 * _.fill([4, 6, 8, 10], '*', 1, 3);
 * // => [4, '*', '*', 10]
 */
function fill(array, value, start, end) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回空數組
    return [];
  }
  //如果start存在且不為number且為遍歷器,那么start=0,end=length(也就是說將fill作為參數傳入map之類的方法,暫時不知道有什么用)
  if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
    start = 0;
    end = length;
  }
  return baseFill(array, value, start, end); //調用baseFill並且將結果數組返回
}

module.exports = fill;

_.findIndex(array, [predicate=_.identity], [fromIndex=0])

對數組從開始進行查找,該方法返回符合判斷條件的第一個元素的索引

_.findLastIndex(array, [predicate=_.identity], [fromIndex=array.length-1])

對數組從末尾進行查找,該方法返回符合判斷條件的第一個元素的索引

這兩個方法依賴於baseFindIndex方法,先看源碼

//_baseFindIndex.js

/**
 *_.findIndex和_.findLastIndex的基本實現,但是不支持遍歷方法的簡寫(不能直接寫一個屬性或對象進行遍歷)
 *
 * @private
 * @param {Array} array 需要處理的數組.
 * @param {Function} predicate 判斷方法,對每個元素調用.
 * @param {number} fromIndex 開始查找的位置.
 * @param {boolean} [fromRight] 指定從開始還是末尾開始搜索.
 * @returns {number} 返回匹配的值的索引,否則返回-1.
 */
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  var length = array.length,//數組長度
      index = fromIndex + (fromRight ? 1 : -1);//數組索引
  //循環,對每個元素調用判斷方法,如果結果為true,返回對應的index
  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index;
    }
  }
  return -1;//返回-1
}

module.exports = baseFindIndex;

相對應的方法都是在baseFindIndex的基礎上進行擴展的

//findIndex.js

var baseFindIndex = require('./_baseFindIndex'),//baseFindIndex方法
    baseIteratee = require('./_baseIteratee'),//封裝遍歷器(讓遍歷器不僅可以是函數,還可以是屬性或者對象)
    toInteger = require('./toInteger');//轉換為整型

var nativeMax = Math.max;//原生最大值方法 /**
 *
 * @param {Array} array 需要處理的數組.
 * @param {Function} [predicate=_.identity] 遍歷器,對每個元素調用.
 * @param {number} [fromIndex=0] 開始查找的位置.
 * @returns {number} 返回匹配的值的索引,否則返回-1.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'active': false },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': true }
 * ];
 *
 * _.findIndex(users, function(o) { return o.user == 'barney'; });
 * // => 0
 */
function findIndex(array, predicate, fromIndex) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回-1
    return -1;
  }
  var index = fromIndex == null ? 0 : toInteger(fromIndex);//開始查找的索引
  if (index < 0) {//如果index為負值,從末尾開始算,-1就是最后一個元素,依次計算,如果最后大於數組的長度,就是0
    index = nativeMax(length + index, 0);
  }
  //調用baseFindIndex,並且將遍歷方法封裝,然后將結果作為返回值返回
  return baseFindIndex(array, baseIteratee(predicate, 3), index);
}

module.exports = findIndex;
//findLastIndex.js

var baseFindIndex = require('./_baseFindIndex'),//baseFindIndex方法
    baseIteratee = require('./_baseIteratee'),//封裝遍歷器(讓遍歷器不僅可以是函數,還可以是屬性或者對象)
    toInteger = require('./toInteger');//轉換為整型

var nativeMax = Math.max,
    nativeMin = Math.min;

/**
 *
 * @param {Array} array 需要處理的數組.
 * @param {Function} [predicate=_.identity] 遍歷器,對每個元素調用.
 * @param {number} [fromIndex=0] 開始查找的位置.
 * @returns {number} 返回匹配的值的索引,否則返回-1.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'active': true },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': false }
 * ];
 *
 * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
 * // => 2
 */
function findLastIndex(array, predicate, fromIndex) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回-1
    return -1;
  }
  var index = length - 1;//開始查找的索引
  if (fromIndex !== undefined) {//如果有fromIndex,進行比較,得到正確的index(負值反向且不能超過數組長度)
    index = toInteger(fromIndex);
    index = fromIndex < 0
      ? nativeMax(length + index, 0)
      : nativeMin(index, length - 1);
  }
  //調用baseFindIndex方法,並且封裝遍歷器,並且指定從右邊查找,最后將結果作為返回值返回
  return baseFindIndex(array, baseIteratee(predicate, 3), index, true);
}

module.exports = findLastIndex;

 

_.find(collection, [predicate=_.identity], [fromIndex=0])

從開始對集合進行查找,該方法返回符合判斷條件的第一個元素

_.findLast(collection, [predicate=_.identity], [fromIndex=0])

從末尾對集合進行查找,該方法返回符合判斷條件的第一個元素

這兩個方法是屬於集合的方法,依賴於createFind方法和上面的findIndex和findLastIndex方法,所以就在這里一起分析了,先看createFind的源碼

//_createFind.js

var baseIteratee = require('./_baseIteratee'),
    isArrayLike = require('./isArrayLike'),
    keys = require('./keys');

/**
 * 創建一個_.find或者_.findLast方法
 *
 * @private
 * @param {Function} findIndexFunc 查找集合index的方法.
 * @returns {Function} 返回新的查找方法.
 */
function createFind(findIndexFunc) {
  return function(collection, predicate, fromIndex) {
    var iterable = Object(collection);//將集合轉為對象
    if (!isArrayLike(collection)) {//如果不是類似數組的對象(就是常規的對象)
      var iteratee = baseIteratee(predicate, 3);
      collection = keys(collection);//集合為這個對象的key的集合
      predicate = function(key) { return iteratee(iterable[key], key, iterable); };//重寫判斷方法
    }
    var index = findIndexFunc(collection, predicate, fromIndex);//調用findIndexFunc方法,取得索引
    //如果index>-1 就取得這個索引對應的值,否則為undefined,並且將結果返回
    return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
  };
}

module.exports = createFind;

find方法相當於是調用createFind傳入findIndex,lastFind方法相當於是調用createFind傳入findLastIndex

//find.js


var createFind = require('./_createFind'),//createFind方法
    findIndex = require('./findIndex');//findIndex方法

/**
 * 
 * @param {Array} array 需要處理的數組.
 * @param {Function} [predicate=_.identity] 遍歷器,對每個元素調用.
 * @param {number} [fromIndex=0] 開始查找的位置.
 * @returns {number} 返回匹配的值,或者undefined.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'age': 36, 'active': true },
 *   { 'user': 'fred',    'age': 40, 'active': false },
 *   { 'user': 'pebbles', 'age': 1,  'active': true }
 * ];
 *
 * _.find(users, function(o) { return o.age < 40; });
 * // => object for 'barney'
 */
var find = createFind(findIndex);//創建find方法

module.exports = find;
//findLast.js

var createFind = require('./_createFind'),//createFind方法
    findLastIndex = require('./findLastIndex');//findIndex方法

/**
 * 
 * @param {Array} array 需要處理的數組.
 * @param {Function} [predicate=_.identity] 遍歷器,對每個元素調用.
 * @param {number} [fromIndex=0] 開始查找的位置.
 * @returns {number} 返回匹配的值,或者undefined.
 * @example
 *
 * _.findLast([1, 2, 3, 4], function(n) {
 *   return n % 2 == 1;
 * });
 * // => 3
 */
var findLast = createFind(findLastIndex);//創建find方法

module.exports = findLast;

_.head(array)/_.first(array)

得到數組的第一個元素

//head.js

/**
 *
 * @param {Array} array 需要處理的數組.
 * @returns {*} Returns 數組的第一個元素.
 * @example
 *
 * _.head([1, 2, 3]);
 * // => 1
 *
 * _.head([]);
 * // => undefined
 */
function head(array) {
  return (array && array.length) ? array[0] : undefined;//不解釋
}

module.exports = head;
//first.js

module.exports = require('./head');

_.flatten(array)

對數組執行扁平化一級操作

_.flattenDeep(array)

對數組遞歸的執行扁平化操作.

_.flattenDepth(array, [depth=1])

對數組執行扁平化depth級操作.

數組扁平化的幾種方法依賴於baseFlatten方法,先看源碼

//_baseFlatten.js

var arrayPush = require('./_arrayPush'),//同Array.push
    isFlattenable = require('./_isFlattenable');//是否可以扁平化

/**
 * _.flatten的基本實現,支持是否限制扁平化操作
 *
 * @param {Array} array 需要處理的數組.
 * @param {number} depth 扁平化的深度.
 * @param {boolean} [predicate=isFlattenable] 判斷是否執行扁平化操作,對每個元素進行調用.
 * @param {boolean} [isStrict] 是否遵守predicate的檢查.
 * @param {Array} [result=[]] 初始化結果值.
 * @returns {Array} 返回扁平化之后的數組.
 */
function baseFlatten(array, depth, predicate, isStrict, result) {
  var index = -1,//數組索引
      length = array.length;//數組長度

  predicate || (predicate = isFlattenable);//如果沒有傳入判斷方法,這判斷方法為isFlattenable(只有可以執行扁平化就執行)
  result || (result = []);//如果沒有傳入的初始的結果數組,則結果為空數組
  //遍歷數組中的每個元素
  while (++index < length) {
    var value = array[index];//元素值
    if (depth > 0 && predicate(value)) {//如果深度大於0並且通過了檢查
      if (depth > 1) {//如果深度大於1(還需要扁平化),遞歸調用自身,並且depth-1,否則將這個元素值添加到結果數組
        baseFlatten(value, depth - 1, predicate, isStrict, result);
      } else {
        arrayPush(result, value);
      }
    } else if (!isStrict) {//如果不需要遵守判斷規則,直接將value添加到結果中
      result[result.length] = value;
    }
  }
  return result;//返回結果數組
}

module.exports = baseFlatten;

相對應的方法都是在baseFlatten的基礎上擴展的

//flatten.js

var baseFlatten = require('./_baseFlatten');//baseFlatten方法

/**
 *
 * @param {Array} array 需要處理的數組.
 * @returns {Array} Returns 扁平化之后數組.
 * @example
 *
 * _.flatten([1, [2, [3, [4]], 5]]);
 * // => [1, 2, [3, [4]], 5]
 */
function flatten(array) {
  var length = array == null ? 0 : array.length;//數組長度
  return length ? baseFlatten(array, 1) : [];//如果不是空數組,調用baseFlatten(只扁平化一級),並將結果返回,否則,返回空數組
}

module.exports = flatten;
//flattenDeep.js

var baseFlatten = require('./_baseFlatten');//baseFlatten方法

var INFINITY = 1 / 0;//無限大

/**
 *
 * @param {Array} array 需要處理的數組.
 * @returns {Array} 返回扁平化之后的數組.
 * @example
 *
 * _.flattenDeep([1, [2, [3, [4]], 5]]);
 * // => [1, 2, 3, 4, 5]
 */
function flattenDeep(array) {
  var length = array == null ? 0 : array.length;//數組長度
  return length ? baseFlatten(array, INFINITY) : [];//如果不是空數組,調用baseFlatten(遞歸調用,直到不能扁平化為止),並將結果返回,否則,返回空數組
}

module.exports = flattenDeep;
//flattenDepth.js

var baseFlatten = require('./_baseFlatten'),//baseFlatten方法
    toInteger = require('./toInteger');//轉化為整型

/**
 * @param {Array} array 需要處理的數組.
 * @param {number} [depth=1] 扁平化的深度.
 * @returns {Array} 返回扁平化之后的數組.
 * @example
 *
 * var array = [1, [2, [3, [4]], 5]];
 *
 * _.flattenDepth(array, 1);
 * // => [1, 2, [3, [4]], 5]
 *
 * _.flattenDepth(array, 2);
 * // => [1, 2, 3, [4], 5]
 */
function flattenDepth(array, depth) {
  var length = array == null ? 0 : array.length;//數組長度
  if (!length) {//如果為空數組,返回空數組
    return [];
  }
  depth = depth === undefined ? 1 : toInteger(depth);//扁平化的深度
  return baseFlatten(array, depth);//遞歸調用depth次baseFlatten,然后將結果作為返回值返回。
}

module.exports = flattenDepth;

_.fromPairs(pairs)

將鍵值對的數組轉化為鍵值對的對象

//fromPairs.js
/**
 * 
 * @param {Array} pairs 鍵值對的數組.
 * @returns {Object} 返回新的對象.
 * @example
 *
 * _.fromPairs([['a', 1], ['b', 2]]);
 * // => { 'a': 1, 'b': 2 }
 */
function fromPairs(pairs) {
  var index = -1,//數組索引
      length = pairs == null ? 0 : pairs.length,//數組長度
      result = {};//結果對象
  //遍歷數組中的值
  while (++index < length) {
    var pair = pairs[index];//鍵值對的值
    result[pair[0]] = pair[1];//將第一個作為鍵,第二個作為值添加到結果對象中
  }
  return result;//返回結果對象
}

module.exports = fromPairs;

先到這里了,下次繼續,慢慢來,一步一個腳印~~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM