underscore.js源码解析(三)

最近工作比较忙,做不到每周两篇了,周末赶着写吧,上篇我针对一些方法进行了分析,今天继续。

没看过前两篇的可以猛戳这里:

underscore.js源码解析(一)

underscore.js源码解析(二)

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   });

小结

今天分析的几个内部函数理解起来有点难度,理解要多看几遍分析不同的情况,也许结合调用的例子理解起来会容易一点,分析我都写在了代码间的注释里,这样读起源码会容易一点,今天就写到这里吧,有点累了,明天还要上班,剩下的方法在之后的文章里都会分析到。

感谢大家的观看,也希望能够和大家互相交流学习,有什么分析的不对的地方欢迎大家批评指出

参考资料

https://segmentfault.com/a/1190000000531871
http://www.w3cfuns.com/house/17398/note/class/id/bb6dc3cabae6651b94f69bbd562ff370
 
 
posted on 2016-03-06 17:24  白刃唯霜  阅读(1772)  评论(0编辑  收藏  举报