终于看完了 Underscore 的集合部分了,看 Underscore 源码真的是长见识了,感觉真的受益匪浅。
但是集合里面方法也挺多的,我都不知道该拿哪些出来讲下,最近接触了 Redux,就说下 createRedux 这个方法吧,为后面讲 Redux 做个铺垫。
先看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var createReduce = function(dir) {
var reducer = function(obj, iteratee, memo, initial) {
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if(!initial) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
for(; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};
return function(obj, iteratee, memo, context) {
var initial = arguments.length >= 3;
return reducer(obj, opitimizeCb(iteratee, context, 4), memo, initial);
};
};

这是 reduce 函数的工厂函数,用于生成一个 reducer ,dir 是决定方向用的。
我们从最后一个 return 开始看起,即

1
2
3
4
return function(obj, iteratee, memo, context) {
var initial = arguments.length >= 3;
return reducer(obj, opitimizeCb(iteratee, context, 4), memo, initial);
};

我们使用 reduce 的时候,如果没有指定 memo 值,这时候参数个数只有两个即 obj 和 iteratee,所以 initial 为 false 表示没有初始化。对于没初始的情况,就是增加了一个 if 语句里面的内容而已,作用是把第一个元素作为 memo 值。
接着就是有没有初始化都共用的部分了,通过一个 for 循环把 keys 遍历,并把相应的信息交给 iteratee 去处理,参数 memo 是上一次处理结果。遍历完后把最后的处理结果 memo 返回就完了。
这个函数派生了两个方法,即

1
2
_.reduce = _.foldl = _.inject = createReduce(1);
_.reduceRight = _.foldr = createReduce(-1);

只是方向不同而已。
举个例子方便理解些,例如:

1
2
3
var sum = _.reduce([1, 2, 3, 4, 5], function(accumulator, value, index, collection) {
return accmulator + value;
}, 0);

结果为 15 这个应该很明显,js 原生也有 reduce 方法,如下:

1
2
3
[1, 2, 3, 4, 5].reduce(function(left, right) {
return left + right;
});

我们看下 Underscore 的例子,主要想说明下他的运行过程,如下:

1
2
3
4
5
6
7
8
9
10
if(!initial)
memo = 1; index = 1;
endif
for
memo = iteratee(1, 2, 1, [1, 2, 3, 4, 5]) = 3;
memo = iteratee(3, 3, 2, [1, 2, 3, 4, 5]) = 6;
memo = iteratee(6, 4, 3, [1, 2, 3, 4, 5]) = 10;
memo = iteratee(10, 5, 4, [1, 2, 3, 4, 5]) = 15;
endfor
return memo = 15;

额,我也不知道写的是什么东西,只是描述下过程而已,你懂的。
剩下的集合部分感觉也没啥好说的了,花点时间看下就可以看懂了=.=。