lodash源码学习

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lodash源码学习相关的知识,希望对你有一定的参考价值。

“Array” Methods

_.pullAt(array, [indexes])

移除数组中在indexs中对应索引的元素,并返回这些元素

这个方法依赖于basePullAt方法

//_basePullAt.js

var baseUnset = require(‘./_baseUnset‘),//_.unset的基本实现,移除对象中对应路径的元素(暂时不分析)
    isIndex = require(‘./_isIndex‘);//是否是一个正确的索引

var arrayProto = Array.prototype;//数组原型

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

/**
 * _.pullAt的基本实现,不支持单独的索引值(rest参数形式),不能捕获删除的元素。
 *
 * @private
 * @param {Array} array 需要修改的数组.
 * @param {number[]} indexes 需要移除的索引值的数组.
 * @returns {Array} 返回修改之后的数组.
 */
function basePullAt(array, indexes) {
  var length = array ? indexes.length : 0,//需要移除的值的个数
      lastIndex = length - 1;//最后一个需要移除的索引的索引
  
  //从indexes末尾遍历(从开始遍历删除元素会改变索引)
  while (length--) {
    var index = indexes[length];//需要移除的索引
    if (length == lastIndex || index !== previous) {//如果index不是privious(防止重复的索引)
      var previous = index;//上一个移除的索引
      if (isIndex(index)) {//如果是一个正确的索引,将数组中这个索引的元素移除
        splice.call(array, index, 1);
      } else {//如果不是,将数组中这个路径的的元素移除
        baseUnset(array, index);
      }
    }
  }
  return array;//返回修改之后的数组
}

module.exports = basePullAt;

对应的源码为

//.pullAt.js

var arrayMap = require(‘./_arrayMap‘),//同Array.map
    baseAt = require(‘./_baseAt‘),//_.at的的基本实现,返回一个数组包含对象中对应路径的元素(暂时不分析)
    basePullAt = require(‘./_basePullAt‘),//basePullAt方法
    compareAscending = require(‘./_compareAscending‘),//排序规则,简单排序(如果a小于b,那么a在b的前面)
    flatRest = require(‘./_flatRest‘),//baseRest的特殊版本,将rest参数扁平化一级
    isIndex = require(‘./_isIndex‘);//是否是正确的索引

/**
 * 
 *
 * @param {Array} array 需要修改的数组.
 * @param {...(number|number[])} [indexes] 需要移除的元素indexes.
 * @returns {Array} 返回一个新数组包含所有移除的元素.
 * @example
 *
 * var array = [‘a‘, ‘b‘, ‘c‘, ‘d‘];
 * var pulled = _.pullAt(array, [1, 3]);
 *
 * console.log(array);
 * // => [‘a‘, ‘c‘]
 *
 * console.log(pulled);
 * // => [‘b‘, ‘d‘]
 */
var pullAt = flatRest(function(array, indexes) {//创建具备rest参数的方法,并且将rest参数扁平化一级,比如(func(arr,[1,2,3],[4,5]) => func(arr,[1,2,3,4,5]))
  var length = array == null ? 0 : array.length,//数组长度
      result = baseAt(array, indexes);//得到对应索引的所有元素
  //调用basePullAt方法,先并且判断每个索引值是否正确,然后对索引进行排序之后传入当做indexes
  basePullAt(array, arrayMap(indexes, function(index) {
    return isIndex(index, length) ? +index : index;
  }).sort(compareAscending));

  return result;//返回结果数组
});

module.exports = pullAt;

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

移除数组中所有通过判断条件返回的true的元素,然后返回包含所有移除元素的数组,判断条件接受三个参数(value,index,array)

//remove.js


var baseIteratee = require(‘./_baseIteratee‘),//遍历器封装
    basePullAt = require(‘./_basePullAt‘);//basePullAt方法

/**
 * @param {Array} array 需要修改的数组.
 * @param {Function} [predicate=_.identity] 判断条件.
 * @returns {Array} 返回包含所有移除的元素的数组.
 * @example
 *
 * var array = [1, 2, 3, 4];
 * var evens = _.remove(array, function(n) {
 *   return n % 2 == 0;
 * });
 *
 * console.log(array);
 * // => [1, 3]
 *
 * console.log(evens);
 * // => [2, 4]
 */
