本文摘要自修言的小册JavaScript 函数式编程实践指南
导语:我们都知道数组是可以链式调用的,像这样。
const arr = [1, 2, 3]
arr.map(mapArr).filter(filterArr).reduce(reduceArr)
那为什么数组可以实现链式调用,或者说满足什么条件才可以链式调用呢?
- 它们都挂载在Array原型的Array.prototype上
- 它们在结束之后都会返回新的Array
- 既然return出来的也是Array,那么自然可以继续访问原型Array.prototype上的方法了。
综上所述,链式调用是有前提的。
它的本质是,是通过在方法中返回对象实例本身的 this/ 与实例 this 相同类型的对象,达到多次调用其原型(链)上方法的目的。
那我们在实际开发中,经常会遇到对数据进行复杂的处理,要经过好几个函数才可以处理完,就像这样:
function add4(num) {
return num + 4
}
function multiply3(num) {
return num*3
}
function divide2(num) {
return num/2
}
以我们朴实无华的想法,应该怎样去写呢?
const sum = add4(multiply3(divide2(num)))
3个处理看起来还可以,那如果多一点呢?
const sum = square(minus10(computeWithBonus(add4(muliply3(divide2(num))))))
虽然我好像看到了我自己的代码,但是,家人们不重要,往下看….马上讲怎么摆脱回调地狱。
答案就是借助reduce实现pipe。(reduce不熟的记得先恶补之后再回来食用)
还是以上面的例子🌰。
reduce(callback,init)
function callback(pre,cur){}
其实就是把三个函数放在一个数组里,使用reduce,每次把计算出来的结果再作为下一个函数的参数,reduce第二个参数是
init也就是初始值,
第一次,pre就是初始值,也就是init的值,cur就是数组里的第一项;
第二次,pre是第一个函数计算出来之后的结果,cur是数组里的第二项;
第三次,pre是第二个函数计算出来之后的结果,cur是数组里的第三项;
返回计算之后的结果。
这个过程懂了,我们代码怎么实现呢?
funArr.reduce(callback, 0)
function callback(pre, cur) {
return cur(pre)
}
看起来也不难对吧,组合一下。
const pipe = funArr => {
function callback(pre, cur) {
return cur(pre)
}
return funArr.reduce(callback, 0)
}
console.log(pipe([add4, multiply3, divide2]))
现在功能是实现了,我们不能每次都以0位初始值吧,我们需要动态的传参进去。
const pipe = (funArr) => {
function funHandle(pre, cur) {
return cur(pre)
}
return function (param) {
return funArr.reduce(funHandle, param)
}
}
const compose = pipe([add4, multiply3, divide2])
console.log(compose(10))
怕小伙伴看不懂,我再详细的讲解一下这个方法。
const pipe = (funArr) => {
console.log(1)
function funHandle(pre, cur) {
return cur(pre)
}
return function (param) {
return funArr.reduce(funHandle, param)
}
}
console.log(2)
const compose = pipe([add4, multiply3, divide2])
console.log(3)
console.log(compose(10))
首先代码从上向下走,会走到console.log(2);
接着走到pipe([add4, multiply3, divide2])
,这是对pipe的调用,所以对走进pipe方法里,执行
console.log(1)
,此时并不会执行return里面的内容,它只是返回了一个函数,但是并没有执行return里面的内容;
接着继续往下执行console.log(3)
,再执行 console.log(compose(10))
,compose = pipe
的返回值
,所以compose(10)才是pipe返回的方法执行的时候。
最后,我们不去手动的拼凑数组,而是采用…
的形式,收集传参。
const pipe = (...funArr) => {
function funHandle(pre, cur) {
return cur(pre)
}
return function (param) {
return funArr.reduce(funHandle, param)
}
}
const compose = pipe(add4, multiply3, divide2)
console.log(compose(10))
甚至你可以更加的精简
const pipe =
(...funArr) =>
param =>
funArr.reduce((pre, cur) => cur(pre), param)
上面就是使用reduce实现pipe的全过程了,那Compose怎么实现呢?
就是reduce倒过来实现。
function compose(...funcs) {
function callback(input, func) {
return func(input)
}
return function(param) {
return funcs.reduceRight(callback,param)
}
}