Underscore.js: как связать пользовательские функции - PullRequest
22 голосов
/ 15 октября 2010

Используя Underscore.js , я могу написать следующее, которое возвращает 42:

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

У меня есть пользовательская функция, не являющаяся частью Underscore.js, которая называется double():

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 ]

24 голосов
/ 16 октября 2010

Не найдя 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 на объектах вместо списков. Хотите поэкспериментировать с этим? Посмотрите этот пример на jsFiddle .

5 голосов
/ 25 ноября 2010

Хорошо, я впервые прочитал аннотированный исходный код подчеркивания.Но я думаю, что вы можете сделать что-то вроде этого:

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

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

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

Синтаксис / детали могут быть неправильными, но суть в следующем: когда вы вызываете _([42,43]), вы вызываете подчеркивание как функцию,Когда вы делаете это, он создает новый объект, а затем смешивает с этим объектом большинство функций подчеркивания.Затем он возвращает этот объект вам.Затем вы можете добавить свои собственные функции к этому объекту, и ничто из этого не загрязняет само пространство имен "_".

Именно так мне выглядел код underscore.js.Если я ошибаюсь, я хотел бы узнать, и, надеюсь, кто-то объяснит, почему.

РЕДАКТИРОВАТЬ: Я на самом деле интенсивно использую underscore.js в течение месяца, и я стал довольно знакомымс этим.Теперь я знаю он ведет себя так, как я сказал здесь.Когда вы вызываете _ как функцию конструктора, вы возвращаете свое собственное «пространство имен» (просто объект) и можете добавлять к нему вещи с помощью addToWrapper (), которые отображаются в вашем пространстве имен, но не в «глобальном» «_»Пространство имен.Таким образом, функция, к которой стремился OP, уже встроена. (И я был действительно впечатлен подчеркиванием, кстати, это очень очень хорошо сделано).

5 голосов
/ 16 октября 2010

Итак, у вас есть пользовательская функция:

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
0 голосов
/ 15 марта 2016

Похоже, что lodash реализовал именно то, что вы ищете:

_.thru(value, interceptor)

из документов:

Этот метод похож на _.tap за исключением того, что он возвращает результат перехватчик

https://lodash.com/docs#thru

0 голосов
/ 08 ноября 2014

Множество способов легко достичь этого, вот одно из таких решений:

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

Однако я бы порекомендовал использовать Ramda , тогда как с auto-curry и pipe / compose задачи такого рода тривиальны:

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

equivalent to:

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

Если вы хотите расширить решение, скажем, первые 2 элемента, а не одно значение, как требует ОП, тогда:

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

Однако в Ramda предпочтительнее R.compose. В любом случае, для таких тривиальных задач это, как правило, более удобно и просто в использовании.

0 голосов
/ 16 октября 2010

Использование compose - это еще один способ справиться с ситуацией, но я думаю, что добавление такой функции, как take , как я предлагал ранее , является лучшим решением.Тем не менее, вот как будет выглядеть код с compose:

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

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

Начальное значение должно быть предоставлено через функцию, которая возвращает это значение (здесь делается путем каррирования identity), ифункции должны быть перечислены в другой, что противоположно тому, что вы имеете с цепочкой, что кажется мне довольно неестественным.

0 голосов
/ 16 октября 2010

Работает ли map для этого?

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

edit

из документации, похоже, что map будет работать, только если вы поместите егоперед звонком на first:

_([42, 43]).chain()
    .map(double)
    .first()
    .value()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...