我正在尝试掌握以下代码如何实现每一个。
_.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
将不胜感激。
答案 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
累加器是第一轮中reduce
(true
)的参数,在后一轮中,它是上一轮的结果。整个函数的结果是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;
};