function remove(array, predicate) {
  var result = [];//返回结果数组
  if (!(array && array.length)) {//如果没有传入array或者为空数组,返回空数组
    return result;
  }
  var index = -1,//数组索引
      indexes = [],//需要移除的索引
      length = array.length;//数组长度

  predicate = baseIteratee(predicate, 3);//将判断条件封装,支持简写
  while (++index < length) {//遍历数组中的元素
    var value = array[index];//当前元素
    if (predicate(value, index, array)) {//调用判断方法返回true,将这个元素加入到结果中,将当前索引值加到indexes中
      result.push(value);
      indexes.push(index);
    }
  }
  basePullAt(array, indexes);//调用basePullAt方法移除indexes中的元素
  return result;//返回结果数组
}

module.exports = remove;

_.reverse(array)

颠倒数组中元素的顺序.

//reserve.js

var arrayProto = Array.prototype;//数组原型

var nativeReverse = arrayProto.reverse;//原生reverse方法的引用

/**
 *
 * @param {Array} array 需要修改的数组.
 * @returns {Array} 返回修改后的数组.
 * @example
 *
 * var array = [1, 2, 3];
 *
 * _.reverse(array);
 * // => [3, 2, 1]
 *
 * console.log(array);
 * // => [3, 2, 1]
 */
function reverse(array) {
  return array == null ? array : nativeReverse.call(array);//不解释
}

module.exports = reverse;

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

创建一个数组片段从数组中的start位置开始,到end位置结束,但不包括end.

这个方法依赖于baseSlice方法,在之前的方法中也经常需要使用这个方法。

//_baseSlice.js

/**
 * _.slice的基本实现.
 *
 * @private
 * @param {Array} array 需要切割的数组.
 * @param {number} [start=0] 切割开始位置.
 * @param {number} [end=array.length] 切割结束位置.
 * @returns {Array} 返回切好的数组片段.
 */
function baseSlice(array, start, end) {
  var index = -1,//数组索引
      length = array.length;//数组长度

  if (start < 0) {//start小于0从末尾算起
    start = -start > length ? 0 : (length + start);
  }
  end = end > length ? length : end;//end不能超过length
  if (end < 0) {//end小于0从末尾算起
    end += length;
  }
  length = start > end ? 0 : ((end - start) >>> 0);// 需要切割的长度,借助右移位运算符 用零填充length 左边空出的位,这样做的好处是如果 length 未定义就取0
  start >>>= 0;

  var result = Array(length);//返回结果数组
  //遍历,从start位置开始,将对应的值添加到结果数组中,直到添加length个元素
  while (++index < length) {
    result[index] = array[index + start];
  }
  return result;//返回结果数组
}

module.exports = baseSlice;

slice方法

//slice.js

var baseSlice = require(‘./_baseSlice‘),//baseSlice方法
    isIterateeCall = require(‘./_isIterateeCall‘),//是否是遍历器的参数(value,index,array)
    toInteger = require(‘./toInteger‘);//转化成整型

/**
 * 
 *
 * @param {Array} array 需要切割的数组.
 * @param {number} [start=0] 切割的开始位置.
 * @param {number} [end=array.length] 切割的结束位置.
 * @returns {Array} 返回切好的数组片段.
 */
function slice(array, start, end) {
  var length = array == null ? 0 : array.length;//数组长度
  if (!length) {//如果为空数组,返回空数组
    return [];
  }
  //如果是遍历器的参数,start=0,end=length(不知道有啥用)
  if (end && typeof end != ‘number‘ && isIterateeCall(array, start, end)) {
    start = 0;
    end = length;
  }
  else {
      //没有传start从开始,也就是返回完整的数组
    start = start == null ? 0 : toInteger(start);
    //没有传end就切到数组结尾
    end = end === undefined ? length : toInteger(end);
  }
  return baseSlice(array, start, end);//调用baseSlice方法,并将结果作为返回值返回
}

module.exports = slice;

_.sortedIndex(array, value)

 执行一个二分法检索决定value应该被插入数组中的位置,使原数组保持它的排序。

_.sortedIndexBy(array, value, [iteratee=_.identity])

这个方法和_.sortedIndex很像,除了它接受一个遍历器被value和array中的每个元素调用以计算排序位置,
遍历器接受一个参数(value)

_.sortedIndexOf(array, value)

 和_.indexOf很像,除了它执行一个二分法检索在一个已将排序的数组上

_.sortedLastIndex(array, value)

这个方法和_.sortedIndex很像,除了他返回value被插入到文档中的最高的index

_.sortedLastIndexBy(array, value, [iteratee=_.identity])

和_.sortedLastIndex很像,除了它接受一个遍历器被value和array中的每个元素调用以计算排序位置,
遍历器接受一个参数(value)

