Underscore.js:如何链接自定义函数

时间:2010-10-15 19:59:44

标签: javascript functional-programming underscore.js

使用Underscore.js,我可以编写以下内容,返回42

_([42, 43]).chain()
    .first()
    .value()

我有自定义功能,而不是名为double()的Underscore.js的一部分:

function double(value) { return value * 2; };

我希望能够在Underscore链中调用此函数,就像它是Underscore的一部分一样。我想写下面的内容,我想返回84

_([42, 43]).chain()
    .first()
    .double()
    .value()

这不起作用,因为Underscore没有定义double()。我可以使用tap(),如下所示:

_([42, 43]).chain()
    .first()
    .tap(double)
    .value()

这是有效的,但tap将函数应用于其参数并返回参数,而不是函数的结果。因此,在我看来,我需要一种tap来返回应用于其参数的函数的结果。在Underscore.js中有这样的东西吗?我错过了一些非常明显的东西吗?

7 个答案:

答案 0 :(得分:24)

找不到返回函数返回值的tap运行,我定义一个take并添加到_

_.mixin({take: function(obj, interceptor) {
    return interceptor(obj);
}});

然后假设我有:

function double(value) { return value * 2; };

我可以写:

_([42, 43]).chain()
    .first()             // 42
    .take(double)        // Applies double to 42
    .value()             // 84

您可以在对象上查看take map,而不是列表。想试试这个吗?请参阅此example on jsFiddle

答案 1 :(得分:5)

所以你有一个自定义功能:

function double(value) { return value * 2; }

您可以使用mixin扩展Underscore:

_.mixin({ double:double });

现在您可以从Underscore对象_调用您的函数:

_.double(42); // 84

并从chain返回的包装对象:

_([42, 43]).chain()
  .first()
  .double() // double made it onto the wrapped object too
  .value(); // 84

答案 2 :(得分:5)

好吧,我刚刚第一次阅读下划线注释源代码。但我认为你可以这样做:

function double(value) { return value * 2; };

var obj = _([42, 43]).addToWrapper({double:double});

obj.chain()
  .first()
  .double()
  .value();

语法/详细信息可能不正确,但核心问题是:当您调用_([42,43])时,您将下划线作为函数调用。执行此操作时,它会实例化一个新对象,然后将大多数下划线函数混合到该对象中。然后,它将该对象返回给您。然后,您可以将自己的函数添加到该对象,并且这些函数都不会污染“_”命名空间本身。

这就是下划线.js代码对我来说的样子。如果我错了,我想找出答案,希望有人会解释原因。

编辑:我实际上已经大量使用了underscore.js大约一个月了,而且我对它非常熟悉。我现在知道它的行为就像我在这里所说的那样。当您将_作为构造函数调用时,您将获得自己的“命名空间”(只是一个对象),并且您可以使用addToWrapper()向其添加内容,这些内容显示在您的命名空间中但不会显示在“global”“_”中命名空间。所以OP想要的功能已经内置了。(而且我对下划线印象非常深刻,顺便说一下,它的表现非常出色)。

答案 3 :(得分:0)

很容易实现这一目标的方法,这就是一个这样的解决方案:

  _.chain([42,43])
    .first(1)
    .map(double)
    .first()
    .value();
    // 84

但是,我建议使用Ramda然后使用自动咖喱和管道/撰写这些类型的任务是微不足道的:

R.pipe(R.head, R.multiply(2))([42, 43]);  // 84

equivalent to:

R.compose(R.multiply(2), R.head)([42, 43]);  // 84

如果你想扩展解决方案,根据OP的要求,先取两个项目,而不是单个值,那么:

R.pipe(R.take(2), R.map(R.multiply(2)))([42, 43])  // [84, 86]

然而,在Ramda R.compose是优选的。无论哪种方式,对于像这样的琐碎任务,它往往更方便,更容易使用。

答案 4 :(得分:0)

看起来lodash已经实现了您正在寻找的东西:

_.thru(value, interceptor)

来自文档:

  

此方法与_.tap类似,只是它返回结果   拦截器

https://lodash.com/docs#thru

答案 5 :(得分:-1)

map是否适用于此?

_([42, 43]).chain()
    .first()
    .map(double)
    .value()

修改

从文档中看来map只有在调用first之前放置它才会起作用:

_([42, 43]).chain()
    .map(double)
    .first()
    .value()

答案 6 :(得分:-1)

使用compose是处理这种情况的另一种方法,但我认为添加take as I suggested earlier这样的函数是更好的解决方案。不过,以下是使用compose代码的代码:

function double(value) { return value * 2; };

_.compose(
    double,
    _.first,
    _.bind(_.identity, _, [42, 43])
)();

初始值需要通过返回该值的函数提供(此处通过currying identity完成),并且函数需要列在另一个函数中,这与您对链的相反对我来说这看起来很不自然。