Underscore.js: как настроить пользовательские функции
Используя 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? Я пропустил что-то ужасно очевидное?
Ответы
Ответ 1
Не найдя 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.
Ответ 2
Итак, у вас есть пользовательская функция:
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
Ответ 3
Хорошо, я впервые прочитал аннотированный исходный код подчеркивания в первый раз. Но я думаю, вы можете сделать что-то вроде этого:
function double(value) { return value * 2; };
var obj = _([42, 43]).addToWrapper({double:double});
obj.chain()
.first()
.double()
.value();
Синтаксис/детали могут быть неправильными, но основная точка заключается в следующем: когда вы вызываете _([42,43])
, вы вызываете подчеркивание как функцию. Когда вы это делаете, он создает новый объект, а затем смешивает с этим объектом большинство функций подчеркивания. Затем он возвращает этот объект вам. Затем вы можете добавить свои собственные функции к этому объекту, и ничто из этого не загрязняет пространство имен "_".
Что мне понравился код underscore.js. Если я ошибаюсь, я бы хотел узнать, и, надеюсь, кто-то объяснит, почему.
EDIT: я действительно использовал underscore.js в течение примерно месяца, и я хорошо знаком с ним. Теперь я знаю, что это ведет себя так, как я сказал здесь. Когда вы вызываете _ как функцию конструктора, вы возвращаете свое собственное "пространство имен" (просто объект), и вы можете добавлять к нему вещи с помощью addToWrapper(), которые отображаются в вашем пространстве имен, но не в "глобальном" "_", Пространство имен. Таким образом, функция, которую захочет OP, уже встроена. (И я был действительно впечатлен подчеркиванием, кстати, это очень хорошо сделано).
Ответ 4
Никто на самом деле не ответил на это по просьбе, поэтому я ответил на запрос, который я тестировал.
В первую очередь ознакомьтесь с примечанием ниже.
function doubleValue(x) { return x * 2; }
var rt = _.chain([42,43])
.first(1)
.map(doubleValue)
.value();
console.log(rt); // prints out [84]
//Обратите внимание, что важно указать .first(), сколько элементов вы хотите, иначе вы можете получить пустой массив в противном случае. Сначала подумайте, как я ХОЧУ первое количество элементов, поэтому .first(2) возвращает [84], [86] и т.д.
Ответ 5
Многие способы легко достичь этого, вот одно из таких решений:
_.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
Если вы хотите расширить решение, чтобы взять первые 2 элемента, вместо одного значения, как запрошено OP, тогда:
R.pipe(R.take(2), R.map(R.multiply(2)))([42, 43]) // [84, 86]
Однако, в Ramda R.compose предпочтительнее. В любом случае, для таких тривиальных задач, как это, он, как правило, более удобен и прост в использовании.
Ответ 6
Похоже, что lodash внедрил именно то, что вы ищете:
_.thru(value, interceptor)
из документов:
Этот метод похож на _.tap, за исключением того, что он возвращает результат Перехватчик
https://lodash.com/docs#thru
Ответ 7
Работает ли map
для этого?
_([42, 43]).chain()
.first()
.map(double)
.value()
изменить
из документации, похоже, что map
будет работать, только если вы поместите его перед вызовом first
:
_([42, 43]).chain()
.map(double)
.first()
.value()
Ответ 8
Использование compose
- это еще один способ справиться с ситуацией, но я думаю, что добавление функции, такой как take
, как я предложил ранее, является лучшим решением. Тем не менее, вот как выглядит код с помощью compose
:
function double(value) { return value * 2; };
_.compose(
double,
_.first,
_.bind(_.identity, _, [42, 43])
)();
Начальное значение должно быть предоставлено через функцию, которая возвращает это значение (здесь выполняется currying identity
), а функции должны быть перечислены в другом, что является обратным тому, что у вас есть с цепочкой, что представляется мне довольно неестественным.