_.sortedLastIndexOf(array, value)

和_.lastIndexOf很像,除了它执行一个二分法检索在一个已将排序的数组上

sorted系列方法,用于对排序的数组的操作,依赖于baseSortedIndexBy方法和baseSortedIndex方法

//_baseSortedIndexBy.js

var isSymbol = require(‘./isSymbol‘);//判断是否为Symbol(js的第七种数据类型,表示一种唯一值)

var MAX_ARRAY_LENGTH = 4294967295,//数组最大长度
    MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1;//数组最大index

var nativeFloor = Math.floor,//原生下舍入方法
    nativeMin = Math.min;//原生最小值方法

/**
 * _.sortedIndexBy和_.sortedLastIndexBy的基本实现,对每个元素调用遍历器计算排序排名,遍历器接受一个参数(value)
 *
 * @param {Array} array 需要处理的数组.
 * @param {*} value 需要评估的值.
 * @param {Function} iteratee 遍历器,被每个元素调用.
 * @param {boolean} [retHighest] 指定是否返回最高的符合条件的index.
 * @returns {number} 返回value应该被插入数组中的位置
 */
function baseSortedIndexBy(array, value, iteratee, retHighest) {
  value = iteratee(value);//对value调用遍历器

  var low = 0,//开始位置
      high = array == null ? 0 : array.length,//结束位置
      valIsNaN = value !== value,//是否是NaN
      valIsNull = value === null,//是否为空
      valIsSymbol = isSymbol(value),//是否是Symbol
      valIsUndefined = value === undefined;//是否是undefined
  //循环,直到开始位置等于结束位置
  while (low < high) {
    var mid = nativeFloor((low + high) / 2),//中间位置
        computed = iteratee(array[mid]),//对中间位置的值调用遍历器得到computed
        othIsDefined = computed !== undefined,//是否为undefined
        othIsNull = computed === null,//是否为null
        othIsReflexive = computed === computed,//是否为正常的值
        othIsSymbol = isSymbol(computed);//是否是Symbol

    if (valIsNaN) {//如果value是NaN
      var setLow = retHighest || othIsReflexive;//是否可以设置新的开始位置
    } else if (valIsUndefined) {//如果value为undefined,
      setLow = othIsReflexive && (retHighest || othIsDefined);
    } else if (valIsNull) {//如果value为空值
      setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);
    } else if (valIsSymbol) {//如果value是Symbol
      setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);
    } else if (othIsNull || othIsSymbol) {//如果中间位置的值为空或者为Symbol
      setLow = false;
    } else {//正常情况,如果中间的值比评估的值小,那么重新设置开始位置
      setLow = retHighest ? (computed <= value) : (computed < value);
    }
    //如果setLow为true,设置起始位置为数组中间位置+1
    if (setLow) {
      low = mid + 1;
    } else {//如果已经不需要在设置起始位置了,设置high为当前范围的中间位置
      high = mid;
    }
  }
  return nativeMin(high, MAX_ARRAY_INDEX);//返回high和最大数组长度的索引的最小值
}

module.exports = baseSortedIndexBy;
//_baseSortedIndex.js

var baseSortedIndexBy = require(‘./_baseSortedIndexBy‘),//baseSortedIndexBy方法
    identity = require(‘./identity‘),//返回第一个参数
    isSymbol = require(‘./isSymbol‘);//是否是Symbol

var MAX_ARRAY_LENGTH = 4294967295,//最大数组长度
    HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;//最大数组长度的一半

/**
 * _.sortedIndex和_.sortedLastIndex的基本实现,执行一个二分法检索决定value应该被插入数组中的位置,使原数组保持它的排序。
 *
 * @private
 * @param {Array} array 需要处理的已经排序的数组.
 * @param {*} value 需要评估的值.
 * @param {boolean} [retHighest] 指定是否返回最高的符合条件的index.
 * @returns {number} 返回value应该被插入数组中的位置
 */
function baseSortedIndex(array, value, retHighest) {
  var low = 0,//起始位置
      high = array == null ? low : array.length;//结束位置

  if (typeof value == ‘number‘ && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
      //循环,直到开始位置等于结束位置
    while (low < high) {
      var mid = (low + high) >>> 1,//中间位置
          computed = array[mid];//中间位置的值

      if (computed !== null && !isSymbol(computed) &&
          (retHighest ? (computed <= value) : (computed < value))) {//正常情况,如果中间的值比评估的值小,那么设置开始位置为中间位置+1
        low = mid + 1;
      } else {//否则设置high为中间位置
        high = mid;
      }
    }
    return high;//返回结束位置
  }
     //如果value不是数字,或者为NaN或者长度太长,调用baseSortedIndexBy,并将结果作为返回值返回
  return baseSortedIndexBy(array, value, identity, retHighest);
}

