@H_301_0@几十年来,函数式编程一直是计算机科学狂热者的至爱,由于数学的纯洁性和谜一般的本质, 它被埋藏在计算机实验室,只有数据学家和有希望获得博士学位的人士使用。但是现在,它正经历一场复兴, 这要感谢一些现代语言比如Python,Julia,Ruby,Clojure以及——但不是最后一个——Javascript。
@H_3010@你是说Javascript?这个WEB脚本语言?没错!
@H301_0@Javascript已经被证明是一项长期以来都没有消失的重要的技术。这主要是由于它扩展的一些框架和库而使其具有重生的能力, 比如backbone.js,jQuery,Dojo,underscore.js等等。这与Javascript函数式编程语言的真实身份直接相关。 对Javascript函数式编程的理解很重要,并且在相当长的一段时间会对各种水平的程序员很有用。
@H_301_0@为什么呢?函数式编程非常强大、健壮并且优雅。它对于大型数据结构非常有用并且高效。 Javascript作为一个客户端脚本语言,在应对日益复杂的网站时,函数式地操作DOM、 组织API响应以及完成一些其它任务会非常有好处。
@H_301_0@在这本书里,你将会学习用Javascript进行函数式编程所需要知道的一切:如何用函数式编程构建你的Javascript web应用, 如何解锁Javascript隐藏的力量,如何编写更强大的代码,并且由于程序更小,使得代码更容易维护,能够更快被下载, 并且花费更少的开支。你还会学到函数式编程的核心概念,以及如何将它们应用到Javascript, 还有将Javascript作为函数式语言时如何回避一些问题,如何在Javascript中混合使用函数式编程和面向对象编程。
@H_3010@不过在我们开始前,先来做个实验。
@H3010@
例子
@H301_0@也许快速举个例子是介绍Javascript函数式编程最好的方式。我们将用Javascript完成一些任务—— 一个使用传统、原生的方法,另一个使用函数式编程。然后我们将会比较这两种方法。
@H_3010@
应用——一个电子商务网站
@H3010@为了追求真实感,我们来做一个电子商务网站,一个邮购咖啡豆的公司。这个网站会销售好几种类型的咖啡, 有不同的品质,当然也有不同的价格。
@H301_0@
命令式方法
@H_301_0@首先,我们开始写程序。为了让这个例子接地气,我们需要创建一些对象来保存数据。如果需要的话我们可以从数据库里取值。 但是现在我们假设他们是静态定义的:
函数计算价格
// 根据size打印到一个HTML的列表中
function printPrice(coffee,size) {
if (size == 'small') {
var price = coffee.basePrice + 2;
}
else if (size == 'medium') {
var price = coffee.basePrice + 4;
}
else {
var price = coffee.basePrice + 6;
}
// create the new html list item
var node = document.createElement("li");
var label = coffee.name + ' ' + size;
var textnode = document.createTextNode(label+' price: $'+price);
node.appendChild(textnode);
document.getElementById('products').appendChild(node);
}
// 现在我们只需根据咖啡的各种价格和size的组合调用printPrice函数
printPrice(columbian,'small');
printPrice(columbian,'medium');
printPrice(columbian,'large');
printPrice(frenchRoast,'small');
printPrice(frenchRoast,'medium');
printPrice(frenchRoast,'large');
printPrice(decaf,'small');
printPrice(decaf,'medium');
printPrice(decaf,'large');
@H_301_0@如你所见,这个代码非常基础。如果现在有更多的咖啡种类而不只是这三个改怎么办?如果有20个,甚至50个? 如果有更多的size呢?如果有有机和无机之分呢?这将会很快将代码量变得巨大无比!
@H_301_0@采用这种方法,我们让机器去打印每一种咖啡类型和每一个size。这就是采用这种命令式方法的基本问题。
@H_301_0@函数式编程
@H_301_0@命令式的代码一步一步地告诉电脑需要做什么来解决问题,相反,函数式编程追求用数学方式来描述问题, 其余的交给电脑来做。 @H_301_0@通过更函数式一些的方式,同样的应用可以这样来写:函数对象
var columbian = function(){
this.name = 'columbian';
this.basePrice = 5;
};
var frenchRoast = function(){
this.name = 'french roast';
this.basePrice = 8;
};
var decaf = function(){
this.name = 'decaf';
this.basePrice = 6;
};
// 为每种size通过字面量创建对象
var small = {
getPrice: function(){return this.basePrice + 2},getLabel: function(){return this.name + ' small'}
};
var medium = {
getPrice: function(){return this.basePrice + 4},getLabel: function(){return this.name + ' medium'}
};
var large = {
getPrice: function(){return this.basePrice + 6},getLabel: function(){return this.name + ' large'}
};
// 将所有咖啡的种类和size放到数组里
var coffeeTypes = [columbian,frenchRoast,decaf];
var coffeeSizes = [small,medium,large];
// 创建由上面内容组成的新对象,并把它们放到一个新数组里
var coffees = coffeeTypes.reduce(function(prevIoUs,current) {
var newCoffee = coffeeSizes.map(function(mixin) {
// `plusmix`是函数式的minxin,见第7章
var newCoffeeObj = plusMixin(current,mixin);
return new newCoffeeObj();
});
return prevIoUs.concat(newCoffee);
},[]);
// 现在我们已经定义了如何获得所有咖啡种类和size组合方式的价格,现在可以直接打印它们了
coffees.forEach(function(coffee){
printPrice(coffee.getPrice(),coffee.getLabel());
});
@H_301_0@首先需要明确的是这个代码更加模块化了。现在新增一种size或者信新增一个咖啡种类就像下面的代码这样简单:
IoUs,current) {
var newCoffee = coffeeSizes.map(function(mixin) {
// `plusMixin` function for functional mixins,see Ch.7
var newCoffeeObj = plusMixin(current,[]).forEach(function(coffee) {
printPrice(coffee.getPrice(),coffee.getLabel());
});
@H_301_0@这样,控制流没有像命令式代码那样从头到尾的顺序进行。在函数式编程里,map函数和其它高阶函数代替了for和while循环, 只有少量关键的代码是在顺序执行。 这使得新接触的人在阅读这样范式的代码有些困难,但是一旦你能够欣赏它,你就会发现这根本没啥难的, 而且这样写看起来更好。
@H_301_0@这个例子仅仅是刚开始展露Javascript中函数式编程能做什么。通过这本书,你将会看到更多函数式实现的强悍的例子。
@H_301_0@