Как я могу предварительно установить аргументы в вызове функции JavaScript? (Частичное Применение Функции)


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

Так:

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

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

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

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

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

6 53

6 ответов:

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

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");

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

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

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

и Карри javascript то, что вы ищете?

если вы используете Додзе вы просто называете додзе.hitch () это делает почти точно то, что вы хотите. Почти-потому что он может быть использован для упаковки контекста, а также. Но ваш пример первый:

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")();

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

еще один общий объект доступен в dojox.ленг.функциональная.модуль Карри (документирован в функциональная забава в JavaScript с Додзе - просто посмотрите на этой странице для "карри"). В частности, вы можете посмотреть на curry () и partial().

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

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

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

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

df.partial(out, df.arg, "world")("hello");

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

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

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

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

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

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

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

затем

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

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

** Изменить: см. ответ Джейсона Бантинга. Этот ответ фактически показывает суб-парный способ цепочки многочисленных исходящих вызовов, а не один выходящий вызов с предустановками для некоторых аргументов. Если этот ответ действительно помогает с подобной проблемой, вы должны обязательно использовать apply и call, как рекомендует Джейсон, а не неясный способ использования eval, который я придумал. **

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

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... немного безопаснее, так как я использовал 'ивала.

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

в дополнение к общему usecase установки контекста функции (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")();

мой