Related recommendations: javascript tutorial
apply(context,[arguments])
, call(context,param1,param2,...)
.Currying is a technique that transforms a function that accepts multiple parameters into a function that accepts a single parameter (the first parameter of the original function), and returns a new function that accepts the remaining parameters and returns a result.
For example, here is an add()
function, which is a function used to process the addition and sum of the parameters (param1, params2,...) we pass to it.
// Here is the first `add(x, y)` function with two parameters `x`, `y` function add(x, y){ return x + y; } // Call the `add()` function and give two parameters `4` and `6` add(4,6); // Simulate computer operation, pass in the first parameter 4 in the first step function add(4, y){ return 4 + y; } // Simulate computer operation, pass in the first parameter 6 in the second step function add(4, 6){ return 4 + 6; }
What would it look like if we curried the add()
function? Here is a simple implementation:
// The curried add() function can accept some parameters function add(x,y){ if (typeof y === 'undefined') { return function (newy){ return x + newy; } } // Full application return x + y; } // Test calls console.log(typeof add(4)); // [Function] console.log(add(4)(6)); // 10 // You can create a save function let saveAdd = add(4); console.log(saveAdd(6)); // 10
As can be seen from the above simple curried add()
function, the function can accept some functions and then return a new function to continue processing the remaining functions.
Here we create a public currying function, so that we don’t have to implement the complex currying process inside it every time we write a function.
//Define a createCurry function function createCurry(fn){ var slice = Array.prototype.slice, stored_args = slice.call(arguments,1); return function () { let new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null,args); }}
In the above public currying function:
arguments
is not a real array, but an object with a length
attribute, so we borrow the slice
method from Array.prototype
to help us convert arguments
into a real array, To facilitate our better operation.createCurry
for the first time, the variable stored_args
holds the parameters except the first parameter, because the first parameter is the function we need to curry.createCurry
function, the variable new_args
gets the parameters and converts them into an array.stored_args
through the closure and merges the value of the variable new_args
into a new array and assigns it to the variable args
.fn.apply(null,args)
method is called to execute the curried function.Now let's test the public curried function
// ordinary function add() function add(x, y){ return x + y; } // Curry to get a new function var newAdd = createCurry(add,4); console.log(newAdd(6)); // 10 //Another simple way console.log(createCurry(add,4)(6));// 10
Of course, this is not limited to currying of two parameters, but also multiple parameters:
// Multiple parameters The ordinary function function add(a,b,c,d){ return a + b + c + d; } // Curry the function to get a new function, multiple parameters can be divided at will console.log(createCurry(add,4,5)(5,6)); // 20 // Two-step curry let add_one = createCurry(add,5); console.log(add_one(5,5,5));// 20 let add_two = createCurry(add_one,4,6); console.log(add_two(6)); // 21Through
the above example, we can find a limitation, that is, whether it is two parameters or multiple parameters, it can only be executed in two steps, such as the following formula:
if we want to be more flexible:
How do we implement it?
After the above exercises, we found that the curried function we created has certain limitations. We hope that the function can be executed in multiple steps:
// Create a curried function that can be executed in multiple steps Function, execute it when the number of parameters is met: // Function formula: fn(x,y,z,w) ==> fn(x)(y)(z)(w); let createCurry = (fn,...params)=> { let args = parsms || []; let fnLen = fn.length; // Specify the parameter length of the curried function return (...res)=> { // Get all the previous parameters through the scope chain let allArgs = args.slice(0); // Deep copy the args parameters shared by closures to avoid the impact of subsequent operations (reference type) allArgs.push(...res); if(allArgs.length < fnLen){ // When the number of parameters is less than the parameter length of the original function, call the createCurry function recursively return createCurry.call(this,fn,...allArgs); }else{ // When the number of parameters is met, trigger function execution return fn.apply(this,allArgs); } } } // Ordinary function function add(a,b,c,d){ with multiple parameters return a + b + c + d; } //Test the currying function let curryAdd = createCurry(add,1); console.log(curryAdd(2)(3)(4)); //
We have implemented flexible currying functions for more than 10 years, but here we find another problem:
curryAdd(add,1,2,3,4)()
;add()
function. This is also One way; but since we are satisfying the number of parameters here, we still deal with this situation.Here we only need to make a judgment before returning the function:
let createCurry = (fn,...params)=> { let args = parsms || []; let fnLen = fn.length; // Specify the parameter length of the curried function if(length === _args.length){ //Add judgment. If the number of parameters for the first time is enough, call the function directly to get the result return fn.apply(this,args); } return (...res)=> { let allArgs = args.slice(0); allArgs.push(...res); if(allArgs.length < fnLen){ return createCurry.call(this,fn,...allArgs); }else{ return fn.apply(this,allArgs); } }}
The above can be regarded as a flexible curried function, but it is not very flexible here because we cannot control when it is executed. As long as the number of parameters is sufficient, it will be executed automatically. What should we do if we want to achieve a timing that can control its execution?
Let’s directly explain the function formula here:
// When the parameters are satisfied, call the function let createCurry = (fn,...params)=> { let args = parsms || []; let fnLen = fn.length; // Specify the parameter length of the curried function //Of course the judgment here needs to be commented out, otherwise the result will be executed directly when the number of parameters is sufficient for the first time //if(length === _args.length){ // Add judgment. If the number of parameters for the first time is enough, call the function directly to get the result //return fn.apply(this,args); //} return (...res)=> { let allArgs = args.slice(0); allArgs.push(...res); // Here it is judged whether the input parameters are greater than 0. If it is greater than 0, it is judged whether the number of parameters is sufficient. // && cannot be used here. If && is used, the result will be executed when the number of parameters is sufficient. if(res.length > 0 || allArgs.length < fnLen){ return createCurry.call(this,fn,...allArgs); }else{ return fn.apply(this,allArgs); } } } // Ordinary function function add(a,b,c,d){ with multiple parameters return a + b + c + d; } // Test the controllable currying function let curryAdd = createCurry(add,1); console.log(curryAdd(2)(3)(4)); // function console.log(curryAdd(2)(3)(4)()); // 10 console.log(curryAdd(2)(3)()); // Returns NaN when the parameters are not enough.
Related recommendations: JavaScript learning tutorial.
The above is to talk about the details of JavaScript function currying. For more information, please pay attention to php Other related articles on the Chinese website!