下划线 - _.every实现

时间:2016-01-20 02:51:42

标签: javascript function functional-programming underscore.js 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作为该参数传递。

还原中的if语句有点令人困惑,因为reduce将传递累加器,在本例中为true,作为iterator中的第一个参数降低。所以:

if(!prev)的确意味着if(!true),如果不是,则返回false。

会结束减少功能吗?或者它会转移到else语句?

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

1 个答案:

答案 0 :(得分:4)

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

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

首先检查prev是否已经为假(因此结果为假),这种实现避免了进一步的需要。可以说,一旦检测到错误,该功能甚至可以在那里停止处理而不再进一步,但唉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

累加器是第一轮中reducetrue)的参数,在后一轮中,它是上一轮的结果。整个函数的结果是result列的最终输入。

return check(next) ? true : false;

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

或者,可以简单地写!!check(next)。所有三个版本的效果都相同 - 将check(next)转换为布尔值。

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

_.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只会返回初始累加器(又名memo)。

如上所述,一旦我们知道结果将为false,则更快就会中止(请注意,此特定版本可能仅适用于数组):

_.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的实施(尽管有些简化)。