JavaScript函数式编程——柯里化
- 柯里化原理
- 如何实现柯里化
- 柯里化的应用
一、柯里化原理
柯里化:在数学和计算机科学中,柯里化是一种使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
前端使用柯里化的用途主要就应该是简化代码结构,提高系统的维护性,一个方法,只有一个参数,强制了功能的单一性,很自然就做到了功能内聚,降低耦合。
柯里化的优点:降低代码的重复,提高代码的适应性。
基于柯里化的基本原理,先将多个参数的函数执行拆分成两次传参执行:
1 //有如下四个参数的函数 2 function add(a, b, c, d){ 3 return a + b + c + d; 4 } 5 //实现一个两次传参的固定柯里化方法 6 function FixedParmasCurry(fu){ 7 var _arg = Array.prototype.slice.call(arguments,1); //获取初始柯里化时传入的参数 8 return function(){ 9 var newArg = _arg.concat(Array.prototype.call(arguments,0)); //拼接当前执行的参数 10 return fn.apply(this, newArg); 11 } 12 } 13 //测试一 14 var foo = FixedParmasCurry(add,1,2); 15 console.log(foo(3,4));//10 16 //测试二 17 var fun = FixedParmasCurry(add,1); 18 console.log(fun(2,3,4));//10
但是这并没有实现真正的柯里化,真正的柯里化应该是每次传任意参数,函数经过任意次执行,直到参数传入达到函数所需要的参数时返回函数的执行结果;也可以说是函数每次传入相应的参数,返回每个执行阶段的结果。为什么这么说呢?在柯里化应用中详细解析。
二、如何实现柯里化
1 // 实现柯里化 2 function add(a, b, c, d){ 3 return a + b + c + d; 4 } 5 // add函数柯里化的真正需求应该是下面这样: 6 var newAdd = Curry(add); 7 newAdd(1,2,3,4); 8 newAdd(1)(2)(3)(4); 9 newAdd(1,2)(3,4); 10 newAdd(1,2)(3)(4); 11 newAdd(1,2,3)(4); 12 newAdd(1)(2)(3,4); 13 newAdd(1)(2,3,4); 14 // 也就是说真正的Curry可以实现以上任意的执行组合 15 16 function Curry(fn,length){ 17 var length = length || fn.length; 18 return function(){ 19 if(arguments.length < length){ 20 var combined = [fn].concat(Array.prototype.slice.call(arguments,0)); 21 return Curry(FixedParmasCurry.apply(this,combined),length - arguments.length); 22 }else{ 23 return fn.apply(this,arguments); 24 } 25 } 26 } 27 28 function FixedParmasCurry(fn){ 29 var _arg = Array.prototype.slice.call(arguments,1); //获取初始柯里化时传入的参数 30 return function(){ 31 var newArg = _arg.concat(Array.prototype.slice.call(arguments,0)); //拼接当前执行的参数 32 return fn.apply(this, newArg); 33 } 34 }
实现分析:FixedParmasCurry(fn)本质上就是将已传入的参数作函数执行必要的参数直接执行,缺少对参数长度的判断。实现函数柯里化时初始化了必要参数的总长度length;然后每次计算还需要传入函数的长度,直到传入的参数arguments.length的长度大于或者等于需要的参数长度,就可以真正的执行函数了(23行)。
三、柯里化应用
例如下面这个模拟的ajax请求需求:
1 function ajax(type,url,data){ 2 var xhr = new XMLHTTPRequest(); 3 xhr.open(type,url,true); 4 xhr.send(data); 5 } 6 //如果有需求是将同一个参数同时发送给三个连接,下面这种做法明显的出现了代码冗余 7 ajax('POST','www.test.com','name=keyin'); 8 ajax('POST','www.test2.com','name=keyin'); 9 ajax('POST','www.test3.com','name=keyin'); 10 //Curry 11 var ajaxCurry = curry(ajax); 12 var post = ajaxCurry("POST"); 13 post('www.test.com','name=keyin'); 14 post('www.test.com2','name=keyin'); 15 post('www.test.com3','name=keyin');
可能在示例中还不那么明显,至少可以想象一件事情,当多个业务功能有一部分基础实现是一致的,如果通过像示例这样的共用一个参数导入,除了减低代码的耦合度,还可以灵活的拆解合并功能需求。
——生命自会找到蓬勃之路。