1 What is the Currization of a function

In computer science, Currying is a technique that converts a function that takes multiple arguments into a function that takes a single argument (the first argument of the original function), and returns a new function that takes the remaining arguments and returns a result. This technique is named after logician Haskell Curry.

What do you mean? In simple terms, Corrification is a technique for modifying functions with multiple parameters. Such as:

// This is a function that takes three arguments
const add = function(x, y, z) {
  return x + y + z
}
Copy the code

If we transform it, we get a function that looks like this:

// Accept a single parameter
const curryingAdd = function(x) {
  // And returns the function that takes the rest of the arguments
  return function(y, z) {
    return x + y + z
  }
}
Copy the code

What difference does it make? In terms of calls:

/ / call the add
add(1.2.3)
​
/ / call curryingAdd
curryingAdd(1) (2.3)
// To see more clearly, it is equivalent to the following
const fn = curryingAdd(1)
fn(2.3)
Copy the code

As you can see, the transformed function can take arguments in batches, so keep that in mind and we’ll see how it works. Even fn (the function returned by curryingAdd) can continue to transform:

const curryingAdd = function(x) {
  return function(y) {
    return function(z) {
      return x + y + z
    }
  }
}
/ / call
curryingAdd(1) (2) (3)
/ / that
const fn = curryingAdd(1)
const fn1 = fn(2)
fn1(3)
Copy the code

The two transformations above are called currization.

The simple idea is to transform a function f with many arguments into a function g that takes some arguments, and this function G returns a function H that takes other arguments. The function h can continue to be currified. Is a nesting doll process ~

So what’s the point of going to all this trouble to corrify the function?

2. The function and characteristics of corrification

2.1 Parameter Multiplexing

Work will meet the requirements: through the re check phone number, mailbox, ID card is legal and so on

So we’ll wrap a checksum function as follows:

/ * * *@description Verifies the string * with the re@param {RegExp} RegExp Specifies the regular object *@param {String} STR The character string to be verified *@return {Boolean} Check whether */ passes the verification
function checkByRegExp(regExp, str) {
    return regExp.test(str)
}
Copy the code

If we want to verify many mobile phone numbers and email addresses, we would call:

// Check the phone number
checkByRegExp(/^1\d{10}$/.'15152525634'); 
checkByRegExp(/^1\d{10}$/.'13456574566'); 
checkByRegExp(/^1\d{10}$/.'18123787385'); 
// Verify the mailbox
checkByRegExp(/^(\w)+(.\w+)*@(\w)+((.\w+)+)$/.'[email protected]'); 
checkByRegExp(/^(\w)+(.\w+)*@(\w)+((.\w+)+)$/.'[email protected]'); 
checkByRegExp(/^(\w)+(.\w+)*@(\w)+((.\w+)+)$/.'[email protected]');
Copy the code

It looks like nothing’s wrong, but there’s room for improvement, right

  1. When checking the same type of data, we write the same re many times.
  2. The code is not very readable, and without comments, it is not immediately obvious what the re does

We try to improve this by using the function Corrification:

// Currize the function
function checkByRegExp(regExp) {
    return function(str) {
        return regExp.test(str)
    }
}
Copy the code

So we pass in different re objects to get different functions:

// Check the phone
const checkPhone = curryingCheckByRegExp(/^1\d{10}$/)
// Verify the mailbox
const checkEmail = curryingCheckByRegExp(/^(\w)+(.\w+)*@(\w)+((.\w+)+)$/)
Copy the code

Now the code for checking mobile phones and emails is simpler and more readable

// Check the phone number
checkPhone('15152525634'); 
checkPhone('13456574566'); 
checkPhone('18123787385'); 
// Verify the mailbox
checkEmail('[email protected]'); 
checkEmail('[email protected]'); 
checkEmail('[email protected]');
Copy the code

This is parameter multiplexing: we simply need to reuse the first parameter regExp to call a function with a specific function directly

General-purpose functions (such as checkByRegExp) solve compatibility problems, but can also cause inconvenience, such as passing multiple parameters in different application scenarios

Sometimes the same rule may be used over and over again (for example, checking the parameters of mobile phones), which leads to the duplication of code. Using Corrification can eliminate the duplication and achieve the purpose of reuse parameters.

An important idea of Corrification: reduce the scope of application, improve the applicability

2.2 Early Return

In the JS DOM event listener, we use the addEventListener method to add an event handler to the element, but some browser versions do not support this method and we use the attachEvent method instead.