module.exports = baseSortedIndex;

对应的方法

sortedIndex

//sortedIndex.js

var baseSortedIndex = require(‘./_baseSortedIndex‘);//baseSortedIndex方法

/**.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array 需要处理的已经排序的数组.
 * @param {*} value 需要评估的值.
 * @returns {number} 返回value应该被插入数组中的位置.
 * @example
 *
 * _.sortedIndex([30, 50], 40);
 * // => 1
 */
function sortedIndex(array, value) {
  return baseSortedIndex(array, value);
}

module.exports = sortedIndex;

baseIndexBy

//baseIndexBy.js


var baseIteratee = require(‘./_baseIteratee‘),//封装遍历器
    baseSortedIndexBy = require(‘./_baseSortedIndexBy‘);//baseSortedIndexBy方法

/**
 * 
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Array
 * @param {Array} array 需要处理的已经排序的数组.
 * @param {*} value 需要评估的值.
 * @param {Function} [iteratee=_.identity] 遍历器,被每个元素调用.
 * @returns {number} 返回value应该被插入数组中的位置.
 * @example
 *
 * var objects = [{ ‘x‘: 4 }, { ‘x‘: 5 }];
 *
 * _.sortedIndexBy(objects, { ‘x‘: 4 }, function(o) { return o.x; });
 * // => 0
 *
 * // The `_.property` iteratee shorthand.
 * _.sortedIndexBy(objects, { ‘x‘: 4 }, ‘x‘);
 * // => 0
 */
function sortedIndexBy(array, value, iteratee) {
  return baseSortedIndexBy(array, value, baseIteratee(iteratee, 2));//不解释
}

module.exports = sortedIndexBy;

sortedIndexOf

//sortedIndexOf.js


var baseSortedIndex = require(‘./_baseSortedIndex‘),//baseSortedIndex方法
    eq = require(‘./eq‘);//判断是否相等

/**
 *
 *
 * @param {Array} array 需要处理的数组.
 * @param {*} value 需要查找的值.
 * @returns {number} 返回匹配的索引值,或者-1.
 * @example
 *
 * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
 * // => 1
 */
function sortedIndexOf(array, value) {
  var length = array == null ? 0 : array.length;//数组长度
  if (length) {
    var index = baseSortedIndex(array, value);//调用baseSortedIndex获取应该插入index
    if (index < length && eq(array[index], value)) {//如果不是插入最后一个并且数组中index的值等于value,返回这个index
      return index;
    }
  }
  return -1;//返回-1
}

module.exports = sortedIndexOf;

sortedLastIndex

//sortedLastIndex.js

var baseSortedIndex = require(‘./_baseSortedIndex‘);//baseSortedIndex方法

/**
 *
 * @param {Array} array 需要处理的数组.
 * @param {*} value 需要评估的值.
 * @returns {number} 返回value应该被插入数组中的位置.
 * @example
 *
 * _.sortedLastIndex([4, 5, 5, 5, 6], 5);
 * // => 4
 */
function sortedLastIndex(array, value) {
  return baseSortedIndex(array, value, true);//不解释
}

module.exports = sortedLastIndex;

sortedLastIndexBy

//sortedLastIndexBy.js

var baseIteratee = require(‘./_baseIteratee‘),//遍历器封装
    baseSortedIndexBy = require(‘./_baseSortedIndexBy‘);//baseSortedIndexBy方法

/**
 *
 * @param {Array} array 需要处理的数组.
 * @param {*} value 需要评估的值.
 * @param {Function} [iteratee=_.identity] 遍历器,被每个元素调用.
 * @returns {number} 返回value应该被插入数组中的位置.
 * @example
 *
 * var objects = [{ ‘x‘: 4 }, { ‘x‘: 5 }];
 *
 * _.sortedLastIndexBy(objects, { ‘x‘: 4 }, function(o) { return o.x; });
 * // => 1
 *
 * // The `_.property` iteratee shorthand.
 * _.sortedLastIndexBy(objects, { ‘x‘: 4 }, ‘x‘);
 * // => 1
 */
