lodash源码学习
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lodash源码学习相关的知识,希望对你有一定的参考价值。
继续学习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源码学习的主要内容,如果未能解决你的问题,请参考以下文章