下划线 - _.every 实现

Underscore - _.every implementation

实施 _.every 方法

我正在尝试掌握下面的代码是如何实现的。

_.every = function(collection, iterator) {
    var check = iterator || _.identity;

    if (collection.length === 0) {
        return true;
    }

    // check if any are falsy

    return _.reduce(collection, function (prev, next) {
        if (!prev) {
            return false;
        } else {
            return check(next) ? true : false;
        }
    }, true);
};

我熟悉 reduce 以及它如何接受 accumulator 作为最后一个 argument,但我还没有看到 bool 作为该参数传递。

此外,reduce 中的 if 语句有点令人困惑,因为 reduce 将传递累加器,在本例中为 true,作为 [=18= 中的第一个参数] 减少。所以:

if(!prev) 真正的意思是 if(!true),如果它不是 true 那么 return false。

这会结束 reduce 功能吗?还是会转到 else 语句?

任何有助于阐明此代码中发生的事情的帮助,或如何实现 _.every 的更好示例将不胜感激。

reduce 函数为输入集合的每个元素调用其函数参数。 prev 第一次调用 reduce 中的匿名函数时为真,但不一定是第二次和第三次。

例如,假设 every 用于检查是否已支付数据库中的所有发票。如果一千张发票中的第二张尚未支付,则无需联系数据库服务器获取第三张和更多的元素。

通过首先检查 prev 是否已经为假(因此结果将为假),此实现避免了进一步的需要。可以说,在检测到 false 时,该函数甚至可以在那里停止处理,不再继续处理,但是可惜 reduce 不提供该选项。

例如,假设我们调用 _.every([1,2,3,4,5,6], function(i) {return i < 3;});:

 accumulator | input | result | check called?
=============================================
 true        | 1     | true   | yes
 true        | 2     | true   | yes
 true        | 3     | false  | yes
 false       | 4     | false  | no
 false       | 5     | false  | no
 false       | 6     | false  | no

累加器是第一轮reduce(true)的参数,后面的轮是上轮的结果。整个函数的结果是 result 列的最后一个条目。

return check(next) ? true : false;

的缩写
if (check(next)) {
    return true;
} else {
    return false;
}

或者,可以简单地写 !!check(next)。三个版本的效果是一样的——将check(next)转换为boolean.

另一种编写整个函数的方法是:

_.every = function(collection, iterator) {
    var check = iterator || _.identity;
    return _.reduce(collection, function (prev, next) {
        return prev && !!check(next);
    }, true);
};

这里我们使用 short-circuit operator && 来达到几乎相同的效果。请注意,我们可以安全地删除 collection.length === 0 的检查,因为 reduce 将简单地 return 初始累加器(又名 memo)。

如上所述,一旦我们知道结果将是假的,更快的方法是中止(请注意,这个特定版本可能只适用于数组):

_.every = function(collection, iterator) {
    var check = iterator || _.identity;
    for (var i = 0;i < collection.length;i++) {
        if (! check(collection[i])) {
            return false;
        }
    }
    return true;
};

这就是我们 see in the wild.

的实现(尽管有点 de-simplified)