一、柯里化
发现不懂的概念,先百度为敬
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
光看上面的概念,可能有的小伙子会 丈二的和尚,摸不着头脑(就是我…)。所以我按照自己的理解想了个简单的栗子。(如果各位觉得不妥的话,就当做没看见噢,勿喷,勿喷…)
假如有两个班的小盆友要去春游,本来一辆大巴车一次就可以将所有人载到目的地,但是由于某种原因,大巴车一次只能载一个班的小盆友了,所以大巴车需要分两次
才能载完小盆友。
//bus:大巴车 class1:一班小盆友 class2:二班小盆友
//正常情况下
function bus(class1,class2){
return `${class1}、${class2}到达目的地,任务完成`
}
const res = bus('1班','2班')
console.log(res) // output: 1班、2班到达目的地,任务完成
//特殊情况下
function bus(class1){
console.log(class1+'到达,但任务未完成')
return function(class2){
return `${class1}、${class2}到达目的地,任务完成`
}
}
const res = bus('1班')('2班')
console.log(res) //output:1班到达,但任务未完成
// 1班、2班到达目的地,任务完成
通过上面的栗子,我们可以认为柯里化就是将一个任务分成若干个子任务去执行,当所有
的子任务都完成后这个任务就完成了。(在函数中我们可以将一个子任务看做一个参数)
二、是多此一举?还是匠心独具?
那既然如此,把任务划分为小任务有什么好处呢?如果可以一次性把事情做完,又何必多此一举?
我们可以设想假如我们需要一个方法来判断某个变量是是否为Number
类型。正常情况下,我们可以按照下面的方式完成该方法。
function isNumber(arg){
return Object.prototype.toString.call(arg)=== `[object Number]`
}
isNumber(123) // output: true
如果现在又需要一个能判断某个变量是否为字符串类型
的方法呢?而且不知道啥时候就又要添加一个判断是否为对象类型
的方法。我们当然可以像下面一样一个一个地完成。
function isString(arg){
return Object.prototype.toString.call(arg)=== `[object String]`
}
isString('123') // output: true
但是,仔细对比发现上面两个方法就只有[object ***]
处不一样,所以,为了更加简洁方便,我们可以多增加一个参数来指定要判断的类型
。
function typeIs(type,arg){
return Object.prototype.toString.call(arg) === `[object ${type}]`
}
typeIs('Number',123) //output: true
typeIs('String',123) //output: false
typeIs('Object',{}) //output: true
这样一来,我们就不用为每种类型都单独定义一个方法了。不过,每次调用方法都得传两个参数,比如下面的情况,有没有感觉这个’Number’很多余。
typeIs('Number',123) //true
typeIs('Number','adc') //false
typeIs('Number','sup') //false
终于,我们的柯里化
派上用处了。和我们最初的来一个加一个
的方式相比,这种方式的好处不言而喻。
function typeIs(type){
return function(arg){
return Object.prototype.toString.call(arg) === `[object ${type}]`
}
}
let isNumber = typeIs('Number')
isNumber(1234) // true
isNumber('1234') // false
let isArray = typeIs('Array')
isArray([]) // true
isArray({}) //false
三、柯里化通用式
二中的栗子实际上就是对函数typeIs(type,arg){}
的柯里化。既然上面的函数可以被柯里化,那么其他函数自然也能,而将一个函数柯里化的通用方法我们称之为柯里化通用式
。
柯里化通用式的封装过程大致分为如下几步:
首先,柯里化通用式应该是一个高阶函数(高阶函数指的是接受一个函数或者返回一个函数的函数),其次,在收集完所有参数前,每一次接收到新的参数后
都应该返回
一个能接收参数的函数
,最后,进行判断
,当所有参数收集完毕时执行原函数并返回结果。
function currying(fn){
//***一个函数的长度就是它参数的个数***
let fnLen = fn.length;
//返回一个收集参数函数
return function collectArgs(){
//将收集的参数保存在args中
let args = [...arguments];
//参数收集完毕,将所有的参数传入fn中执行并返回执行结果
if(args.length >= fnLen){
return fn(...args);
}
//参数还没有收集完毕,返回一个函数,并于其中递归调用收集参数函数
return function(){
return collectArgs(...args,...arguments);
}
}
}
我们可以直接拿函数typeIs(type,arg){}
来实验:
function typeIs(type,arg){
return Object.prototype.toString.call(arg) === `[object ${type}]`
}
const whatType = currying(typeIs)
const isString = whatType('String')
const isArray = whatType('Array')
console.log(isArray([])) // true
console.log(isString(23)) // false
这样比我们自己手动柯里化函数又方便了许多并且具有更强的通用性。
至此,本文就结束了,本人水平有限,如有不妥之处,还请各位海涵和不吝赐教(手动抱拳~)。