2008-11-26 9 views
52

Próbuję napisać funkcję JavaScript, która zwróci swój pierwszy argument (funkcja) z wszystkimi pozostałymi argumentami jako ustawionymi parametrami tej funkcji.Jak mogę wstępnie ustawić argumenty w wywołaniu funkcji JavaScript? (Zastosowanie częściowe)

Więc:

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

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

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

wynik byłby "hello world" dwukrotnie. dla pewnej implementacji settera

Wpadłem na problem z manipulowaniem tablicą argumentów przy mojej pierwszej próbie, ale wydaje się, że byłby lepszy sposób na zrobienie tego.

+0

Polecam funkcję 'curry' [Lodasha] (http://lodash.com/docs#curry). BTW Polecam całą bibliotekę, jest bardzo przydatna. –

Odpowiedz

96

Przede wszystkim trzeba częściowy - there is a difference between a partial and a curry - i tu jest wszystko, czego potrzebujesz, bez ram:

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

Teraz, za pomocą przykładu, można to zrobić dokładnie to, czego szukasz:

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

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

funkcja partial() mogłyby zostać wykorzystane do wdrożenia, ale nie jest Zmiękczanie. Oto cytat z a blog post on the difference:

Gdzie częściowy aplikacja wykonuje funkcję i od buduje funkcję, która trwa mniej argumentów, Zmiękczanie buduje funkcje, które podejmują wiele argumentów funkcji przez skład których każdy wziąć jeden argument.

Nadzieję, że pomaga.

+0

Czy mógłbyś to edytować? func.apply (this, allArguments) musi być zwracany func.apply (this, allArguments) – AlexH

+0

Gotowe - tak naprawdę nie wyglądało to poza twoją powracającą przykładową funkcję, więc zaniedbałem to pierwotnie. Dzięki. –

+0

Głosuję za twoją odpowiedzią właśnie dlatego, że wcześniej szukałem czegoś, aby to zrobić. –

3

Czy curried javascript jest tym, czego szukasz?

+0

Nie sądzę, że on potrzebuje curry, per se - patrz http://stackoverflow.com/questions/218025/what-is-the-difference-between-currying-and-partial-application –

+1

Link już nie działa. Czy to ta sama strona? http://www.crockford.com/javascript/www_svendtofte_com/code/curried_javascript/index.html –

2

Jeśli używasz Dojo, po prostu wywołaj funkcję dojo.hitch(), która robi prawie dokładnie to, co chcesz. Niemal —, ponieważ można go również wykorzystać do spakowania kontekstu. Ale Twój przykład jest pierwszy:

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

Jak również:

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() jest częścią Dojo Bazy, tak szybko, jak jest to ujęte dojo.js tam ty.

Inny ogólny obiekt jest dostępny w module dojox.lang.functional.curry (udokumentowanym w Functional fun in JavaScript with Dojo — po prostu spójrz na tę stronę dla "curry"). W szczególności możesz przyjrzeć się curry() i partial().

curry() kumuluje argumenty (jak w twoim przykładzie), ale z jedną różnicą: gdy tylko arion jest spełniony, wywołuje funkcję zwracającą wartość. Wdrożenie swój przykład:

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

Zauważ, że ostatni wiersz nie ma „()” na końcu — nazywa się automatycznie.

częściowy() pozwala zastąpić argumenty losowo:

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

** EDIT: Patrz odpowiedź Jason Bunting za. Ta odpowiedź pokazuje w istocie podrzędny sposób łączenia wielu połączeń wychodzących, a nie pojedyncze wywołanie z ustawieniami wstępnymi dla niektórych argumentów. Jeśli ta odpowiedź faktycznie pomaga w podobnym problemie, powinieneś upewnić się, że użyjesz aplikacji i wywołań, jak zaleca Jason, zamiast niejasnego sposobu użycia ewalu, które wymyśliłem. **

Cóż ... Twój się rzeczywiście napisać „nieokreśloną” dużo w tym ... ale to powinien być zbliżony do tego, co chcesz:

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

pewnie bym przenieść kod w getArgString do samej funkcji ustawiającej ... trochę bezpieczniej, ponieważ użyłem "eval".

+0

Właściwy pomysł, ale straszna implementacja. Zobacz moją odpowiedź - nie musisz budować rzeczy jako napisów, JavaScript ma funkcję call() i apply(), które ułatwiają takie działania. –

+0

Huh ... nigdy nie słyszałem o wywołaniu lub zastosowaniu funkcji wcześniej - te ułatwiają. – Illandril

+0

Proponuję spędzić trochę czasu, czytając o nich - podczas gdy oni mają bardzo niszowe zastosowanie, są bardzo przydatne dla tych niszowych zastosowań! –

0

Można użyć do tego celu Function.prototype.bind(). To dodatek do ES5.

Oprócz zwykłej funkcji ustawiania kontekstu funkcji (wartość this), może również ustawiać częściowe argumenty.

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 Moja funkcja jest rzeczywiście bardzo prosty. Najdłuższą częścią jest po prostu lista argumentów. Będę zerwać kodu:

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 

jest obsługiwany w these browsers: Chrome 7+, Firefox 4+, IE9 +. MDN (połączony na początku) ma jednak polyfill.

0
function myLog(arg1,arg2,arg3){ 
    return function(){ 
     //you can use the arguments from the parent scope 
     console.log(arg1+" "+arg2+" "+arg3); 
    }; 
} 
var passMeAnywhere = myLog(1,2,3); 
//execute the function, args will be in the immediate lexical scope. 
passMeAnywhere(); 
//One of the previous posts showed a way for context binding 
//Context binding allows any context 
//New is not required with binding 
+0

nie działa. sam to wypróbowałeś? –

1

Korzystanie JavaScript na apply() można modyfikować function prototype

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

Następnie można nazwać jako out.pass('hello','world')

apply pobiera tablicę dla argumentu 2/parametr.

arguments to właściwość dostępna wewnątrz funkcji, która zawiera wszystkie parametry w strukturze tablicowej.

Innym powszechnym sposobem aby to zrobić jest użycie bind

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

następnie

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

to trochę wystarczyć, choć trochę brzydki.

+1

Myślę, że powinieneś dodać właściwe wyjaśnienie, jak to działa; Należy również podać przykład funkcji PO. – sintakonte

+1

Wiele osób jest przeciwnych modyfikacjom bazowych prototypów do banalnych celów, ale ten wydaje się idealny, zachowując czytelność w całym kodzie. –

+0

Osobiście uważam, że właśnie użyłem binda w większości przypadków, ale niech będzie przykład dodawania –

Powiązane problemy