首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Underscore.js (和_.each)重写_.reduce _..every/_..all

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

Stack Overflow用户
提问于 2014-06-19 03:55:23
回答 1查看 6.7K关注 0票数 5

我正在重新编写许多标准Underscore.js函数的底层代码,以提高我的JavaScript技能,并且有点被_.every/_.all所困扰。在库本身中,_.every/_.all函数似乎只使用现有的_.each函数编写,但有人鼓励我使用自己的_.reduce版本编写一个版本(该版本已经包含了我的_.each版本)。我在下面为这两个函数提供了代码。

我的_.every函数的第一个测试(也见下文)失败了,其中使用_.identity函数传递所有假值(只返回作为参数输入的值)作为迭代器:

测试:

代码语言:javascript
复制
  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:

代码语言:javascript
复制
  _.every = function(collection, iterator) {
    // TIP: Try re-using reduce() here.
    return _.reduce(collection, function(allFound, item) {
      return iterator(item) && allFound;
    }, true);
  };

_.each:

代码语言:javascript
复制
_.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:

代码语言:javascript
复制
  _.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;
  };
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-06-19 04:57:20

您的测试没有通过,因为它没有像预期的那样返回false (尽管它返回的是falsey值)。

代码语言:javascript
复制
_.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。我通常更喜欢后者,因此更改您的实现如下:

代码语言:javascript
复制
_.every = function(collection, iterator) {
    return _.reduce(collection, function(allFound, item) {
        return !!iterator(item) && allFound;
    }, true);
};

你的其他问题:

在调用迭代器函数时,需要使用iterator.call还是iterator.apply?如果是这样的话,我应该使用哪些参数,以及如何指定参数?

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

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

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

为什么需要调用两次返回,一次在调用_.reduce函数时调用,一次在_.reduce中定义的匿名函数中调用

如果你想要一个函数--任何函数--返回一个值,你必须从这个函数中调用返回。您不能期望从内部函数调用return,并且期望封装函数能够神奇地理解它应该反过来返回被调用函数的值。如果没有显式调用return,某些语言默认返回函数中最后一个表达式的值,这是方便的,也可能是令人困惑的,这取决于您的透视图。如果您有使用这种语言的经验(例如,Ruby),那么对于您来说,所有的return语句都可能显得有些过分。

作为一篇社论,我觉得iterator是测试功能的一个糟糕的命名选择。它实际上并不是对任何东西进行迭代(作为参数的函数正在执行任何迭代)。更好的名称可能是非常通用的callbackcb。术语“谓语”是指将值映射到truefalse的函数,这是我首选的术语。另一个常见的选择是简单的test,因为它毕竟只是一个对其参数执行二进制筛选的函数。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24298493

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档