We write a code that is compatible with each browser version:

/ * * *@description: 
 * @param {object} Element DOM Element object *@param {string} Type Indicates the event type *@param {Function} Fn event handler *@param {boolean} IsCapture Whether to capture *@return {void}* /
function addEvent(element, type, fn, isCapture) {
    if (window.addEventListener) {
        element.addEventListener(type, fn, isCapture)
    } else if (window.attachEvent) {
        element.attachEvent("on" + type, fn)
    }
}
Copy the code

We use addEvent to addEvent listening, but each time this method is called, a judgment is made, and there is no need to repeat the judgment once the browser version is determined.

Cremation treatment:

function curryingAddEvent() {
    if (window.addEventListener) {
        return function(element, type, fn, isCapture) {
            element.addEventListener(type, fn, isCapture)
        }
    } else if (window.attachEvent) {
        return function(element, type, fn) {
            element.attachEvent("on" + type, fn)
        }
    }
}
const addEvent = curryingAddEvent()
​
// You can also combine the above code with an immediate function
const addEvent = (function curryingAddEvent() {
  ...
})()
Copy the code

Now we get addEvent is the function after the judgment, the future call does not need to repeat the judgment.

So, this is called early return or early confirmation, where the function corrified can do some of the tasks ahead of time, and return a function that does some of the other tasks

Also, we can see that curryingAddEvent doesn’t seem to accept arguments. This is because the condition for the original function (that is, whether the browser version supports addEventListener) is directly fetched globally. Logically, it could be:

let mode = window.addEventListener ? 0 : 1;
function addEvent(mode, element, type, fn, isCapture) {
  if (mode === 0) {
    element.addEventListener(type, fn, isCapture);
  } else if (mode === 1) {
    element.attachEvent("on"+ type, fn); }}// Then we can accept a parameter after currified
function curryingAddEvent(mode) {
    if (mode === 0) {
        return function(element, type, fn, isCapture) {
            element.addEventListener(type, fn, isCapture)
        }
    } else if (mode === 1) {
        return function(element, type, fn) {
            element.attachEvent("on" + type, fn)
        }
    }
}
Copy the code

Of course there is no need to change

2.3 Delayed Execution

In fact, the above example of regular checksum event listening already shows delayed execution.

The curryingCheckByRegExp function calls return the checkPhone and checkEmail functions

The addEvent function is returned after the curringAddEvent call

The returned functions are not executed immediately, but wait to be invoked.

Package general currie chemical tool function

We have manually changed the function from add to curryingAdd, checkByRegExp to curryingCheckByRegExp and addEvent to curryingAddEvent.

Do we have to manually modify the underlying function every time we currize a function? Of course not

We can encapsulate a generic Currie tool function

/ * * *@description: a utility function that corries a function *@param {Function} F sub n to be Currified *@param {array} List of parameters that arGS has received *@return {Function}* /
const currying = function(fn, ... args) {
    // Number of parameters required by fn
    const len = fn.length
    // Return a function to accept the remaining arguments
    return function (. params) {
        // Concatenates a list of received and newly received parameters
        let _args = [...args, ...params]
        // If the number of arguments received is not enough, return a new function to receive the remaining arguments
        if (_args.length < len) {
            return currying.call(this, fn, ... _args) }// Call the original function after receiving all parameters
        return fn.apply(this, _args)
    }
}
Copy the code

This Currie tool function is used to receive some arguments, return a new function, wait for the rest, recurse until all the required arguments are received, and then call the original function through Apply.

Now we don’t really have to manually modify the original function to make the function currify

// Use the utility function directly to return the function to verify mobile phone, email
const checkPhone = currying(checkByRegExp(/^1\d{10}$/))
const checkEmail = currying(checkByRegExp(/^(\w)+(.\w+)*@(\w)+((.\w+)+)$/))
Copy the code

However, the above example of event listening cannot be currified with the utility function, because its condition is directly obtained from the global, so it is special. Instead, it can be currified with the utility function. Of course this is not necessary; it is much more straightforward and readable to modify the original function directly

4 summary and supplement

  1. Currization highlights an important idea: reduce the scope of application and improve the applicability
  2. The three functions and characteristics of Coriolization are parameter reuse, early return and delayed execution
  3. A typical use of closures is to use closures to form a scope that is stored in memory, and to store some of the received parameters in this scope for later use. And returns a new function to accept the remaining arguments