Currying origins and the name of mathematician Haskell Curry (the programming language Haskell is also named after him).
Currying is also usually called partial evaluation. Its meaning is to pass parameters to a function step by step. After each parameter is passed, partially apply parameters, and return a more specific function to accept the remaining parameters. Multiple layers of such partial parameter functions can be nested in the middle until the final result is returned.
Therefore, the process of currying is a process of gradually passing parameters, gradually narrowing the scope of application of functions, and gradually solving them.
Currying a sum function
Following step-by-step evaluation, let's look at a simple example
var concat3Words = function (a, b, c) { return a+b+c; }; var concat3WordsCurrying = function(a) { return function (b) { return function (c) { return a+b+c; }; }; }; console.log(concat3Words("foo ","bar ","baza")); // foo bar baza console.log(concat3WordsCurrying("foo ")); // [Function] console.log(concat3WordsCurrying("foo ")("bar ")("baza")); // foo bar bazaAs you can see, concat3WordsCurrying("foo ") is a Function, each call returns a new function, which accepts another call, and then returns a new function until the result is finally returned, and the distribution is solved, and progresses layer by layer. (PS: The characteristics of closures are taken advantage of here)
So now we go further. If we require more than 3 parameters to be passed, we can pass as many parameters as possible and output the result when the parameters are not passed?
First, let’s have a normal implementation:
var add = function(items){ return items.reduce(function(a,b){ return a+b }); }; console.log(add([1,2,3,4]));But if you ask to multiply each number by 10 and then add it, then:
var add = function (items,multi) { return items.map(function (item) { return item*multi; }).reduce(function (a, b) { return a + b }); }; console.log(add([1, 2, 3, 4],10));Fortunately, there are map and reduce functions. If we follow this pattern, we need to add 1 to each item and summarize it, then we need to replace the functions in map.
Let’s take a look at the Curryization implementation:
var adder = function () { var _args = []; return function () { if (arguments.length === 0) { return _args.reduce(function (a, b) { return a + b; }); } [].push.apply(_args, [].slice.call(arguments)); return arguments.callee; } }; var sum = adder(); console.log(sum); // Function sum(100,200)(300); // The call format is flexible, one or more parameters can be entered at a time, and it supports chain call to sum(400); console.log(sum()); // 1000 (total calculation)The above adder is a Curry-shaped function, which returns a new function, and the new function can accept new parameters in batches, delaying until the last calculation.
General Curry function
A more typical currying will encapsulate the last calculation into a function, and then pass this function as a parameter into the currying function, which is clear and flexible.
For example, multiply each term by 10, we can pass the processing function as a parameter:
var currying = function (fn) { var _args = []; return function () { if (arguments.length === 0) { return fn.apply(this, _args); } Array.prototype.push.apply(_args, [].slice.call(arguments)); return arguments.callee; } }; var multi=function () { var total = 0; for (var i = 0, c; c = arguments[i++];) { total += c; } return total; }; var sum = currying(multi); sum(100,200)(300); sum(400); console.log(sum()); // 1000 (It is only calculated when blank calls)In this way, sum = currying(multi), the call is very clear, and the use effect is also brilliant. For example, to accumulate multiple values, you can use multiple values as parameters sum(1,2,3), or support chain calls, sum(1)(2)(3)
The foundation of currying
The above code is actually a high-order function. A high-order function refers to a function that operates a function. It receives one or more functions as parameters and returns a new function. In addition, the characteristics of closure are also relied on to save the parameters entered in the intermediate process. Right now:
Functions can be passed as parameters
Functions can be used as return value of function
Closure
The role of curryculation
Delay calculation. The above example is relatively low.
Parameter multiplexing. When the same function is called multiple times and the parameters passed are mostly the same, then the function may be a good candidate for currying.
Create functions dynamically. This can be dynamically generated after partial calculation results, on this basis, a new function is dynamically generated to process the subsequent business, thus omitting repeated calculations. Or you can dynamically create a new function by applying part of the subset of parameters to be passed into the calling function, which saves the parameters passed repeatedly (not necessarily every time in the future). For example, an auxiliary method for event browsers to add events:
var addEvent = function(el, type, fn, capture) { if (window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } else if (window.attachEvent) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }); } };Every time you add an event, you have to execute if...else.... In fact, in a browser, you only need to make a decision once. You can dynamically generate a new function based on the result after a judgment, and there is no need to recalculate it in the future.
var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, function(e) { fn.call(el, e); }, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, function(e) { fn.call(el, e); }); }; } })();This example, after the first judgment of if...else..., part of the calculation is completed, and a new function is created dynamically to process the parameters passed in later. This is a typical currylation.
The Function.prototype.bind method is also a Curry application
Unlike the call/apply method that executes directly, the bind method sets the first parameter to the context of function execution, and other parameters are passed to the calling method in turn (the body of the function itself does not execute, which can be regarded as delayed execution), and dynamically creates and returns a new function, which conforms to the currying characteristics.
var foo = {x: 888}; var bar = function () { console.log(this.x); }.bind(foo); // Bind bar(); // 888Below is a simulation of a bind function. testBind creates and returns a new function. In the new function, the function that really wants to execute the business is bound to the context passed in the actual parameter, and the execution is delayed.
Function.prototype.testBind = function (scope) { var fn = this; //// this points to a function that calls the testBind method, return function () { return fn.apply(scope); } }; var testBindBar = bar.testBind(foo); // Bind foo to delay execution of console.log(testBindBar); // Function (see, after bind, return a new function that delays execution) testBindBar(); // 888Here we should pay attention to the understanding of this in prototype.
The above article in-depth analysis of functions currying in JavaScript Currying is all the content I share with you. I hope you can give you a reference and I hope you can support Wulin.com more.