#javascript
#javascript
Вопрос:
Я хочу функцию, похожую на «карри», — такого рода вещи
function invoker (fn) {
var slice = Array.prototype.slice,
args = slice.apply(arguments, [1]);
return function () {
return fn.apply(null, args);
};
}
Но я хочу, чтобы пользователь мог выполнять
invoker(f)
или
invoker(foo.bar)
Я не могу найти правильное магическое заклинание для этого. Все примеры, которые я вижу, требуют, чтобы объект области передавался отдельно; что подвержено ошибкам и неестественно. IE
invokerx(foo.bar, foo)
Есть ли способ сделать это? Я не против иметь 2 разные функции
invokeG(f)
invokeO(foo.bar)
РЕДАКТИРОВАТЬ: уточнение
'f' is a global scope function
'foo' is an object
'bar is a method on that object
Т.е. я хочу, чтобы этот инструмент curry работал как со «свободными» функциями, так и с объектными функциями.
Необходимость идти
<curry function>(foo.bar,foo)
кажется довольно неуклюжим, я должен сказать «foo» дважды
Комментарии:
1. Выражение «Я хочу, чтобы пользователь мог выполнять
invoker(f)
илиinvoker(foo.bar)
» не описывает, чего вы хотите. Пожалуйста, опишите словами проблему, которую вы пытаетесь решить. РЕДАКТИРОВАТЬ: может быть, вы хотите, чтобы пользователь мог привязывать контекст к функции?2. Что такое
f
иfoo.bar
в этом случае?
Ответ №1:
Первый аргумент apply
— это тот this
, который доступен для функции. Поскольку вы передаете null
, this
он будет сопоставлен window
.
Будет работать следующая модификация:
function invoker (fn, target) {
var slice = Array.prototype.slice,
args = slice.apply(arguments, [2]);
if(typeof fn === 'string')
fn = (target || window)[fn];
return function () {
return fn.apply(target || null, args);
};
}
// Either will work:
invoker(foo.bar, foo);
invoker('bar', foo);
// Any of these will work:
invoker(escape, null);
invoker(escape, window);
invoker('escape', null);
invoker('escape', window);
ОБНОВЛЕНИЕ добавило небольшое изменение для добавления поддержки передачи имен, поэтому вам не нужно передавать имя объекта дважды.
ОБНОВЛЕНИЕ 2 Поскольку мы выполняем каррирование, нам нужно, чтобы всегда было два аргумента.
Комментарии:
1. правильно, но для этого требуется, чтобы вызывающий абонент использовал 2 разные подписи вызовов. Я чувствую, что вызывающий (foo.bar, foo) является избыточным, я уже сказал foo один раз
2. Вы это знаете, и я это знаю, но JavaScript этого не знает. Я добавлю альтернативное решение, которое не требует нескольких экземпляров foo, но оно будет менее эффективным.
3. привет, Брайан, еще одно героическое усилие, и я уверен, что ты сможешь найти действительно хорошее решение. Вы довольно близко подходите к ‘invoker (‘bar’, foo), но мы все еще не совсем там 🙂
4. @pm100: если не делать что-то ужасное, например
invoker('foo.bar')
, это настолько близко, насколько это возможно5. Во-первых, это будет работать только в том случае, если foo является глобальной переменной. Во-вторых, для этого потребуется использовать
eval
(страшно / медленно) или синтаксический анализ строки (медленно). На практике существует множество примеров функций curry, привязанных к объектам, и всем им требуется второй параметр, что часто приводит к вводу имени объекта дважды. Именно так работает JavaScript.
Ответ №2:
Вы не можете. Когда вы это делаете invoker(foo.bar)
, invoker
невозможно узнать, что аргумент является функцией-членом foo
. Функции не хранят своего «владельца», а invoker
просто передают ссылку на функцию.
Самое близкое, что вы можете получить, это использовать function.bind()
, например:
invoker(foo.bar.bind(foo))
Комментарии:
1. Я думаю, это все, я хотел найти «владельца» функции. Я удивлен, что у apply нет какого-либо варианта, который позволяет это.
2. На самом деле,
.bind
функция является функцией каррирования, поэтому вам здесь не нужноinvoker
.var boundBar = foo.bar.bind(foo)
это все, что вам нужно. В противном случае вы выполняете каррированную функцию. Смотрите: developer.mozilla.org/en/JavaScript/Reference/Global_Objects /…