JavaScript 函数组合
一、什么是函数组合
函数组合 compose()
可以把指定的函数串联起来执行,一个函数的输出结果是另一个函数的输入参数,像多米诺骨牌一样依次执行输出最终结果。
类似数学中的复合函数。函数 f
和 g
的组合可以被定义为 f(g(x))
,从内到外依次求值。举个例子:
实现输入 Tim, Cook
,打印出 HELLO,TIM COOK
,用函数组合的方式,对两个函数 greeting()
, toUpperCase()
进行组合:
const greeting = (x, y) => {
return `HELLO, ${x} ${y}`;
}
const toUpperCase = (x) => {
return x.toUpperCase();
}
const fn = compose(toUpperCase, greeting);
console.log(fn('Tim', 'Cook')); // HELLO, TIM COOK
上面初始函数 greeting()
的执行结果作为参数传递给 toUpper()
,再执行得到最后结果。函数组合的好处在于可专注编写基本函数,将多个单一功能的纯函数进行组合。
二、函数组合的实现
函数组合 compose()
要满足能传入多个基本函数,underscore 的实现如下:
const greeting = (x, y) => {
return `HELLO, ${x} ${y}`;
}
const toUpperCase = (x) => {
return x.toUpperCase();
}
function compose() {
const args = arguments;
// 倒序调用
const start = args.length - 1;
return function () {
let i = start;
let result = args[start].apply(this, arguments);
while (i--) result = args[i].call(this, result);
return result;
};
};
const fn = compose(toUpperCase, greeting);
console.log(fn('Tim', 'Cook')); // HELLO, TIM COOK
三、函数组合的应用
假设要从服务器获取以下数据:
const data = {
result: "SUCCESS",
tasks: [
{
id: 104, complete: false, priority: "high",
dueDate: "2013-11-29", username: "Scott",
title: "Do something", created: "9/22/2013"
},
{
id: 105, complete: false, priority: "medium",
dueDate: "2013-11-22", username: "Lena",
title: "Do something else", created: "9/22/2013"
},
{
id: 107, complete: true, priority: "high",
dueDate: "2013-11-22", username: "Mike",
title: "Fix the foo", created: "9/22/2013"
},
{
id: 108, complete: false, priority: "low",
dueDate: "2013-11-15", username: "Punam",
title: "Adjust the bar", created: "9/25/2013"
},
{
id: 110, complete: false, priority: "medium",
dueDate: "2013-11-15", username: "Scott",
title: "Rename everything", created: "10/2/2013"
},
{
id: 112, complete: true, priority: "high",
dueDate: "2013-11-27", username: "Lena",
title: "Alter all quuxes", created: "10/5/2013"
}
]
};
需要写一个名为 getIncompleteTaskSummaries
的函数,接收一个 username
作为参数,从服务器获取数据,然后筛选出这个用户的未完成的任务的 ids
、priorities
、titles
、和 dueDate
数据,并按照日期升序排序。以 Scott
为例,最终筛选出的数据为:
[
{
id: 110, title: "Rename everything",
dueDate: "2013-11-15", priority: "medium"
},
{
id: 104, title: "Do something",
dueDate: "2013-11-29", priority: "high"
}
]
实现如下:
- 未使用函数组合
- 使用函数组合
// 过程式编程
const fetchData = function () {
// 模拟
return Promise.resolve(data)
};
const getIncompleteTaskSummaries = function (membername) {
return fetchData()
.then(function (data) {
return data.tasks;
})
.then(function (tasks) {
return tasks.filter(function (task) {
return task.username == membername
})
})
.then(function (tasks) {
return tasks.filter(function (task) {
return !task.complete
})
})
.then(function (tasks) {
return tasks.map(function (task) {
return {
id: task.id,
dueDate: task.dueDate,
title: task.title,
priority: task.priority
}
})
})
.then(function (tasks) {
return tasks.sort(function (first, second) {
const a = first.dueDate,
b = second.dueDate;
return a < b ? -1 : a > b ? 1 : 0;
});
})
.then(function (task) {
console.log(task)
})
};
getIncompleteTaskSummaries('Scott')
这里直接使用 ramda.js 的函数组合:
// 使用 compose
const fetchData = function () {
return Promise.resolve(data)
};
const getIncompleteTaskSummaries = function (membername) {
return fetchData()
.then(R.compose(
console.log,
R.sortBy(R.prop('dueDate')),
R.map(R.pick(['id', 'dueDate', 'title', 'priority'])
),
R.filter(R.propEq('complete', false)),
R.filter(R.propEq('username', membername)),
R.prop('tasks'),
))
};
getIncompleteTaskSummaries('Scott')