本节书摘来华章计算机出版社《JavaScript应用程序设计》一书中的第2章,第2.14节,作者:Eric Elliott 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.14 函数式编程
函数式编程是一种使用高阶函数(非对象和数据)来进行代码组织和复用的编程风格。高阶函数将普通函数视为数据或参数,或将函数作为其返回结果。高阶函数在JavaScript中有多种用途,它是非常强大的代码重用工具,来看下面的示例。
函数式编程常常用于将算法从数据类型中抽象出来,这样的好处是可以减少算法复用时为支持多种数据类型而导致的代码冗余。如果算法没有经过抽象,那么在操作集合时,你将需要为集合中每一种不同的数据类型分别构建相应的处理函数,而这些函数间的区别仅仅只是传参类型略有不同而已,这类问题在多数应用中都存在。
注意: 你有一箩筐函数,它们除了在操作对象的数据类型上有差异外,其他方面几乎没什么不同。让我来告诉你,你已经违反了软件设计中最为重要的一个原则:DRY(不要重复造轮子),这个例子是一个典型的“代码异味”。
举例来说,假设你需要对两个商品列表按照价格高低进行排序,其中一个商品列表是演唱会门票,价格字段为ticketPrice,另一个为书本,价格字段为price。
这里你可以选择将两个字段合并为一个更为通用的字段,随后再搭配一个处理该字段类型(鸭子类型)的通用函数,不过这种代码重构的方式有些怪异。
相反,你可以为排序时的比较操作传入一个函数:
var shows = [
{
artist: 'Kreap',
city: 'Melbourne',
ticketPrice: '40'
},
{
artist: 'DJ EQ',
city: 'Paris',
ticketPrice: '38'
},
{
artist: 'Treasure Fingers',
city: 'London',
ticketPrice: '60'
}
],
books = [
{
title: 'How to DJ Proper',
price: '18'
},
{
title: 'Music Marketing for Dummies',
price: '26'
},
{
title: 'Turntablism for Beginners',
price: '15'
}
];
test('Datatype abstraction', function () {
var sortedShows = shows.sort(function (a, b) {
return a.ticketPrice < b.ticketPrice;
}),
sortedBooks = books.sort(function (a, b) {
return a.price < b.price;
});
ok(sortedShows[0].ticketPrice >
sortedShows[2].ticketPrice,
'Shows sorted correctly.');
ok(sortedBooks[0].price >
sortedBooks[1].price,
'Books sorted correctly.');
});
高价函数常用来从算法实现中抽象出迭代器模板,在前一个示例中你也注意到了,实际的迭代操作是由数组内置方法Array.prototype.sort()来完成的,你甚至不用去手写一个for循环。传统的迭代方式大致是这样:
test('Traditional for loop', function () {
var i,
length = books.length;
for (i = 0; i < length; i++) {
books[i].category = 'music';
}
ok(books[0].category === 'music',
'Books have categories.');
});
在对集合中的元素循环遍历处理方面,JavaScript提供了不少将函数作为参数传入的迭代器方法,其中最为常见数组迭代器莫过于forEach()方法。假设你想为列表中的每一本书都添加category字段,又不想手写循环去跟踪那些临时索引变量,forEach()方法可以帮到你,就像下面这样:
test('Iterator abstraction', function () {
books.forEach(function (book) {
book.category = 'music';
});
ok(books[0].category === 'music',
'Books have categories.');
});
高价函数另一个使用场景是偏函数与函数加里化。