首页 > 代码库 > JavaScript Patterns 4.10 Curry
JavaScript Patterns 4.10 Curry
Function Application
apply() takes two parameters: the first one is an object to bind to this inside of the function, the second is an array or arguments, which then becomes the array-like arguments object available inside the function. If the first parameter is null, then this points to the global object, which is exactly what happens when you call a function that is not a method of a specific object.
// define a functionvar sayHi = function(who) { return "Hello" + ( who ? ", " + who : "") + "!";};// invoke a functionsayHi();// "Hello"sayHi(‘world‘);// "Hello, world!"// apply a functionsayHi.apply(null, ["hello"]);// "Hello, hello!"//-----------------------------------------------------------------------var alien = { sayHi : function(who) { return "Hello" + ( who ? ", " + who : "") + "!"; }};alien.sayHi(‘world‘);// "Hello, world!"sayHi.apply(alien, ["humans"]);// "Hello, humans!"
In the preceding snippet, this inside of sayHi() points to alien. In the previous example this points to the global object.
When you have a function that takes only one parameter, you can save the work of creating arrays with just one element:
// the second is more efficient, saves an arraysayHi.apply(alien, ["humans"]); // "Hello, humans!"sayHi.call(alien, "humans"); // "Hello, humans!"
Partial Application
var add = function(x, y) { return x + y;};// full applicationadd.apply(null, [5, 4]);// 9// partial applicationvar newadd = add.partialApply(null, [5]);// applying an argument to the new functionnewadd.apply(null, [4]);// 9
Here’s no partialApply() method and functions in JavaScript don’t behave like this by default. But you can make them, because JavaScript is dynamic enough to allow this. The process of making a function understand and handle partial application is called currying.
// a curried add()// accepts partial list of argumentsfunction add(x, y) { var oldx = x, oldy = y; if ( typeof oldy === "undefined") {// partial return function(newy) { return oldx + newy; }; } // full application return x + y;}// testtypeof add(5);// "function"add(3)(4);// 7// create and store a new functionvar add2000 = add(2000);add2000(10);// 2010
General-purpose currying function
function schonfinkelize(fn) { var slice = Array.prototype.slice, stored_args = slice.call(arguments, 1); return function() { var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null, args); };}// a normal functionfunction add(x, y) { return x + y;}// curry a function to get a new functionvar newadd = schonfinkelize(add, 5);newadd(4);// 9// another option -- call the new function directlyschonfinkelize(add, 6)(7);// 13// a normal functionfunction add(a, b, c, d, e) { return a + b + c + d + e;}// works with any number of argumentsschonfinkelize(add, 1, 2, 3)(5, 5);// 16// two-step curryingvar addOne = schonfinkelize(add, 1);addOne(10, 10, 10, 10);// 41var addSix = schonfinkelize(addOne, 2, 3);addSix(5, 5);// 16
When to Use Currying
When you find yourself calling the same function and passing mostly the same parameters, then the function is probably a good candidate for currying. You can create a new function dynamically by partially applying a set of arguments to your function. The new function will keep the repeated parameters stored (so you don’t have to pass them every time) and will use them to pre-fill the full list of arguments that the original function expects.