js高级进阶之函数柯里化

函数柯里化是所有编程语言推崇的函数优化方式,js的函数柯里化是你写出优雅函数的基础。

概念

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
例如:我们求(a+b)*c 如果普通方法实现 f(a,b,c),用柯里化方法:f(a)(b)©这种调用格式获取。

实现原理

  • 闭包来保存参数;
  • 高阶函数实现运算;

例如最简单的例子:求 (15+3)*4的值

let calcu = (a, b, c) => (a+b)*c;
        function curry(fn, args){
            let len = fn.length;
            let _this = this;
            let _args = args || [];
            return function(){
                let args = Array.prototype.slice.apply(arguments);
                args = Array.prototype.concat.call(_args, args);
                // 当接收到的参数小于fn所需参数个数时,继续接收参数
                if(args.length < len){
                    return curry.call(_this, fn, args);
                }
                return fn.apply(this, args);
            }
        }
        let add = curry(calcu);
        console.log(add(15)(3)(5)); //72

在上面的代码中,我们使用curry函数它可以不用一次性接收三个参数,而是慢慢接收,当发现接收到的参数达到3个之后再返回结果。这就是参数复用(通过例子我们可以看到,实现这个特点主要原因是利用了闭包,将接收到的参数存于_args中,由于闭包的原因这些参数在函数执行完之后并不会被释放掉。

上面的curry方法,将每次调用fn时读入的参数用args来保存,并将本次读入的参数args与当前一共拥有的所有参数_args用concat方法连接起来,当参数个数符合fn的参数个数要求时,则调用fn。

特点

  • 参数复用: 就是利用闭包的原理,让我们前面传输过来的参数不要被释放掉。
  • 提前确认: 利用柯里化特性对某些全局方法进行改写,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断。
  • 延迟运行:利用闭包将函数的定义和执行环境分开。

参数复用案例:

// 正常正则验证字符串 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

提前确认:

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);
    }
}

延迟运行:

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

柯里化函数的最佳实现

函数柯里化是一个比较难以应用的方法,最主要是能够理解其原理使你阅读源码无障碍,下面给出一个柯里化的最佳实现方法,背过他可以救急

// 支持多参数传递
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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章