最近工作比较忙,做不到每周两篇了,周末赶着写吧,上篇我针对一些方法进行了分析,今天继续。
没看过前两篇的可以猛戳这里:
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 });
小结
今天分析的几个内部函数理解起来有点难度,理解要多看几遍分析不同的情况,也许结合调用的例子理解起来会容易一点,分析我都写在了代码间的注释里,这样读起源码会容易一点,今天就写到这里吧,有点累了,明天还要上班,剩下的方法在之后的文章里都会分析到。