柯里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
常用场景
// 普通的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
/* * 正常正则验证字符串 reg.test(txt) * * 需求: * 验证字符串中是否含有数字或者字母 * */ //直接调用正则验证 /\d+/g.test('hello') //false /[a-z]+/g.test('hello') //true // 常规函数封装 function check(reg, txt) { return reg.test(txt) } check(/\d+/g, 'hello') //false check(/[a-z]+/g, 'hello') //true // Currying 注:项目中需要多次用到规则函数时,将该函数定义为全局函数,函数复用性更好。 function curryingCheck(reg) { return function(txt) { return reg.test(txt) } } /** * hasNumber验证全局是否含有数字 * hasLetter验证全局是否含有字母 * hasNumber,hasLetter此时已经定义为对应的工具函数,调用时只需要传递一个参数 * 使用起来更加方便而且语义化更强,代码复用度更高 * **/ const hasNumber = curryingCheck(/\d+/g) const hasLetter = curryingCheck(/[a-z]+/g) hasNumber('hello') // false hasLetter('hello') // true
/** * * 兼容现代浏览器以及IE浏览器的事件添加方法 * **/ //常规写法 //问题:每次使用addEvent为元素添加事件的时候,都会判断一次。 var addEvent = 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); } } } //Currying //初始addEvent的执行其实值实现了部分的应用,而剩余的参数应用都是其返回函数实现。 var addEvent = (function() { if (document.addEventListener) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); } }; } else { return function( event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); } }; } })(); //不使用自启动函数写法 var addEvent = function() { const isSupport = document.addEventListener; if (isSupport) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); } } } else { return function( event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); } }; } }
/** * * 函数常用的bind方法实质就是延迟执行 * **/ Function.prototype.bind = function (context) { var _this = this var args = Array.prototype.slice.call(arguments, 1) return function() { return _this.apply(context, args) } }
// 实现一个add方法,使计算结果能够满足如下预期: add(1)(2)(3)()= 6; add(1, 2, 3)(4)() = 10; add(1)(2)(3)(4)(5)() = 15; // 定义参数累加方法 function adder(...args) { return args.reduce( (total, currentVal) => { total += currentVal; return total; }) } function addCurry(fn) { // _allargs 存储所有的参数 // _tempargs 传递给累加方法的复制所有参数的临时参数 // _tempargs 作用在收集到所有参数时使_allargs清空参数,否则重复调用时,_allargs会将上一次调用时参数与此次调用的参数合拼,造成错误的结果。 var _allargs = []; var _tempargs= []; return function reFn(...args){ if(args.length === 0){ _tempargs = _allargs; _allargs = []; return fn.apply(this,_tempargs); } else{ _allargs = _allargs.concat(args) return reFn; } } } console.log(add(1, 2, 3)(4)()); // 10 console.log(add(5)(6)(7)(8)(9)()); // 35 /* * 可重复调用写法 * * 知识点:对象(包括数组,对象,函数等)参与原始运算如算术或逻辑运算时,会无参调用其toString或者valueOf方法得到 * 一个原始值,然后用这个原始值参与运算。 ** */ function add(...args) { // 将参数绑定到add上 // 此时f其实还是add函数,但已经固定了一些参数,所以并不是原来的add函数 // 用bind返回新函数以保证满足**柯里化保留参数**的特性 var f = add.bind(null/*this不用绑定*/, ...args) // 重新实现这个bound add函数的toString方法 // f参与运算应该被当成args的和,与f自己再接收的参数无关 // 考虑到lazy的特性,还是需要时再计算,但又没了缓存,每次用都会重新计算 // 如果有需要,可以改成有缓存的版本 f.toString = () => { return args.reduce((a, b) => a + b, 0) } return f; } // 考虑到add可能直接被用于运算中,可以加上这句 add.toString = () => 0 /* 注意以上代码中的add,add3,add8,add8p,add9都是不同的函数,且每个函数要加的数是不一样的。 */ const add3 = add(0, 1)(2) // add3的功能是对传入的数值加3并返回 console.log( add3(2) + 0 ) // log出5 const add8 = add3(1)(2)(2) // add8由add3的持续调用得到 const add8p = add3(5) // 另一种方式得到add8,注意两个add8不是同一个函数,起名add8p const add9 = add8(1) // 由add8再传入得到add9函数 console.log( add9(1) + 3 ) // log出13 console.log( add8(1) + 3 ) // log出12
https://www.jianshu.com/p/2975c25e4d71
https://zhuanlan.zhihu.com/p/296852112