我正在努力重写许多标准Underscore.js函数的底层代码,以处理我的JavaScript技能,并且对_.every
/ _.all
感到困惑。似乎在库本身中,_.every
/ _.all
函数仅使用现有的_.each
函数编写,但我鼓励使用我的{{1}版本编写版本(已经包含我的_.reduce
版本)。我已经为下面的两个函数提供了代码。
我的_.each
函数(见下文)失败的第一个测试是使用_.every
函数(简单地返回作为参数输入的值)作为迭代器传递所有错误值的函数: / p>
测试:
_.identity
我有几个问题,为什么我的 it('fails for a collection of all-falsy results', function() {
expect(_.every([null, 0, undefined], _.identity)).to.equal(false);
});
函数失败了上面显示的测试,以及其他多个测试(例如;混合的真/假值,未定义的值等):
- 调用迭代器函数时,是否需要使用_.every
或iterator.call
?如果是,我使用哪个以及如何指定参数?
- 在这里使用iterator.apply
而不仅仅是_.reduce
有什么好处,特别是当Underscore.js库不使用_.each
时?
- 为什么返回需要被调用两次,一次是在调用_.reduce
函数时,一次是在_.reduce
中定义的匿名函数内(我在构建函数时也想知道这一点)利用_.reduce
函数?对我来说,似乎我正在返回_.map
函数的结果,该函数已经返回了一些东西。
_每:
_.reduce
_各:
_.every = function(collection, iterator) {
// TIP: Try re-using reduce() here.
return _.reduce(collection, function(allFound, item) {
return iterator(item) && allFound;
}, true);
};
_降低:
_.each = function(collection, iterator) {
// define spec for arrays
if (Array.isArray(collection)) {
for(var i = 0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
}
// define spec for objects
else {
for(var key in collection) {
iterator(collection[key], key, collection);
}
}
};
答案 0 :(得分:4)
您的测试没有通过,因为它没有按预期返回false
(尽管它返回了一个假值)。
_.every = function(collection, iterator) {
return _.reduce(collection, function(allFound, item) {
return iterator(item) && allFound;
}, true);
};
当您返回iterator(item) && allFound
时会发生什么情况,如果iterator(item)
为假(但不是false
),则不会返回false
,而是返回{{1}的值}}。要自己验证,请打开REPL,然后键入iterator(item)
;结果将是undefined && true
,而不是undefined
。
因此,如果您希望此显式返回false
,而不仅仅是假值,则必须将其强制转换为布尔值。您可以执行false
或Boolean(truthy_or_falsey_value)
。我通常更喜欢后者,所以改变你的实现:
!!truthy_or_falsey_value
您的其他问题:
调用迭代器函数时,是否需要使用iterator.call或 iterator.apply?如果是,我使用哪个以及如何指定参数?
这取决于你的目标是什么。当您想要控制函数体中_.every = function(collection, iterator) {
return _.reduce(collection, function(allFound, item) {
return !!iterator(item) && allFound;
}, true);
};
关键字的值时,主要使用call
和apply
。 JavaScript的一些内置数组方法(如this
和Array.prototype.map
)采用Array.prototype.filter
,这是使用thisArg
或call
进行回调的内容。至于apply
和call
之间的差异,它只是如何处理参数。有关详细信息,请参阅this answer。
在这里使用
时apply
而不仅仅是reduce
有什么好处, 特别是当Underscore.js库不使用each
?
可能没有,或者很少。可能存在性能差异,但最好的方法是分析两种方法。
为什么返回需要调用两次,一次调用时 _.reduce函数,一次在_.reduce
中定义的匿名函数内
如果你想要一个函数 - 任何函数 - 来返回一个值,你必须从该函数中调用return。你不能期望从内部函数调用reduce
,并期望封闭函数神奇地理解它应该反过来返回被调用函数的值。如果未明确调用return
,某些语言默认返回函数中最后一个表达式的值,这可能既方便又令人困惑,具体取决于您的观点。如果您有使用这种语言的经验(例如Ruby),那么所有return
语句对您来说可能有些过分。
作为编辑说明,我觉得return
对于测试功能来说是一个糟糕的命名选择。它实际上并不是迭代任何东西(它是一个参数的函数正在进行任何迭代)。更好的名称可能是非常通用的iterator
或callback
。术语"predicate"表示将值映射到cb
或true
的函数,这是我首选的术语。另一个常见的选择是false
,因为它毕竟只是一个对其参数执行二进制过滤的函数。