concept

** Currification: ** can be understood as a function that accepts some arguments early, delays execution, and does not immediately output the result, but instead returns a function that accepts the rest of the arguments. Because such a property, also known as a partially computed function, is a step-by-step process of receiving parameters.

The concept is a bit abstract and confusing to understand, so let’s use an example of the Add function to illustrate it

function add(a,b){
 return a + b
}
add(1.2) / / 3
Copy the code

After modifying the add function above, we will use pseudo code to implement the following

function curryingAdd(){
	// Currified implementation
}
add(1) (2) / / 3
Copy the code

Through the implementation of the pseudo-code, we can see that currization is actually changing the two parameters of add, a and B, to pass one parameter a and then return one parameter B and calculate the result of the two passed parameters. It follows that the Currization function is a gradual process of receiving parameters. ** Through the above pseudo-code, we know the implementation process of Currization, next let’s pseudo-code step by step implementation below

The implementation process

// Define a Currization function. Wrap ordinary functions as Currization functions
function currying(fn) {
  let args = [];
  const inner = function () {
    // Start collecting parameters with each call
    let _args = [].slice.apply(arguments);
    // If there are parameters to continue collecting,
    // If there are no arguments, the collected arguments are passed to the function call
    if (_args.length > 0) { args.push(... _args);return inner;
    } else {
      // The args was not released due to closure, resulting in the following calls retaining the arguments passed in last time
      // Clear it manually
      // There is no need to handle this in real development. In the development process, we collect the arguments and reuse the last argument with each call
      const params = [...args]
      args = []
      return fn.apply(null, params); }};// Return a function
  return inner;
}
// Define ordinary functions
function add(){
	const args = [].slice.apply(arguments);
  return args.reduce((acc,cur) = >{return acc+cur},0)}// wrap the add function as a Cremation function
const curryingAdd = currying(add)

console.log(curryingAdd(1.2.3) ());/ / 6
console.log(curryingAdd(1.2) (3) ());/ / 6
console.log(curryingAdd(1) (2) (3) ());/ / 6
Copy the code

According to the above implementation process, it can be found that the principle of The Currization function is not complicated, just collect the parameters and call it at the appropriate time. However, as a result of the above implementation process, we have to execute an empty parameter at the end of the call. Is there a way to get the result without executing the last step? The answer is yes!!

Based on our knowledge of prototypes and prototype chains, we know that there are two methods on the prototype of a function, toString and valueOf. Things get interesting when js implicitly calls the valueOf and toString methods to get the desired value, depending on the context

function currying(fn) {
  let args = [];
  const inner = function () {
    let _args = [].slice.apply(arguments); args.push(... _args);return inner;
  };
  inner.toString = function () {
    const params = [...args]
    args = []
    return fn.apply(null, params);
  };
  inner.valueOf = function () {
    const params = [...args]
    args = []
    return fn.apply(null, params);
  };
  return inner;
}
Copy the code

This can only be done in a browser, and the node.js environment will still print out the function’s prototype. But the principle is clear.

The effect of cremation

So to figure out how it works, let’s look at what cremation does, why cremation is used. Based on the above implementation, it can be seen that the implementation of Currization is also a closure process, so currization has the advantages of closures.

  1. Reuse of parameters
  2. Confirmation in advance
  3. Delay the

Let’s start with 1 and 3, not to mention parameter reuse, closure properties. Deferred execution, such as the bind mechanism used in JS, is implemented by corrification, which returns a function to be called at the time it is used. Validation in advance, because we return one function at a time, allows us to know at run time that we are going to get an error on that parameter, spreading errors within one function over multiple functions.

Application scenarios

Take a look at an application scenario of Cremation, which is also a problem I encountered in the process of actual development. ** : ** Pass two dates, calculate the difference between the two dates by how many years, how many months, how many days, if no year is passed, return how many months and how many days.

Example:

function diffDate(date1,data2,type){
	// TODO
}
diff('2020-04-01'.'2021-05-03'.'year') // 2 days in January 1 year
diff('2020-04-01'.'2021-05-03'.'month') // 2 days in 13 months
diff('2020-04-01'.'2021-05-03'.'day') / / 397 days
Copy the code

Problem solving process:

A moment package is usually used to handle date-related content in front-end projects. Currently, moment is no longer maintained. It is the same with day.js. So in this case, the API that we’re looking for is diff

const moment = require('moment')

const diffDate = (begin, end) = > {
  If begin > end returns negative
  let flag = false;
  const contrast = () = > {
    if (moment(begin).isAfter(end)) {
      flag = true;
      return [end, begin];
    }
    return [begin, end];
  };
  // Compare dates with smaller dates in front and larger dates behind
  let [from, to] = contrast();
  function computed() {
    const args = [].slice.apply(arguments);
    let diffObj = {};
    args.forEach((arg) = > {
      // Based on the passed argument. Calculation time difference
      const diff = Math.abs(moment(from).diff(moment(to), arg));
      // Update from time, update the start time to continue comparison after comparison
      from = moment(from).add(diff, arg); diffObj = { ... diffObj, [arg]: diff, }; });return diffObj;
  }
  const computedDate = currying(computed);
  const result = computedDate('Years') ('Months') ('Days') ();console.log(flag, result); // false { Years: 1, Months: 1, Days: 2 }
};
diffDate('2021-04-01'.'2022-05-03');
Copy the code

** Analysis: ** Application scenario is to calculate whether A product has expired, how many days of expiration, how many days of expiration, the expiration date is known. For example, the expiration date of product A is 2022-05-03, and today is 2021-04-01. Start date from 2022-04-01, start date from 2022-04-01, start date from 2022-04-01, start date from 2022-04-01, start date from 2022-04-01. Up to the day.

You can see the first way of using the Currization function.