function sortedLastIndexBy(array, value, iteratee) {//不解释
  return baseSortedIndexBy(array, value, baseIteratee(iteratee, 2), true);
}

module.exports = sortedLastIndexBy;

sortedLastIndexOf

//sortedLastIndexOf.js

var baseSortedIndex = require(‘./_baseSortedIndex‘),//baseSortedIndex方法
    eq = require(‘./eq‘);//判断是否相等

/**
 * 
 * @param {Array} array 需要处理的数组.
 * @param {*} value 需要查找的值.
 * @returns {number} 返回匹配的元素的index,或者-1.
 * @example
 *
 * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
 * // => 3
 */
function sortedLastIndexOf(array, value) {
  var length = array == null ? 0 : array.length;//数组长度
  if (length) {//如果不为空数组
    var index = baseSortedIndex(array, value, true) - 1;//调用baseSortedIndex获取索引
    if (eq(array[index], value)) {//如果数组中index位置的值等于value,返回这个index
      return index;
    }
  }
  return -1;//返回-1
}

module.exports = sortedLastIndexOf;

_.sortedUniq(array)

 返回数组中唯一的值,和_.uniq很像,除了这是用来处理和优化排序数组的

_.sortedUniqBy(array, [iteratee])

和_.sortedUniq很像,除了它接受一个遍历方法被每个元素调用

这两个方法依赖于baseSortedUniq方法

//_baseSortedUniq.js

var eq = require(‘./eq‘);//判断是否相等

/**
 * _.sortedUniq和_.sortedUniqBy的基本实现,不支持遍历器的简写
 *
 * @private
 * @param {Array} array 需要修改的数组.
 * @param {Function} [iteratee] 遍历器,被每个元素调用.
 * @returns {Array} 返回新的没有重复的数组.
 */
function baseSortedUniq(array, iteratee) {
  var index = -1,//数组索引
      length = array.length,//数组长度
      resIndex = 0,//返回值索引
      result = [];//返回数组
  //遍历array
  while (++index < length) {
    var value = array[index],//当前元素
        computed = iteratee ? iteratee(value) : value;//如果有遍历器进行调用,得到computed

    if (!index || !eq(computed, seen)) {//如果index为0或者computed和seen不相等
      var seen = computed;//当前存储的计算的值
      result[resIndex++] = value === 0 ? 0 : value;//将这个value存入结果中
    }
  }
  return result;//返回结果
}

module.exports = baseSortedUniq;

对应方法

sortedUniq

//sortedUniq.js

var baseSortedUniq = require(‘./_baseSortedUniq‘);//baseSortedUniq方法

/**
 *
 *
 * @param {Array} array 需要处理的数组.
 * @returns {Array} 返回新的没有重复的数组.
 * @example
 *
 * _.sortedUniq([1, 1, 2]);
 * // => [1, 2]
 */
function sortedUniq(array) {//不解释
  return (array && array.length)
    ? baseSortedUniq(array)
    : [];
}

module.exports = sortedUniq;

sortedUniqBy

//sortedUniqBy.js

var baseIteratee = require(‘./_baseIteratee‘),//便利器封装
    baseSortedUniq = require(‘./_baseSortedUniq‘);//baseSortedUniq方法

/**
 * 
 *
 * @param {Array} array 需要处理的数组.
 * @param {Function} [iteratee] 遍历器被每个元素调用.
 * @returns {Array} 返回新的没有重复的数组.
 * @example
 *
 * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
 * // => [1.1, 2.3]
 */
function sortedUniqBy(array, iteratee) {//不解释
  return (array && array.length)
    ? baseSortedUniq(array, baseIteratee(iteratee, 2))
    : [];
}

module.exports = sortedUniqBy;

_.tail(array)

 返回数组中除了第一个元素之外的所有元素.

//tail.js

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

/**
 *
 *
 * @param {Array} array 需要查询的数组.
 * @returns {Array} 返回切好的数组片段.
 * @example
 *
 * _.tail([1, 2, 3]);
 * // => [2, 3]
 */
function tail(array) {
  var length = array == null ? 0 : array.length;//数组长度
  return length ? baseSlice(array, 1, length) : [];//如果不为空数组,调用baseSlice方法从1位置切割数组并返回,否则返回空数组
}

module.exports = tail;

一天一点进步,一步一个脚印~~~

以上是关于lodash源码学习的主要内容,如果未能解决你的问题,请参考以下文章

lodash源码学习(13)

lodash源码学习

lodash源码学习

lodash源码学习

lodash源码学习(10)

lodash源码学习debounce,throttle