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源码学习的主要内容,如果未能解决你的问题,请参考以下文章