Javascript 中的 高阶函数与 Partial Application
Javascript 中的 高阶函数与 Partial Application
本文会简单介绍下函数式编程中常用的高阶函数与 Partial Application 以及他们在 Javascript 中的应用。首先会简短的介绍下这两者的意思,以及他们的用途;之后会附上一个简单的用例,最后再阐述下使用他们进行抽象的优点。
什么是高阶函数?
高阶函数就是一个函数的返回值也是一个函数,即
function highOrderFunction(a) {
const c = someOperation(a);
return () => console.log(c);
}
什么是 Partial Application?
Partial Application 是函数式编程中一个很常见的模式,简单来说,假设有函数
f(a, b, c)
, 我们只传入参数a
和 c
, 使得函数f
返回一个输入参数为b
的函数,在 javascript 中,即:
const a = 'a';
const c = 'c';
// 噢!我们这里并拿不到 b 啊!
const g = (b) => f(a, b, c);
// 那就先把 a 和 c 给函数 f 吧! b 之后再想办法
foo(g);
function foo(g) {
// B 只有这里才有哦
const b = 'b';
return g(b);
}
为什么需要 Partial Application-0
简单来说,就两点
- 使得函数的输入更符合逻辑
- 解耦
例子
在程序设计中,经常会犯的一个错误就是 Banana-Jungle Problem. 一个函数需要的明明只是一根香蕉而已,但是有时候程序员会把一整个雨林都传进去,然后在该函数内拿出一根香蕉。举例来说,假设你要订外卖,你需要手机,需要外卖店的商家,需要你的地址,还要知道你想要吃啥
function callDelivery(phone, provider, address, food) {
return phone.call(provider, address, food);
}
callDelivery(phone, provider, address, food);
但是其实我们仔细想一想,叫外卖有这么复杂吗?地址是预存的,只有想要点的食物和商家才会变,因此这完全可以写成
// 创建一个有了菜就行了的订单
function createOrder(phone, address) {
return (provider, food) => phone.call(address);
}
// 现在只要把我们想吃的说出来就行了!
function orderFood(order, provider, food) {
return order(provider, food);
}
// 调用的时候这么调
const order = createOrder(phone, address);
orderFood(order, provider, food);
为什么需要 Partial Application-1
逻辑
为什么说这样的输入更符合逻辑呢?从例子上讲,因为你绝大多数情况下都不会在订外卖的时候思考自己家的地址,现在的 App 都会为你保存地址的吧?你唯一需要纠结的就是到底选哪家,到底吃什么,仅此而已。你真正需要的其实就是动动脑子选食物,完事!
程序设计
这样实际的例子在项目中可能很难找到,但是如果细心的去思考每个函数的作用,尤其是一些超过30行的函数的时候,经常都会有一些同样的问题——某一个函数做的事情太多了(违背单一职责原则,即使是函数式编程中,一个函数也只应该做一件事情),又是打开App,又是订单,又是选地址。实际上这都是可以分开的。
解耦
所谓耦合,就是相关的意思,在第一个例子中,叫外卖这个行为,与手机,商家,地址与菜品都有耦合关系。而抽象成两个行为(当然还能拆的更碎,具体取决于业务和代码情况) 可以降低这种耦合性。
在作为对比的例子中,订外卖被拆解成了两个行为,选菜,以及(打开App和最后的选地址)。这个括号表示这两个行为是在一起的。 (P.S. 这也是函数式编程不普及的原因,她的思考模式并不像面向对象编程那样的显而易见) 选菜的行为只要知道商家,菜品和怎么去订就行了。而怎么去定只要知道手机和自己家的地址就行了,他们之间完全没有交集,也不应该有交集。这样程序的耦合性就非常低了。