# ES6 中的高阶函数
高阶函数是执行以下一项或两项操作的函数
- 将一个或多个函数作为参数
- 返回一个函数作为结果
下面代码会发生什么呢?
const has = p => o => o.hasOwnProperty(p);
const sortBy = p => (a, b) => a[p] > b[p];
1
2
2
# 理解语法
为了说明如何使用箭头编写高阶函数,让我们看一个经典示例:add。在 ES5 中,它看起来像这样:
function add(x){
return function(y){
return y + x;
};
}
var addTwo = add(2);
addTwo(3); // => 5
add(10)(11); // => 21
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
我们的 add 函数接受 x 并返回一个接受 y 的函数,该函数返回 y + x。我们如何用箭头函数写这个?我们知道
- 箭头函数定义是一个表达式
- 箭头函数隐式返回单个表达式的结果
所以我们必须做的就是让我们的箭头函数体成为另一个箭头函数,因此:我们的add用es6表示如下
const add = x => y => y + x;
const add2 = add(2);
add2(4);
add(8)(7);
1
2
3
4
2
3
4
# 对我们的用户进行排序
# 使用高阶函数
const has = p => o => o.hasOwnProperty(p);
const sortBy = p => (a, b) => a[p] > b[p];
let result;
let users = [
{ name: 'Qian', age: 27, pets : ['Bao'], title : 'Consultant' },
{ name: 'Zeynep', age: 19, pets : ['Civelek', 'Muazzam'] },
{ name: 'Yael', age: 52, title : 'VP of Engineering'}
];
result = users
.filter(has('pets'))
.sort(sortBy('age'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 不使用高阶函数
const has = p => o => o.hasOwnProperty(p);
const sortBy = p => (a, b) => a[p] > b[p];
let result;
let users = [
{ name: 'Qian', age: 27, pets : ['Bao'], title : 'Consultant' },
{ name: 'Zeynep', age: 19, pets : ['Civelek', 'Muazzam'] },
{ name: 'Yael', age: 52, title : 'VP of Engineering'}
];
result = users
.filter(x => x.hasOwnProperty('pets'))
.sort((a, b) => a.age > b.age);
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 为啥这样很有用呢?
原因如下
- 它减少了重复的代码
- 更高的代码复用率
- 它增加了代码含义的清晰度
假如我们想要筛选有pets和title
的数据,我们可以这样写
result = users
.filter(x => x.hasOwnProperty('pets'))
.filter(x => x.hasOwnProperty('title'))
1
2
3
2
3
上面的代码只是混乱:它没有增加清晰度,只是为了阅读和写作。与使用我们的 has 函数的相同代码进行比较:
result = users
.filter(has('pets'))
.filter(has('title'))
1
2
3
2
3
上面的写法更短也更容易写,这样可以减少拼写错误。我认为这段代码也更加清晰,因为它的用途一目了然。
# 更近一步
让我们创建一个函数,该函数将生成一个过滤器函数,用于检查对象是否具有具有特定值的键。
我们的函数检查了一个键,但是为了检查值,我们的过滤器函数需要知道两件事(键和值)
//[p]roperty, [v]alue, [o]bject:
const is = p => v => o => o.hasOwnProperty(p) && o[p] == v;
// broken down:
// outer: p => [inner1 function, uses p]
// inner1: v => [inner2 function, uses p and v]
// inner2: o => o.hasOwnProperty(p) && o[p] = v;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
所以我们名为“is”的新函数做了三件事
- 获取一个属性名称并返回一个函数
- 获取一个值并返回一个函数
- 获取一个对象并测试该对象是否具有指定值的属性,最后返回一个布尔值。
下面是一个使用它来过滤我们的用户的例子
const titleIs = is('title');
// titleIs == v => o => o.hasOwnProperty('title') && o['title'] == v;
const isContractor = titleIs('Contractor');
// isContractor == o => o.hasOwnProperty('contractor') && o['title'] == 'Contractor';
let contractors = users.filter(isContractor);
let developers = users.filter(titleIs('Developer'));
let user = {name: 'Viola', age: 50, title: 'Actress', pets: ['Zak']};
isEmployed(user); // true
isContractor(user); // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 注意书写风格
对比下面两个函数,那个更易读
const i = x => y => z => h(x)(y) && y[x] == z;
const is = prop => val => obj => has(prop)(obj) && obj[prop] == val;
1
2
3
2
3
编写单行函数时有一种趋势,即以牺牲可读性为代价,尽可能简洁。但是简短而无意义的名称使函数看起来很简单,但是难以理解。
所以可以使用更有意义的函数名或者参数名。
# 还有一件事
如果您想按年龄降序而不是升序排序怎么办?或者找出谁不是员工? 我们是否必须编写新的实用函数 sortByDesc 和 notHas?不我们没有。 我们可以将返回布尔值的函数包装起来,用一个函数来反转布尔值
//take args, pass them thru to function x, invert the result of x
const invert = x => (...args) => !x(...args);
const noPets = invert(hasPets);
let petlessUsersOldestFirst = users
.filter(noPets)
.sort(invert(sortBy('age')));
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8