doubleyong
管理员
管理员
  • 最后登录2025-12-02
  • 发帖数1198
  • 最爱沙发
  • 喜欢达人
  • 原创写手
  • 社区居民
  • 忠实会员
阅读:3991回复:0

[es6]柯里化详解

楼主#
更多 发布于:2020-08-30 11:07
柯里化,其本质就是高级函数的一种应用,那费这么大劲封装,到底有什么用处呢?



01
好处1:参数复用

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

上面的示例是一个正则的校验,正常来说直接调用check函数就可以了,但是如果我有很多地方都要校验是否有数字,其实就是需要将第一个参数reg进行复用,这样别的地方就能够直接调用hasNumber,hasLetter等函数,让参数能够复用,调用起来也更方便。


02

好处2:提前确认


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

我们在做项目的过程中,封装一些dom操作可以说再常见不过,上面第一种写法也是比较常见,但是我们看看第二种写法,它相对一第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断


03

好处3:延迟执行



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

像我们js中经常使用的bind,实现的机制就是Currying.



说了这几点好处之后,发现还有个问题,难道每次使用Currying都要对底层函数去做修改


04

柯里化:简单封装



// 初步封装
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)
    }
}

这边首先是初步封装,通过闭包把初步参数给保存下来,然后通过获取剩下的 arguments 进行拼接,最后执行需要currying的函数。


但是好像还有些什么缺陷,这样返回的话其实只能多扩展一个参数,currying(a)(b)(c)这样的话,貌似就不支持了(不支持多参数调用),一般这种情况都会想到使用递归再进行封装一层


05

柯里化:完美封装



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

这边其实是在初步的基础上,加上了递归的调用,只要参数个数小于最初的fn.length,就会继续执行递归



参考https://www.jianshu.com/p/2975c25e4d71
知识需要管理,知识需要分享
游客


返回顶部

公众号

公众号