JS进阶系列文章
阅读本文章你将收获到:
- 什么是组合函数
- 手写一个组合函数
- 组合函数之结合律
- 什么是
pointfree
- 调试组合函数
组合函数
顾名思义,组合函数是将多个函数按照排序进行执行,前一个函数返回的数据,作为下一个函数的参数,最后返回最终的结果。
例子
同样,我们看个例子,假使我们需要将一个数字先进行乘法运算,后进行求平方。我们会将这两个部分进行拆分成两个基础函数double
、square
。再通过double
函数的返回值传入到square
中调用。
function double(num){
return num*2
}
function square(num){
return num**2
}
// 调用方式
const count=10
const res=square(double(count))
console.log(res);
柯里化和纯函数很容易写出可读性不强代码
square(double(count))
上面这种调用方式代码可读性不是很好,我们可以组合上面两个基础函数。
function composeFn(m,n){
return function(count){
return n(m(count))
}
}
使用组合函数,将两个函数传入,composeFn
会返回一个新的函数,我们只需要将要处理的数据传入到返回的这个新函数,这个新函数将会帮我们处理并返回处理好的数据。
const newFn=composeFn(double,square)
const res2=newFn(count)
console.log(res2);
通过compose
组合而成的函数只能是一个参数,往往我们的基础函数参数可能不止一个,这时候我们将会用到柯里化函数进行处理。
关于柯里化,可以阅读这一篇文章【 聊一聊JavaScript中的柯里化 】
实现组合函数
function MyCompose(...fns) {
var length = fns.length;
// 判断传入的参数是否为函数
for (var i = 0; i < length; i++) {
if (typeof fns[i] !== "function") {
throw new TypeError("Expected arguments are functions");
}
}
// 返回一个接收参数的函数
function compose(...args) {
// 当前执行函数索引
var index = 0;
// 调用当前索引对应的函数
var result = length ? fns[index].apply(this, args) : args;
while (++index < length) {
// 将上一次返回的结果,传入到下一个函数中
var result = fns[index].call(this, result);
}
return result;
}
return compose;
}
解析写法
- 在一开始,函数接收一系列的函数。
- 先判断传入的参数是否全为函数,如果有一个非函数的函数则抛出异常
- 创建一个新函数,它需要接收要处理的参数
- 函数内,声明一个变量,记录当前执行的函数索引,将传入的参数按函数顺序执行
- 最终返回这个函数
结合律
结合律 (英文为 associativity
),函数的组合要满足结合律,例如a
、b
、c
三个函数进行组合,可以将a
和b
进行组合,或者是b
和c
进行组合。这个特性计算结合律。
let fn = compose(a,b,c);
let ass1 = compose(compose(a,b),c);
let ass2 = compose(composea,(b,c));
pointfree
pointfree
是一种编程风格,这种风格也被称之为Tacit programming
,point
代表形参,意思是没有形参的编程风格。
// pointfree风格,带有word形参
var word = (word) => word.toUpperCase();
// pointfree风格,没有任何形参
var word = compose(toUpperCase);
也就是说,我们完全可以把数据处理的过程,定义成一种与参数无关的合成运算。不需要用到代表数据的那个参数,只要把一些简单的运算步骤合成在一起即可。
它省去了对参数命名的麻烦,代码也更加简洁优雅。pointfree
是基础函数的组合的高级函数,这些高级函数往往应用在我们的业务中。
Debug
从上面的知识中,我们知道组合函数是多个函数的组合,当组合函数返回的结果与预期不符合时,如何去调试它?
- 对中间的值进行打印,并且知道当前执行位置
var log = MyCurrying((t, v) => {
console.log(t, v);
return v;
});
- 在组合函数中,按照从左往右的顺序,给个函数套上log函数,就可以知道每次输出的值
var newFn = MyCompose(double,log('double--'),square,log('double--'));
var res2 = newFn(10);
// double-- 20
// double-- 400
// 400