lodash源碼學習(2)
繼續學習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源碼學習(2)