[Turn] Detailed JS function currying

The first time I saw the word when currying, or watching an algorithm related blog mentioned the curried function, then a look at the word feel very high-end, in fact, when you know after the discovery of fact, It is a special use of higher-order functions. It really is like no matter how high the role must have a name to be useful.

First, look at what in the end is currying?


维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

Look at this explanation a bit abstract, we took the add function was made of numerous examples, do a simple implementation.


// 普通的add函数
function add(x, y) {
    return x + y
}

// Currying后
function curryingAdd(x) {
    return function (y) {
        return x + y
    }}

add(1, 2)           // 3
curryingAdd(1)(2)   // 3

Is actually a function of the Add the x, y with the first two parameters becomes a function returns a function of the received x and y to process parameters. Now thought to be relatively clear, is only part of the argument passed to the function to call it, it returns a function to handle the rest of the parameters.

But here so much trouble encapsulating an effort, in the end what use is it? No good programmers want us to do more things that are impossible to drop, this life is impossible.

To row a Currying What good is it?

  1. Multiplexing parameters

// 正常正则验证字符串 
reg.test(txt)

// 函数封装后
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// Currying后
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }}

var hasNumber = curryingCheck(/\d+/g)var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false

The above example is a regular check, the normal function for direct calls to check on it, but there are many places if I have to check whether there is a number, in fact, the first argument needs to be reg multiplexed, so do not the place can be called directly hasNumber, hasLetter and other functions, so that parameters can be reused, and more convenient to call it.

  1. 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() {
    if (document.addEventListener) {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    } else {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler);
            }
        };
    }})();

//换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了
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);
    }}

We are doing the project process, package some of the common dom operation can be said then, however, the first written above is relatively common, but we look at the second wording, it is relatively a first writing is self-executing and returns a new function, this is actually a method determined in advance which ones will avoid every judge.

  1. Delayed operation

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

Like bind our js frequently used mechanism to achieve is Currying., Said the benefits of these points, and found there is a problem, would each use Currying have to do to modify the underlying function,

There is no universal method of packaging?


// 初步封装var currying = function(fn) {
    // args 获取第一个方法内的全部参数
    var args = Array.prototype.slice.call(arguments, 1)
    return function() {
        // 将后面方法里的全部参数和args进行合并
        var newArgs = args.concat(Array.prototype.slice.call(arguments))
        // 把合并后的参数通过apply作为fn的参数并执行
        return fn.apply(this, newArgs)
    }
}

First, the package here are preliminary, initial parameter through the closure to survive, then spliced ​​by acquiring the remaining arguments, currying last executed functions needed.

But what seems to have some flaws, this can only be returned if in fact a multi-parameter expansion, currying (a) (b) (c) In this case, seemingly does not support (does not support multi-parameter call), the general this situation will think and then using a recursive encapsulation layer.


// 支持多参数传递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);

        // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }

        // 参数收集完毕,则执行fn
        return fn.apply(this, _args);
    }
}

In fact, here is a preliminary basis, plus a recursive call, as long as the number of parameters is less than the initial fn.length, will continue to implement recursion.

Benefits finished, the general method has also been, let's focus on the performance curry

curry of some performance issues as long as you know the following four points on the same subject:

  • Access is usually better than the arguments object to access parameters to be named slow
  • Some older versions of the browser in the realization arguments.length is quite slow
  • Use fn.apply (...) and fn.call (...) is usually higher than direct calls fn (...) a little slower
  • Creating a large number of nested scopes and closure function will bring spending, either on memory or speed

In fact, in most applications, the main performance bottleneck is in operation DOM node, which js performance loss is basically negligible, so the curry can be assured of direct use.

Finally, extended a classic face questions




// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };

    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _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

Reference Information

This article is reproduced from https://www.jianshu.com/p/2975c25e4d71

Guess you like

Origin www.cnblogs.com/gehai/p/11506385.html