Underscore 源码学习 (七) - flatten

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

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,则只展开一层。

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

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

_.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 方法的时候不是会出问题吗?

_.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 方法就能理解了。

_.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 就是交集。