如何使用_.reduce(和_.each)从Underscore.js重写_.every / _。all

时间:2014-06-19 03:55:24

标签: javascript underscore.js

我正在努力重写许多标准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); }); 函数失败了上面显示的测试,以及其他多个测试(例如;混合的真/假值,未定义的值等):

- 调用迭代器函数时,是否需要使用_.everyiterator.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);
    }
  }
};

1 个答案:

答案 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,而不仅仅是假值,则必须将其强制转换为布尔值。您可以执行falseBoolean(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); }; 关键字的值时,主要使用callapply。 JavaScript的一些内置数组方法(如thisArray.prototype.map)采用Array.prototype.filter,这是使用thisArgcall进行回调的内容。至于applycall之间的差异,它只是如何处理参数。有关详细信息,请参阅this answer

  

在这里使用apply而不仅仅是reduce有什么好处,   特别是当Underscore.js库不使用each

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

  

为什么返回需要调用两次,一次调用时   _.reduce函数,一次在_.reduce

中定义的匿名函数内

如果你想要一个函数 - 任何函数 - 来返回一个值,你必须从该函数中调用return。你不能期望从内部函数调用reduce,并期望封闭函数神奇地理解它应该反过来返回被调用函数的值。如果未明确调用return,某些语言默认返回函数中最后一个表达式的值,这可能既方便又令人困惑,具体取决于您的观点。如果您有使用这种语言的经验(例如Ruby),那么所有return语句对您来说可能有些过分。

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