javascript curry / частичный вызов функции для объектных функций

#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 /…