Corrification: When you look at this word it sounds very high-end, but when you get to know it it’s actually a special use of higher-order functions.

Sure enough, no matter how it works, a high-end name is useful.

First of all, what is Corrification?

According to Wikipedia, Currying, English: Currying, is a technique of replacing a function that takes multiple arguments with a function that takes a single argument (the first argument of the original function), and returning a new function that takes the remaining arguments and returns a result.

Given the confusion of the explanation, let’s do a simple implementation using an Add function

// A normal function
function add(x , y) {
    return x + y; 
}


/ / after Currying
function curryingAdd(x){
    return function(y){
        return x + y;
    }
}
add(1.2) / / 3
curryingAdd(1) (2) / / 3
Copy the code

You’re essentially changing the x and y arguments of add to a function that accepts x and then returns a function that handles y arguments. The idea should now be clear: call a function with just a few arguments and let it return a function that handles the rest.

But what’s the point of going to all this trouble to encapsulate it?

Make a list of the benefits of Currying

1. Parameter multiplexing

// Normal rege.test (TXT)

// Encapsulate normal functions
function check(reg , txt){
    return reg.text(txt);
}


/ / after Currying
function curryingCheck(reg){
    return function(txt){
        returnreg.text(txt); }}var hasNum = curryingCheck(/\d+/g);
var hasLetter = curryingCheck(/[a-z]+/g);

hasNum('123')
hasLetter('fadf')
Copy the code

The example above is a regex validation. Normally, just calling the check function would do fine, but if we have a lot of places to check for numbers, we actually need to reuse the first parameter reg so that it can be directly called elsewhere, making the argument reusable and easier to call

2. Confirm in advance

var on = function(element, event , handler){
    if(document.addEventListener){
        if(element && event && handler){
            element.addEventListener(event , handler , false); }}else{
        if(element && event && handler){
            element.attachEvent('on'+ event , handler ); }}}var on = (function(element , event , handler){
    if(document.addEventListener){
        if(element && event && handler){
            element.addEventListener(event , handler , false); }}else{
        if(element && event && handler){
            element.attachEvent('on'+ event , handler ); }}}) ();// The isSupport parameter is set first
var on = function(isSupport , element , event , handler){
    isSupport = isSupport || document.addEventListener; 
    if(isSupport){
        return element.addEventListener(event , handler , false);
    }else {
        return element.attachEvent('on'+ event , handler ); }}Copy the code

We are doing in the process of the project, some wrapped dom manipulation can be common, however, the first written above are common, but we look at the second kind of writing, it is relative to the first writing since the execution and returns a new function, it is confirmed which one way can go ahead and avoid judging every time.

3. Delay execution

Function.prototype.bind = function( context ){
    var _this = this ; 
    var args = Array.prototype.slice.call(arguments.1);
    
    return function(){
        return_this.apply(context , args); }}Copy the code

A mechanism commonly used in JAVASCRIPT to implement bind is called Currying.

There is a problem with using only Currying rying. Do you need to change the underlying function every time you use a Currying rying rying?

Generic encapsulation methods

// Initial encapsulation
var currying = function(fn) {
    // args gets all the arguments in the first method
    var args = Array.prototype.slice.call(arguments.1)
    return function() {
        // merge all the parameters in the following method with args
        var newArgs = args.concat(Array.prototype.slice.call(arguments))
        // apply the merged parameters as fn parameters and execute them
        return fn.apply(this, newArgs)
    }
}
Copy the code

This is a rudimentary wrapper that saves all the arguments in a closure, then concatenates them by taking the rest of the arguments, and finally executes functions that require currying.

Those who are concerned will notice that there is a problem with the above code: it can only extend one more parameter, which is not supported for currying(a), (b), (c).

// Support multiple parameter passing
function progressCurrying(fn, args) {

    var _this = this
    var len = fn.length;
    var args = args || [];

    return function() {
        var _args = Array.prototype.slice.call(arguments);
        Array.prototype.push.apply(args, _args);

        // If the number of arguments is less than the original fn.length, the recursive call continues to collect arguments
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }

        // After parameters are collected, run fn
        return fn.apply(this, _args); }}Copy the code

As long as the number of arguments is less than the initial fn.length, the recursion will continue.

With the above method, but as a programmer, we also have to pay attention to its performance

Description of the properties of currying

There are four main problems with the performance of currying:

  • Accessing arguments objects is generally a bit slower than accessing named arguments
  • Some older browsers are quite slow to implement arguments.length
  • usefn.apply(...)fn.call(...)Usually more than direct callsfn(...)A bit slow
  • Creating lots of nested scopes and closures can be costly, both in memory and speed

However, in most applications, the main performance bottleneck is the DOM node manipulation, and the performance loss of JS is basically negligible, so Curry can be directly trusted to use.

Frequently seen exam

// Implement an add method that satisfies the following expectations:
add(1) (2) (3) = 6;
add(1.2.3) (4) = 10;
add(1) (2) (3) (4) (5) = 15;

function add() {
    // On the first execution, an array is defined to store all the parameters
    var _args = Array.prototype.slice.call(arguments);

    // Internally declare a function that uses closure properties to hold _args and collect all parameter values
    var _adder = function() { _args.push(... arguments);return _adder;
    };

    // Take advantage of the toString implicit conversion feature to implicitly convert when finally executed and calculate the final value returned
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;
}

add(1) (2) (3)                / / 6
add(1.2.3) (4)             / / 10
add(1) (2) (3) (4) (5)          / / 15
add(2.6) (1)                / / 9



Copy the code