Javascript 如何使用0.reduce(和0.each)从underline.js重新写入0.every/0.all

Javascript 如何使用0.reduce(和0.each)从underline.js重新写入0.every/0.all,javascript,underscore.js,Javascript,Underscore.js,我正在为许多标准的underline.js函数重新编写底层代码,以提高我的JavaScript技能,我有点被.

我正在为许多标准的underline.js函数重新编写底层代码,以提高我的JavaScript技能,我有点被
.
/
.\uAll
卡住了。似乎在库本身中,
.each
/
.all
函数只使用现有的
.each
函数编写,但我被鼓励使用我的
.reduce
版本编写一个版本(它已经包含了我的
.each
)。我已经为下面两个函数提供了代码

第一个测试my
。每个
函数(也请参见下文)都失败,其中所有的假值都是使用
\.identity
函数(仅返回作为参数输入的值)作为迭代器传入的:

测试:

我有几个问题想知道为什么我的
\。每个
函数都没有通过上面显示的测试,以及其他多项测试(例如:真/假混合值、未定义值等):

-调用迭代器函数时,是否需要使用
iterator.call
iterator.apply
?如果是的话,我应该使用哪种参数,如何指定参数

-在这里使用
.reduce
比只使用
.each
有什么好处,特别是当underline.js库不使用
.reduce

-为什么需要调用return两次,一次是在调用
.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);
    }
  }
};
\每个=函数(集合、迭代器){
//定义阵列的规范
if(数组.isArray(集合)){
对于(变量i=0;i
_.减少:

  _.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;
  };
\uu0.reduce=函数(集合、迭代器、累加器){
//如果未给出明确的起始值,则添加设置累加器的条件。
如果(参数长度<3){
累加器=集合[0];
}
_.每个(集合、函数(值){
累加器=迭代器(累加器,值);
});
回流蓄能器;
};

您的测试没有通过,因为它没有按预期返回
false
(尽管它返回了一个false值)

返回
iterator(item)&&allFound
时,如果
iterator(item)
为false(但不是
false
),则不会返回
false
,而是返回
iterator(item)
的值。要亲自验证这一点,请打开一个REPL,然后键入
undefined&&true
;结果将是
未定义
,而不是
错误

因此,如果希望它显式返回
false
,而不仅仅是一个false值,那么必须将其强制为布尔值。您可以执行
布尔(truthy\u或\u false\u值)
!!真实值或虚假值
。我通常更喜欢后者,因此请更改您的实现:

_.every = function(collection, iterator) {
    return _.reduce(collection, function(allFound, item) {
        return !!iterator(item) && allFound;
    }, true);
};
你的其他问题:

调用迭代器函数时,是否需要使用iterator.call或 迭代器.apply?如果是的话,我应该使用哪种参数,如何指定参数

这取决于你的目标是什么
call
apply
主要用于控制函数体中
this
关键字的值。JavaScript的一些内置数组方法(如
array.prototype.map
array.prototype.filter
)采用
thisArg
,这就是提供的使用
调用
应用
进行回调的方法。至于
call
apply
之间的区别,那只是参数的处理方式。有关更多详细信息,请参阅

在这里使用
reduce
而不仅仅是
每个
有什么好处,
尤其是当underline.js库不使用
reduce

可能没有,或者很少。可能存在性能差异,但最好的方法是分析这两种方法

为什么需要调用return两次,调用 _.reduce函数,并且一旦在u.reduce中定义的匿名函数中

如果您想要一个函数——任何函数——返回一个值,那么必须从该函数中调用return。您不能期望从内部函数调用
return
,而期望封闭函数神奇地理解它应该反过来返回被调用函数的值。如果未显式调用
return
,则某些语言默认返回函数中最后一个表达式的值,这可能是方便的,也可能是混乱的,具体取决于您的角度。如果您有使用这种语言的经验(例如Ruby),那么所有的
return
语句对您来说可能有点过分

作为编辑说明,我觉得对于测试函数来说,
iterator
是一个糟糕的命名选择。它实际上并不是迭代任何东西(作为参数的函数正在进行任何迭代)。更好的名称可能是非常通用的
回调
cb
。该术语表示将值映射到
true
false
的函数,这是我首选的术语。另一个常见的选择是简单的
test
,因为它毕竟只是一个对其参数执行二进制筛选的函数

_.every = function(collection, iterator) {
    return _.reduce(collection, function(allFound, item) {
        return iterator(item) && allFound;
    }, true);
};
_.every = function(collection, iterator) {
    return _.reduce(collection, function(allFound, item) {
        return !!iterator(item) && allFound;
    }, true);
};