最近工作比較忙,做不到每周兩篇了,周末趕著寫吧,上篇我針對一些方法進行了分析,今天繼續。 沒看過前兩篇的可以猛戳這裡: underscore.js源碼解析(一) underscore.js源碼解析(二) underscore.js源碼GitHub地址: https://github.com/jash
最近工作比較忙,做不到每周兩篇了,周末趕著寫吧,上篇我針對一些方法進行了分析,今天繼續。
沒看過前兩篇的可以猛戳這裡:
underscore.js源碼GitHub地址: https://github.com/jashkenas/underscore/blob/master/underscore.js本文解析的underscore.js版本是1.8.3
_.map/_.collect
1 _.map = _.collect = function(obj, iteratee, context) { 2 iteratee = cb(iteratee, context); 3 //判斷是否是數組,是的話就將裡面的屬性取出 4 var keys = !isArrayLike(obj) && _.keys(obj), 5 length = (keys || obj).length, 6 results = Array(length); 7 for (var index = 0; index < length; index++) { 8 var currentKey = keys ? keys[index] : index; 9 results[index] = iteratee(obj[currentKey], currentKey, obj); 10 } 11 return results; 12 };
map和each的區別就是map是將最後的結果以數組的形式返回
createReduce
1 var createReduce = function(dir) { 2 var reducer = function(obj, iteratee, memo, initial) { 3 //判斷是否是數組 4 var keys = !isArrayLike(obj) && _.keys(obj), 5 length = (keys || obj).length, 6 //判斷迭代方向,dir為1是從左向右迭代,dir為-1是從右向左迭代(從_.reduce和_.reduceRight里就可以清晰的看出來) 7 index = dir > 0 ? 0 : length - 1; 8 //判斷是否存在初始值 9 if (!initial) { 10 //如果沒有初始值,則將第一個設置為初始值,前面判斷了是否是數組,是數組返回數組否則返回對象 11 memo = obj[keys ? keys[index] : index]; 12 //根據方向將初始位置賦給index,為了下邊遍歷使用 13 index += dir; 14 } 15 //進行遍歷 16 for (; index >= 0 && index < length; index += dir) { 17 var currentKey = keys ? keys[index] : index; 18 //進行迭代運算 19 memo = iteratee(memo, obj[currentKey], currentKey, obj); 20 } 21 return memo; 22 }; 23 24 return function(obj, iteratee, memo, context) { 25 //通過參數數量判斷是否有初始值 26 var initial = arguments.length >= 3; 27 //調用reducer() 28 return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); 29 }; 30 };
看了上面的註釋你可以清晰的發現createReduce的結構已經很清晰了,內部調用reducer函數,先判斷有沒有初始值,如果沒有則根據迭代方向給初始值賦值,然後進行迴圈的迭代。
createPredicateIndexFinder
1 var createPredicateIndexFinder = function(dir) { 2 return function(array, predicate, context) { 3 //迭代函數 4 predicate = cb(predicate, context); 5 var length = getLength(array); 6 //判斷遍歷方向,dir為1是從左向右,dir為-1是從右向左 7 var index = dir > 0 ? 0 : length - 1; 8 for (; index >= 0 && index < length; index += dir) { 9 //遍歷返回符合條件的是第幾個 10 if (predicate(array[index], index, array)) return index; 11 } 12 //否則返回-1 13 return -1; 14 }; 15 };
_.findIndex/_.findLastIndex
1 _.findIndex = createPredicateIndexFinder(1); 2 _.findLastIndex = createPredicateIndexFinder(-1);
調用上面的createPredicateIndexFinder內部函數,兩者只是遍歷的方向不同,最終返回相應的索引
_.findKey
1 _.findKey = function(obj, predicate, context) { 2 //迭代函數 3 predicate = cb(predicate, context); 4 var keys = _.keys(obj), key; 5 for (var i = 0, length = keys.length; i < length; i++) { 6 //獲取對象屬性 7 key = keys[i]; 8 if (predicate(obj[key], key, obj)) return key; 9 } 10 };
返回滿足條件的屬性
_.find / _.detect
1 _.find = _.detect = function(obj, predicate, context) { 2 var key; 3 if (isArrayLike(obj)) { 4 //如果是數組通過findIndex獲取滿足條件的索引 5 key = _.findIndex(obj, predicate, context); 6 } else { 7 //如果是對象則通過findKey獲取滿足條件的屬性 8 key = _.findKey(obj, predicate, context); 9 } 10 //根據上面取到的索引或屬性,返回相應的值 11 if (key !== void 0 && key !== -1) return obj[key]; 12 };
_.filter/_.select
1 _.filter = _.select = function(obj, predicate, context) { 2 var results = []; 3 predicate = cb(predicate, context); 4 //遍曆數據,將符合條件的存入results數組當中,並返回 5 _.each(obj, function(value, index, list) { 6 if (predicate(value, index, list)) results.push(value); 7 }); 8 return results; 9 };
_.negate
1 _.negate = function(predicate) { 2 return function() { 3 return !predicate.apply(this, arguments); 4 }; 5 };
_.negate就是一個取反的函數
_.reject
1 _.reject = function(obj, predicate, context) { 2 return _.filter(obj, _.negate(cb(predicate)), context); 3 };_.reject調用了filter,只是做了一個判斷條件的取反操作,說明_.reject就是講不符合條件的存入數組並返回
_.every/_.all
1 _.every = _.all = function(obj, predicate, context) { 2 predicate = cb(predicate, context); 3 //判斷數組還是對象 4 var keys = !isArrayLike(obj) && _.keys(obj), 5 length = (keys || obj).length; 6 for (var index = 0; index < length; index++) { 7 //數組獲取索引,對象獲取屬性 8 var currentKey = keys ? keys[index] : index; 9 //如果有不滿足條件的就返回false 10 if (!predicate(obj[currentKey], currentKey, obj)) return false; 11 } 12 return true; 13 };
_.every就是判斷是否所有的數據都滿足條件
_.some
1 _.some = _.any = function(obj, predicate, context) { 2 predicate = cb(predicate, context); 3 var keys = !isArrayLike(obj) && _.keys(obj), 4 length = (keys || obj).length; 5 for (var index = 0; index < length; index++) { 6 var currentKey = keys ? keys[index] : index; 7 if (predicate(obj[currentKey], currentKey, obj)) return true; 8 } 9 return false; 10 };
跟every的判斷結構差不多,只不過最後的判斷條件不同,some是判斷時候有滿足條件的,有的話就返回true
_.values
1 _.values = function(obj) { 2 var keys = _.keys(obj); 3 var length = keys.length; 4 var values = Array(length); 5 for (var i = 0; i < length; i++) { 6 values[i] = obj[keys[i]]; 7 } 8 return values; 9 };
_.values就是講obj的所有值拷貝到數組當中
_.sortedIndex
1 _.sortedIndex = function(array, obj, iteratee, context) { 2 iteratee = cb(iteratee, context, 1); 3 var value = iteratee(obj); 4 var low = 0, high = getLength(array); 5 while (low < high) { 6 var mid = Math.floor((low + high) / 2); 7 if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; 8 } 9 return low; 10 };
_.sortedIndex看過源碼就可以看出是二分法查找
createIndexFinder
1 var createIndexFinder = function(dir, predicateFind, sortedIndex) { 2 return function(array, item, idx) { 3 var i = 0, length = getLength(array); 4 if (typeof idx == 'number') { 5 //判斷方向,1是從前到後,-1則為從後到前 6 if (dir > 0) { 7 i = idx >= 0 ? idx : Math.max(idx + length, i); 8 } else { 9 length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; 10 } 11 //如果是排序好的就使用二分法 12 } else if (sortedIndex && idx && length) { 13 idx = sortedIndex(array, item); 14 //判斷找出的值是否一樣,是就返回這個值,否則返回-1 15 return array[idx] === item ? idx : -1; 16 } 17 if (item !== item) { 18 //對item為NaN的處理 19 idx = predicateFind(slice.call(array, i, length), _.isNaN); 20 return idx >= 0 ? idx + i : -1; 21 } 22 for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { 23 //通過遍歷的方法找出item對應的索引 24 if (array[idx] === item) return idx; 25 } 26 //找不到則返回-1 27 return -1; 28 }; 29 };
_.indexOf/_.lastIndexOf
1 _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); 2 _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
_.contains/_.includes/_.include
1 //判斷是否包含對應的值 2 _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { 3 //如果是對象,則將obj的所有值拷貝到數組當中 4 if (!isArrayLike(obj)) obj = _.values(obj); 5 if (typeof fromIndex != 'number' || guard) fromIndex = 0; 6 //查找是否存在這個值,如果存在,indexOf 返回相應的索引,則為true,如果不存在,indexOf 返回-1,則為false 7 return _.indexOf(obj, item, fromIndex) >= 0; 8 };
_.invoke
1 //對每一個元素都執行一次方法,最後把結果存入數組返回 2 _.invoke = restArgs(function(obj, method, args) { 3 var isFunc = _.isFunction(method); 4 return _.map(obj, function(value) { 5 //如果是函數則每個元素都執行一遍方法,如果不是,則返回所有的值,最後結果以數組的形式返回 6 var func = isFunc ? method : value[method]; 7 return func == null ? func : func.apply(value, args); 8 }); 9 });
小結
今天分析的幾個內部函數理解起來有點難度,理解要多看幾遍分析不同的情況,也許結合調用的例子理解起來會容易一點,分析我都寫在了代碼間的註釋里,這樣讀起源碼會容易一點,今天就寫到這裡吧,有點累了,明天還要上班,剩下的方法在之後的文章里都會分析到。