lodash源码学习(1)

2019年12月11日 阅读数:27
这篇文章主要向大家介绍lodash源码学习(1),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

  前端开发这个行业这几年发展速度太快,各类新技术不断更新,从es5到es6再到es7,从grunt,browserify到webpack,gulp,rollup,还有什么postcss,typescript,flow...,一直都在学习新技术,做为一个才工做不久的新人,感受心里有点浮躁了,想巩固一下基础,以前听别人说lodash的源码很不错,因此学习学习。我不是什么大牛,若是有什么分析得不对的,你们请务必要原谅我。。。。话很少说,lodash版本4.17.4,开始!。css

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)

建立一个新数组并包含原数组中全部的非假值元素。webpack

//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])

建立一个新数组包含原来的数组和全部添加的元素和数组es6

//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])

建立一个新数组,其结果为原数组中不包含过滤数组中的值。web

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

_.difference很像,除了他接受一个遍历函数被每一个原数组和过滤数组中的值调用,调用以后再进行过滤。typescript

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

_.difference很像,除了他接受一个比较方法被每一个原数组和过滤数组中的值调用,比较方法接受两个参数(arrVal原数组的值,othVal过滤数组的值),比较结果为true则过滤掉。gulp

这三个方法都依赖于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 个元素去掉,而后返回剩余的部分ide

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

先到这里了,下次继续,慢慢来,一步一个脚印~~~