我正在重新编写许多标准Underscore.js函数的底层代码,以提高我的JavaScript技能,并且有点被_.every/_.all所困扰。在库本身中,_.every/_.all函数似乎只使用现有的_.each函数编写,但有人鼓励我使用自己的_.reduce版本编写一个版本(该版本已经包含了我的_.each版本)。我在下面为这两个函数提供了代码。
我的_.every函数的第一个测试(也见下文)失败了,其中使用_.identity函数传递所有假值(只返回作为参数输入的值)作为迭代器:
测试:
it('fails for a collection of all-falsy results', function() {
expect(_.every([null, 0, undefined], _.identity)).to.equal(false);
});关于为什么我的_.every函数失败了上面显示的测试以及多个其他测试(例如,混合真/假值、未定义值等),我有几个问题:
-When调用迭代器函数,我需要使用iterator.call还是iterator.apply?如果是这样的话,我应该使用哪些参数,以及如何指定参数?
-What的好处是在这里使用_.reduce而不仅仅是_.each,特别是当Underscore.js库不使用_.reduce时。
-Why确实需要调用两次,一次在调用_.reduce函数时调用,一次在_.reduce中定义的匿名函数中调用(我在构建利用_.map函数的函数时也想知道这一点)?在我看来,我似乎是在返回_.reduce函数的结果,该函数已经返回了一些内容。
_.every:
_.every = function(collection, iterator) {
// TIP: Try re-using reduce() here.
return _.reduce(collection, function(allFound, item) {
return iterator(item) && allFound;
}, true);
};_.each:
_.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);
}
}
};_.reduce:
_.reduce = function(collection, iterator, accumulator) {
// add condition to set accumulator if no explicit starting value is given.
if (arguments.length < 3) {
accumulator = collection[0];
}
_.each(collection, function(value) {
accumulator = iterator(accumulator, value);
});
return accumulator;
};发布于 2014-06-19 04:57:20
您的测试没有通过,因为它没有像预期的那样返回false (尽管它返回的是falsey值)。
_.every = function(collection, iterator) {
return _.reduce(collection, function(allFound, item) {
return iterator(item) && allFound;
}, true);
};当您返回iterator(item) && allFound时所发生的是,如果iterator(item)是falsey (但不是false),它将不会返回false,而是返回iterator(item)的值。要验证这一点,请打开一个REPL,并输入undefined && true;结果将是undefined,而不是false。
因此,如果要显式地返回false,而不仅仅是falsey值,则必须将其强制转换为布尔值。您可以做Boolean(truthy_or_falsey_value)或!!truthy_or_falsey_value。我通常更喜欢后者,因此更改您的实现如下:
_.every = function(collection, iterator) {
return _.reduce(collection, function(allFound, item) {
return !!iterator(item) && allFound;
}, true);
};你的其他问题:
在调用迭代器函数时,需要使用iterator.call还是iterator.apply?如果是这样的话,我应该使用哪些参数,以及如何指定参数?
这取决于你的目标是什么。call和apply主要用于控制函数体中this关键字的值。JavaScript的一些内置数组方法(如Array.prototype.map和Array.prototype.filter)采用thisArg,这就是所提供的使用call或apply进行回调的方法。至于call和apply之间的区别,这只是参数的处理方式。有关更多详细信息,请参阅这个答案。
在这里使用
reduce而不仅仅是each有什么好处,特别是当Underscore.js库不使用reduce时?
可能一点也没有,或者很少。可能存在性能差异,但最好的方法是分析这两种方法。
为什么需要调用两次返回,一次在调用_.reduce函数时调用,一次在_.reduce中定义的匿名函数中调用
如果你想要一个函数--任何函数--返回一个值,你必须从这个函数中调用返回。您不能期望从内部函数调用return,并且期望封装函数能够神奇地理解它应该反过来返回被调用函数的值。如果没有显式调用return,某些语言默认返回函数中最后一个表达式的值,这是方便的,也可能是令人困惑的,这取决于您的透视图。如果您有使用这种语言的经验(例如,Ruby),那么对于您来说,所有的return语句都可能显得有些过分。
作为一篇社论,我觉得iterator是测试功能的一个糟糕的命名选择。它实际上并不是对任何东西进行迭代(作为参数的函数正在执行任何迭代)。更好的名称可能是非常通用的callback或cb。术语“谓语”是指将值映射到true或false的函数,这是我首选的术语。另一个常见的选择是简单的test,因为它毕竟只是一个对其参数执行二进制筛选的函数。
https://stackoverflow.com/questions/24298493
复制相似问题