-
柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
-
通俗的解释:
- 柯里化就是一个函数原本有多个参数,只传入一个参数,生成一个新函数,由新函数接收剩下的参数来运行得到结果.
- 与柯里化类似的二个有意思的概念 ====>偏函数与高阶函数大家知道他的定义吗?下文会简单的给出答案.
-
在JavaScript高级程序设计 里这样描述的:
- 柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要的参数.
- 通用方式:
function curry(fn){ var args = Array.prototype.slice.call(arguments,1); return function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.contact(innerArgs); return fn.apply(null,finalArgs); } } function add (num1,num2){ return num1 + num2; } var curriedAdd = curry(add,5); alert(curriedAdd(3);// 8
- 在书中还有一个关于柯里化与绑定函数的结合示例:
function bind(fn,context){ var var args = Array.prototype.slice.call(arguments,2); return function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.contact(innerArgs); return fn.apply(context,finalArgs); } } //通过柯里化和绑定函数可以实现强大的动态函数创建功能.
以上是JavaScript高级程序设计里对于柯里化的解释与简单示例.
-
现在我提出个问题,如何判断一个标签是否为原生的html标签,给大家二分钟思考下.
- 简单实现:最最简单了
var str = 'html,body,base,head,link,meta,style,title,' + 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' + 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' + 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' + 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' + 'embed,object,param,source,canvas,script,noscript,del,ins,' + 'caption,col,colgroup,table,thead,tbody,td,th,tr,' + 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' + 'output,progress,select,textarea,' + 'details,dialog,menu,menuitem,summary,' + 'content,element,shadow,template,blockquote,iframe,tfoot'; function isHTMLTag ( tag, str ) { var list = str.split(','); var flag =false; for (var i = 0; i < list.length; i++) { if(list[i] == tag){ flag = true; break; } } return flag; } var isHTMLTag = isHTMLTag('div',str);
- 这个方法是不是超级简单就实现了,但是这个方法单独使用ok一点问题没有,但是如果我们调用100次呢,我们循环就是(100*str的个数)次.
- 大家可能会说不用for循环,用indexOf就没有循环,其实indexOf的内部实现也是有循环的.
- 简单实现:最最简单了
-
接下来我们来看看我们熟悉的Vue框架的源码中是如何实现这个功能的.
- 源码其实就是通过函数柯里化,闭包等技巧来优化我们的操作的.
/** * Make a map and return a function for checking if a key * is in that map. */ function makeMap ( str, expectsLowerCase ) { var map = Object.create(null); var list = str.split(','); for (var i = 0; i < list.length; i++) { map[list[i]] = true; } return expectsLowerCase ? function (val) { return map[val.toLowerCase()]; } : function (val) { return map[val]; } } var isHTMLTag = makeMap( 'html,body,base,head,link,meta,style,title,' + 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' + 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' + 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' + 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' + 'embed,object,param,source,canvas,script,noscript,del,ins,' + 'caption,col,colgroup,table,thead,tbody,td,th,tr,' + 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' + 'output,progress,select,textarea,' + 'details,dialog,menu,menuitem,summary,' + 'content,element,shadow,template,blockquote,iframe,tfoot' ); var isTextInputType = makeMap('text,number,password,search,email,tel,url');//这个是判断是否是可输入框的 var isHTMLTag = isHTMLTag('div'); //没想到吧,Vue的源码里判断是否是原生的Html标签竟然是将所有的原生标签枚举出来的,惊喜不惊喜,意外不意外
- 但是源码呢只是初始化的时候调用(str的个数)次,然后每次使用的时候因为函数柯里化因为闭包只需要根据map的值去检索,性能得到了很大的提升,虽然代码量变多了,也损失了部分性能与增加了额外开销,但是整体性能得到了很大的提升.
- 这个makeMap方法我们可以拿出来用到自己的项目中去,也能算是一个亮点哟.
-
好了,基础的大概就这些了,现在我们来看几道关于柯里化的面试题.
- 实现add(1)(2)(3)(4)...无限累加.
- 实现add(1)(2)(3)(4)...并且支持add(1,2)(3,4)(5)..或者add(1,2,3,4,5).
-
先来解决无限累加问
-
解决这个问题首先要知道:toString方法用于将当前对象以字符串的形式返回,toString()函数的返回值为String类型,返回当前对象的字符串形式,并且JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要.
function add(a){ function s(b){ a = a + b; return s; } s.toString = function(){ return a; } return s; } var a = add(1)(2)(3)(4).toString();//10
-
现在我们来解决第二个问题,下面这个该怎么处理呢
var a = add(1)(2)(3)(4); //10 var b = add(1, 2, 3, 4); //10 var c = add(1, 2)(3, 4); //10 var d = add(1, 2, 3)(4); //10 function add() { var args = Array.prototype.slice.call(arguments);//伪数组转数组 function hardAdd() { var innerArgs = Array.prototype.slice.call(arguments,0);//伪数组转数组 args = args.concat(innerArgs);//合并参数 return hardAdd; } hardAdd.toString = function () {//重写toString方法 return args.reduce((previous, current) => {//用reduce做累加 return previous + current }); } return hardAdd; }
-
- 其实还有一个题是已知参数数量,累加达到参数数量后返回结果,大家可以试试啦
fn(a,b,c,d) => fn(a)(b)(c)(d);//这样的 //提示:fn.length这个返回的是啥呢?希望留言
-
哦,对了,接下来跟大家说下 偏函数 还有 高阶函数 的定义吧(柯里化也放一起吧,便于大家比较). (ps:我认为柯里化就是每次只能传一个参数才算柯里化,但也不完全是)
- 柯里化:一个函数原本有多个参数,只传入一个参数,生成一个新函数,由新函数接收剩下的参数来运行得到结果.
- 偏函数:一个函数原本有多个参数,只传入一部分参数,生成一个新函数,由新函数接收剩下的参数来运行得到结果.
- 高阶函数:一个函数参数是一个函数,该函数对参数进行加工,得到一个函数,这个加工用的函数就是高阶函数,或者函数作为返回值也是的.