摘译自:https://strongloop.com/strongblog/higher-order-functions-in-es6easy-as-a-b-c/ 先来看下高阶函数定义:
- 接受1个或多个函数作为参数
- 返回函数类型
常规ES6箭头函数用法:(返回值类型)
const square = x => x * x;
高阶写法:
const has = p => o => o.hasOwnProperty(p);
const sortBy = p => (a, b) => a[p] > b[p];
理解语法
ES5实现高阶函数,也叫柯里化:
function add(x){
return function(y){
return y + x;
};
}
var addTwo = add(2);
addTwo(3); // => 5
add(10)(11); // => 21
add函数接受x,返回一个函数接受y返回y+x。如何用箭头函数实现同样功能呢?我们知道:
- 箭头函数体是表达式,并且
- 箭头函数隐式返回表达式
所以为了实现高阶函数,我们可以使箭头函数的函数体为另一个箭头函数:
const add = x => y => y + x;
// outer function: x => [inner function, uses x]
// inner function: y => y + x;
这样我们可以创建一个绑定了x的内部函数:
const add2 = add(2);// returns [inner function] where x = 2
add2(4); // returns 6: exec inner with y = 4, x = 2
add(8)(7); // 15
这个例子看起来没什么实际作用,但是很好的展示了如何在返回的函数中引用外部参数。
用户排序例子
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'));
上述代码利用了Array的sort 和 filter 方法,这两个方法都接收一个函数参数,此处我们利用了箭头函数的高阶函数写法返回需要的函数。 对比下直接传入函数的写法:
result = users
.filter(x => x.hasOwnProperty('pets')) //pass Function to filter
.sort((a, b) => a.age > b.age); //pass Function to sort
高阶函数写法:
result = users
.filter(has('pets')) //pass Function to filter
.sort(sortBy('age')); //pass Function to sort
优势在哪?
- 减少代码重复
- 提高代码重用性
- 更容易阅读的代码
假设我们想列出有pets和title的用户,可以采用如下传统写法:
result = users
.filter(x => x.hasOwnProperty('pets'))
.filter(x => x.hasOwnProperty('title'))
...
采用高阶函数写法:
result = users
.filter(has('pets'))
.filter(has('title'))
...
可以明显感受到高阶写法更容易写和维护。
更进一步
假设想实现一个过滤器函数完成如下功能:判断一个对象是否包含指定值的key。之前的has函数用于检查对象key,我们需要在此基础上加入值的检查:
//[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;
所以我们的新函数is做了下面三件事:
- 接收属性名返回函数…
- 接收值返回函数…
- 接收对象,并判读该对象是否有指定的属性名和值,并返回boolean
下面是一个使用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
关于命名习惯
下面的写法需要你花点时间去理解其含义:
const i = x => y => z => h(x)(y) && y[x] == z;
使用一些更明确含义的参数命名:
const is = prop => val => obj => has(prop)(obj) && obj[prop] == val;
继续
如果我们想进一步提供排序功能,但是仅改为降序排列,或者列出不包含某属性的用户,我们需要重新实现诸如 sortByDesc 和 notHas这样的新函数吗?答案是不需要,对于最终返回结果是boolean值的高阶函数,我们可以对其进行取反包装,如下:
//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')));
结论
函数式编程受到越来越多的重视,ES6的箭头函数提供了更简洁方便的JavaScript实现方式,如果你还没有看到这种方法的广泛使用,那么可以预见在未来几个月函数式编程会伴随ES6的普及变得更为流行。即使你不太喜欢这种方式,那么理解高阶函数也是非常有必要的。