Underscore 中间 flatten 相关的方法之前一直不是很理解,现在完全搞懂了,稍微说一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var flatten = function(input, shallow, strict, output) {
output = output || [];
var idx = output.length;
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
// 若value为数组,把里面东西去出来赋值给output
// 否则直接赋值给output
// isArrayLike的判断可以去掉,保留的原因是因为他用来判断value是否为数组很快,可以迅速筛选掉非数组
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
// Flatten current level of array or arguments object.
if (shallow) {
// 如果给了shallow参数,只只遍历一层
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
// 一直遍历下去,如果是元素则按下面赋值,如果是数组则继续遍历
flatten(value, shallow, strict, output);
idx = output.length;
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
};

这个方法不难看懂,作用是将input平铺展开,如果 shallowtrue,则只展开一层。

1
2
_.flatten([1, 2, [3], [[4, [5]]]]) // [1, 2, 3, 4, 5]
_.flatten([1, 2, [3], [[4, [5]]]], true) // [1, 2, 3, [4, [5]]]

这里的 strict 参数就是之前一直卡住的原因,就是下面这个地方:

1
2
3
4
5
6
7
8
9
10
11
_.without = restArgs(function(array, otherArrays) {
return _.difference(array, otherArrays);
});
_.difference = restArgs(function(array, rest) {
rest = flatten(rest, true, true);
// 遍历array,如果array中一个元素包含在rest中,则去掉该元素
return _.filter(array, function(value){
return !_.contains(rest, value);
});
});

这是两个方法,那时候想 without 方法调用的时候, otherArrays是一个数组了,到 difference 方法的时候,这个数组去调用 flatten 方法的时候不是会出问题吗?

1
_.flatten([1, 2, 3], true, true) // []

脑子里面就这样想…卡了好久,等我基本看了全部源码才会过来看才理解了。
difference 方法的 restArgs 很重要,他们两个是各自独立的方法,但是 without 可以共用 difference 的逻辑。
上面那样子理解是有问题的,因为在 without 方法中 otherArrays 如果是[1, 2, 3],到了 flatten 调用的时候因为 restArgs 的关系他变成了 [[1, 2, 3]],调用最后返回结果[1, 2, 3]。然后我就纳闷了,加了一层又解除这是何解…
不过抛开 without 方法去看 difference 方法就能理解了。

1
2
3
4
5
6
7
8
_.without([1, 2, 3, 4, 5, 6], 1, 2, 3);
// 1, 2, 3通过restArgs变为[1, 2, 3],传入difference方法
// [1, 2, 3]通过restArgs变为[[1, 2, 3]],传入flatten方法
// 返回[1, 2, 3],剩下的可以看懂了不解释
// 之所以shallow和strict都为true,是因为不需要两个数组即使内容一样他们也是不想等的,
// 不需要进行处理,所以没必要展开
_.difference([1, 2, 3, 4, 5, 6], [1, 2], [3]);
// [1, 2], [3]通过restArgs变为[1, 2, 3],传入difference方法,其余同

所以其实 difference 方法的 restArgs 虽然对与 without 方法中的调用是多余的,但是作为一个独立的方法,他还是有必要的。
上面注释应该说的很清楚了,完。

其实只是我自己没看清楚而已,也不难。这个地方的很多方法比如 union, intersection等等都是集合的相关操作。比如 difference 就是差集,union 就是并集,而intersection 就是交集。