
对于js函数的柯里化深入分析
发布日期:2021-05-08 01:28:07
浏览次数:19
分类:原创文章
本文共 5284 字,大约阅读时间需要 17 分钟。
一、柯里化的理解
-
柯里化
1)柯里化(currying)也称为部分求值,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术
2)所谓"柯里化",就是把一个多参数的函数,转化为单参数函数,并且返回接受余下的参数而且返回结果
3)一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来,待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值 -
柯里化的分析
1)需求分析:实现两数的相加
实例代码:
function add1(x,y){ return x+y; } // 3 console.log(add1(1,2));
2)函数的柯里化,把一个多参数的函数转换为一个单参数的函数,所有的函数只接收一个参数
实例代码:
// 把x、y两个参数变成先用一个函数接收x参数,然后返回一个函数去处理y参数 // 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数 function add2(y){ return function(x){ return x+y; }; } // 3 console.log(add2(2)(1));
- 柯里化函数的封装
1)实例代码:
// 只能扩展一个参数,不支持多参数调用 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); }; };
2)上面的函数封装只能实现一个参数的扩展,无法实现多个参数的扩展,需要修改为支持多参数传递
实例代码:
// 支持多参数传递 function progressCurring(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 progressCurring.call(_this,fn,_args); } // 参数收集完毕,那么就执行fn return fn.apply(this,_args); } }
- 柯里化函数的性能
1)存取arguments对象通常要比存取命名参数要慢一点
2)一些老版本的浏览器在arguments.length的实现上是相当慢的
3)使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
4)创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上
二、柯里化的应用和面试
- 柯里化的应用
1)参数复用,很多地方都要校验是否有数字,其实就是需要将第一个参数reg进行复用,这样别的地方就能够直接调用hasNumber,hasLetter等函数,让参数能够复用,调用起来也更方便
实例代码:
// 正常正则验证字符串 reg.test(txt)// 函数封装后function check(reg, txt) { return reg.test(txt)}check(/\d+/g, 'test') //falsecheck(/[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') // truehasNumber('testtest') // falsehasLetter('21212') // false
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); }}
3)延迟运行,js中经常使用的bind,实现的机制就是Currying
实例代码:
Function.prototype.bind = function (context) { var _this = this var args = Array.prototype.slice.call(arguments, 1) return function() { return _this.apply(context, args) }}
- 柯里化函数的经典面试题一
1) 需求分析:
实现一个add方法,使计算结果能够满足如下预期
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15
2)实例代码:
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; } // 6 console.log( add(1)(2)(3)); // 10 console.log( add(1,2,3)(4)); // 15 console.log( add(1)(2)(3)(4)(5));
- 柯里化函数的经典面试题二
1)需求分析:
实现 sum(1)(2)(3) 返回结果是1,2,3之和
2)实例代码:
// 实现 sum(1)(2)(3) 返回结果是1,2,3之和 function sum(a){ return function(b){ return function(c){ return a + b + c; }; }; }; // 6 console.log(sum(1)(2)(3));
3)实现一个curry函数,将普通函数进行柯里化
实例代码:
function curry(fn,args=[]){ return function(){ let rest = [...args,...arguments]; if(rest.length < fn.length){ return curry.call(this,fn,rest); }else{ return fn.call(this,rest); }; }; } // test function sum2(a,b,c){ return a + b + c; } let sumFn = curry(sum2); // 6 console.log(sumFn(1)(2)(3)); // 6 console.log(sumFn(1)(2,3));
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2025年04月03日 02时20分19秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Neo4j : 通过节点的 id属性 对节点进行查,改,删操作
2019-03-05
Linux标准错误和标准输出重定向到同一个文件
2019-03-05
HTTP Status 404 – Not Found
2019-03-05
【2021年新书推荐】ASP.NET Core 5 and Angular
2019-03-05
python-day3 for语句完整使用
2019-03-05
spring 程序开发步骤
2019-03-05
java基础知识:构造函数
2019-03-05
java基础知识:封装
2019-03-05
linux下安装tomcat服务器
2019-03-05
mysql 中的数据实现递归查询
2019-03-05
linux下远程上传命令scp
2019-03-05
(四)块设备文件
2019-03-05
可重入和不可重入函数
2019-03-05
(2.1)关系模型之关系结构和约束
2019-03-05
DBA 技能图谱1.0
2019-03-05
深入学习C++
2019-03-05
【HTTP】HTTP状态码图解
2019-03-05
《图解TCP/IP》学习——第六章TCP与UDP
2019-03-05