Как я могу предварительно установить аргументы в вызове функции JavaScript? (Частичное применение функции) - PullRequest
53 голосов
/ 26 ноября 2008

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

Итак:

function out(a, b) {
    document.write(a + " " + b);
}

function setter(...) {...}

setter(out, "hello")("world");
setter(out, "hello", "world")();

Выводит "hello world" дважды. для некоторой реализации сеттера

Я столкнулся с проблемой манипулирования массивом аргументов с первой попытки, но, похоже, есть лучший способ сделать это.

Ответы [ 6 ]

96 голосов
/ 26 ноября 2008

Прежде всего, вам нужен частичный - есть разница между частичным и карри - и вот все, что вам нужно, без рамки :

function partial(func /*, 0..n args */) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    var allArguments = args.concat(Array.prototype.slice.call(arguments));
    return func.apply(this, allArguments);
  };
}

Теперь, используя ваш пример, вы можете делать именно то, что вам нужно:

partial(out, "hello")("world");
partial(out, "hello", "world")();

// and here is my own extended example
var sayHelloTo = partial(out, "Hello");
sayHelloTo("World");
sayHelloTo("Alex");

Функцию partial() можно использовать для реализации, но не является каррированием. Вот цитата из сообщения в блоге о разнице :

Если частичное приложение берет функцию и из нее строит функцию, которая принимает меньше аргументов, каррирование строит функции, которые принимают несколько аргументов, по составу функций, каждый из которых принимает один аргумент.

Надеюсь, это поможет.

3 голосов
/ 26 ноября 2008

Является ли карри javascript , что вы ищете?

2 голосов
/ 15 декабря 2017

Используя Javascript apply(), вы можете изменить function prototype

Function.prototype.pass = function() {
    var args = arguments,
        func = this;
    return function() {
        func.apply(this, args);
    }
};

Вы можете затем назвать его как out.pass('hello','world')

apply принимает массив для второго аргумента / параметра.

arguments - свойство, доступное внутри функции, которая содержит все параметры в массиве, как структура.

Еще один распространенный способ сделать это - использовать bind

loadedFunc = func.bind(this, v1, v2, v3);

1021 * тогда *

loadedFunc() === this.func(v1,v2,v3);

этого достаточно, хотя и немного некрасиво.

2 голосов
/ 26 ноября 2008

Если вы используете Dojo, вы просто вызываете dojo.hitch (), который делает почти то, что вы хотите. Почти & mdash; потому что он также может быть использован для упаковки контекста. Но ваш пример первый:

dojo.hitch(out, "hello")("world");
dojo.hitch(out, "hello", "world")();

А также:

var A = {
  sep: ", ",
  out: function(a, b){ console.log(a + this.sep + b); }
};

// using functions in context    
dojo.hitch(A, A.out, "hello")("world");
dojo.hitch(A, A.out, "hello", "world")();

// using names in context
dojo.hitch(A, "out", "hello")("world");
dojo.hitch(A, "out", "hello", "world")();

dojo.hitch () является частью базы Додзё, поэтому, как только вы включите dojo.js, она для вас.

Другое общее средство доступно в модуле dojox.lang.functional.curry (задокументировано в Функциональное развлечение в JavaScript с помощью Dojo & mdash; просто посмотрите на эту страницу "curry"). В частности, вы можете захотеть взглянуть на curry () и part ().

curry () накапливает аргументы (как в вашем примере), но с одним отличием: как только арность удовлетворяется, она вызывает функцию, возвращающую значение. Реализация вашего примера:

df.curry(out)("hello")("world");
df.curry(out)("hello", "world");

Обратите внимание, что в конце последней строки нет "()" & mdash; он вызывается автоматически.

partal () позволяет произвольно заменять аргументы:

df.partial(out, df.arg, "world")("hello");
1 голос
/ 19 июня 2015

Вы можете использовать для этого Function.prototype.bind(). Это дополнение ES5.

В дополнение к общему варианту установки контекста функции (значение this), он также может устанавливать частичные аргументы.

function out(a, b) {
  document.write(a + " " + b);
}

function setter(func) {
  return func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1)));
}

setter(out, "hello")("world");
setter(out, "hello", "world")();

Моя setter функция на самом деле очень проста. Самая длинная часть - это просто получение списка аргументов. Я разобью код следующим образом:

func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1)))
func.bind.apply(                                                        )  // need to use apply to pass multiple arguments as an array to bind()
                func,                                                      // apply needs a context to be run in
                      [window].concat(                                 )   // pass an array of arguments to bind(), starting with window, to be the global context
                                      [].slice.call(arguments).slice(1)    // convert the arguments list to an array, and chop off the initial value

Поддерживается в этих браузерах : Chrome 7+, Firefox 4+, IE9 +. Хотя MDN (связанный в начале) имеет полифил.

0 голосов
/ 26 ноября 2008

** РЕДАКТИРОВАТЬ: См. Ответ Джейсона Бантинга. Этот ответ на самом деле показывает неконкурентный способ объединения множества исходящих вызовов, а не один исходящий вызов с предустановками для некоторых аргументов. Если этот ответ действительно помогает с подобной проблемой, вы должны обязательно использовать apply и call, как рекомендует Джейсон, вместо неясного способа использовать eval, который я придумал. **

Ну ... на самом деле ваш аут будет много писать "undefined" в этом ... но это должно быть близко к тому, что вы хотите:

function out(a, b) {
    document.write(a + " " + b);
}

function getArgString( args, start ) {
    var argStr = "";
    for( var i = start; i < args.length; i++ ) {
        if( argStr != "" ) {
            argStr = argStr + ", ";
        }
        argStr = argStr + "arguments[" + i + "]"
    }
    return argStr;
}

function setter(func) {
    var argStr = getArgString( arguments, 1 );
    eval( "func( " + argStr + ");" );
    var newSettter = function() {
        var argStr = getArgString( arguments, 0 );
        if( argStr == "" ) {
            argStr = "func";
        } else {
            argStr = "func, " + argStr;
        }
        return eval( "setter( " + argStr + ");" );
    }
    return newSettter;
}

setter(out, "hello")("world");
setter(out, "hello", "world")();

Я бы, вероятно, переместил код в getArgString в саму функцию setter, хотя ... немного безопаснее, так как я использовал 'eval'.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...