Javascript函数式编程简单介绍


Posted in Javascript onOctober 11, 2015

几十年来,函数式编程一直是计算机科学狂热者的至爱,由于数学的纯洁性和谜一般的本质, 它被埋藏在计算机实验室,只有数据学家和有希望获得博士学位的人士使用。但是现在,它正经历一场复兴, 这要感谢一些现代语言比如Python,Julia,Ruby,Clojure以及——但不是最后一个——Javascript。

你是说Javascript?这个WEB脚本语言?没错!

Javascript已经被证明是一项长期以来都没有消失的重要的技术。这主要是由于它扩展的一些框架和库而使其具有重生的能力, 比如backbone.js,jQuery,Dojo,underscore.js等等。这与Javascript函数式编程语言的真实身份直接相关。 对Javascript函数式编程的理解很重要,并且在相当长的一段时间会对各种水平的程序员很有用。

为什么呢?函数式编程非常强大、健壮并且优雅。它对于大型数据结构非常有用并且高效。 Javascript作为一个客户端脚本语言,在应对日益复杂的网站时,函数式地操作DOM、 组织API响应以及完成一些其它任务会非常有好处。

在这本书里,你将会学习用Javascript进行函数式编程所需要知道的一切:如何用函数式编程构建你的Javascript web应用, 如何解锁Javascript隐藏的力量,如何编写更强大的代码,并且由于程序更小,使得代码更容易维护,能够更快被下载, 并且花费更少的开支。你还会学到函数式编程的核心概念,以及如何将它们应用到Javascript, 还有将Javascript作为函数式语言时如何回避一些问题,如何在Javascript中混合使用函数式编程和面向对象编程。

不过在我们开始前,先来做个实验。

例子

也许快速举个例子是介绍Javascript函数式编程最好的方式。我们将用Javascript完成一些任务—— 一个使用传统、原生的方法,另一个使用函数式编程。然后我们将会比较这两种方法。

应用——一个电子商务网站

为了追求真实感,我们来做一个电子商务网站,一个邮购咖啡豆的公司。这个网站会销售好几种类型的咖啡, 有不同的品质,当然也有不同的价格。

命令式方法

首先,我们开始写程序。为了让这个例子接地气,我们需要创建一些对象来保存数据。如果需要的话我们可以从数据库里取值。 但是现在我们假设他们是静态定义的: 

// create some objects to store the data.
var columbian = {
 name: 'columbian',
 basePrice: 5
};
var frenchRoast = {
 name: 'french roast',
 basePrice: 8
};
var decaf = {
 name: 'decaf',
 basePrice: 6
};
// 我们将使用辅助函数计算价格
// 根据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');

如你所见,这个代码非常基础。如果现在有更多的咖啡种类而不只是这三个改怎么办?如果有20个,甚至50个? 如果有更多的size呢?如果有有机和无机之分呢?这将会很快将代码量变得巨大无比!

采用这种方法,我们让机器去打印每一种咖啡类型和每一个size。这就是采用这种命令式方法的基本问题。

函数式编程

命令式的代码一步一步地告诉电脑需要做什么来解决问题,相反,函数式编程追求用数学方式来描述问题, 其余的交给电脑来做。

通过更函数式一些的方式,同样的应用可以这样来写:

// 从接口中分解数据和逻辑
var printPrice = function(price, label) {
 var node = document.createElement("li");
 var textnode = document.createTextNode(label+' price: $'+price);
 node.appendChild(textnode);
 document.getElementById('products 2').appendChild(node);
}
// 为每种咖啡创建函数对象
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());
});

首先需要明确的是这个代码更加模块化了。现在新增一种size或者信新增一个咖啡种类就像下面的代码这样简单:

var peruvian = function(){
 this.name = 'peruvian';
 this.basePrice = 11;
};
var extraLarge = {
 getPrice: function(){return this.basePrice + 10},
 getLabel: function(){return this.name + ' extra large'}
};
coffeeTypes.push(Peruvian);
coffeeSizes.push(extraLarge);

咖啡对象的数组和size对象的数组混合(mix)到了一起,也就是他们的方法和成员变量被组合到了一块儿 ——通过一个叫“plusMinxin”的自定义函数(详见第七章)。这些咖啡类型的类(columbian, frenchRoast, decaf)包含了成员变量, 而这些size对象(small, medium, large)包含了获取名称和计算价格的方法。 ”混合”(minxing)这个动作通过一个map操作来起作用,也就是对数组中的每一个成员执行一个纯函数并返回一个新的函数, 然后这些返回的函数被放到了一个reduce函数中被操作,reduce也是一个高阶函数,和map有些像, 只是reduce把数组里的所有元素处理后组合到了一个东西里面。最终,新的数组包含了所有可能的种类和size的组合, 这个数组通过forEach方法遍历,forEach也是一个高阶函数,它会让数组里面每一个对象作为参数执行一遍回调函数。 在这个例子里,这个回调函数是一个匿名函数,它获取这些对象后,以对象的getPrice()和getLabel() 两个方法的返回值作为参数调用printPrice函数。

实际上,我们可以让这个例子更加函数式:去掉coffees变量,并将函数串到一起链式调用,这也是函数式编程的一个小技巧。

coffeeTypes.reduce(function(previous, current) {
 var newCoffee = coffeeSizes.map(function(mixin) {
  // `plusMixin` function for functional mixins, see Ch.7
  var newCoffeeObj = plusMixin(current, mixin);
  return new newCoffeeObj();
 });
 return previous.concat(newCoffee);
},[]).forEach(function(coffee) {
 printPrice(coffee.getPrice(),coffee.getLabel());
});

这样,控制流没有像命令式代码那样从头到尾的顺序进行。在函数式编程里,map函数和其它高阶函数代替了for和while循环, 只有少量关键的代码是在顺序执行。 这使得新接触的人在阅读这样范式的代码有些困难,但是一旦你能够欣赏它,你就会发现这根本没啥难的, 而且这样写看起来更好。

这个例子仅仅是刚开始展露Javascript中函数式编程能做什么。通过这本书,你将会看到更多函数式实现的强悍的例子。

总结

首先,采用函数式风格的优点已经明确了。 其次,不要害怕函数式编程。的确,它往往被认为是编程语言的纯逻辑形式,但是我们不需要理解lambda演算也能够在日常任务中应用它。 实际上,通过把我们的程序拆分成小的片段,它们变得更容易被理解、维护,也更加可靠。 map和reduce函数是Javascript中不太被知道的内建函数,然而我们将要关注它们。

Javascript是一个脚本语言,可交互,易使用,不需要编译。我们甚至不需要下载任何开发软件, 你最喜欢的浏览器就可以作为开发环境的解释器。

感兴趣吗?好,我们开始!

Javascript 相关文章推荐
js实现鼠标滚轮控制图片缩放效果的方法
Feb 20 Javascript
jQuery移动端日期(datedropper)和时间(timedropper)选择器附源码下载
Apr 19 Javascript
深入浅出ES6新特性之函数默认参数和箭头函数
Aug 01 Javascript
jQuery实现微信长按识别二维码功能
Aug 26 Javascript
Extjs让combobox写起来简洁又漂亮
Jan 05 Javascript
jQuery实现select下拉框获取当前选中文本、值、索引
May 08 jQuery
XMLHttpRequest对象_Ajax异步请求重点(推荐)
Sep 28 Javascript
JS文件中加载jquery.js的实例代码
May 05 jQuery
详解vue2.0监听属性的使用心得及搭配计算属性的使用
Jul 18 Javascript
JS实现指定区域的全屏显示功能示例
Apr 25 Javascript
js+HTML5 canvas 实现简单的加载条(进度条)功能示例
Jul 16 Javascript
vue中axios防止多次触发终止多次请求的示例代码(防抖)
Feb 16 Javascript
jQuery实现仿新浪微博浮动的消息提示框(可智能定位)
Oct 10 #Javascript
JS+DIV+CSS排版布局实现美观的选项卡效果
Oct 10 #Javascript
JS实现漂亮的窗口拖拽效果(可改变大小、最大化、最小化、关闭)
Oct 10 #Javascript
JavaScript实现的浮动层框架用法实例分析
Oct 10 #Javascript
表单验证插件Validation应用的实例讲解
Oct 10 #Javascript
JS实现的车标图片提示效果代码
Oct 10 #Javascript
jqTransform美化表单
Oct 10 #Javascript
You might like
用PHP和ACCESS写聊天室(七)
2006/10/09 PHP
php之对抗Web扫描器的脚本技巧
2008/10/01 PHP
解决Laravel5.x的php artisan migrate数据库迁移创建操作报错SQLSTATE[42000]
2020/04/06 PHP
alert和confirm功能介绍
2014/05/21 Javascript
JS实现闪动的title消息提醒效果
2014/06/20 Javascript
require.js深入了解 require.js特性介绍
2014/09/04 Javascript
js获取滚动距离的方法
2015/05/30 Javascript
JS实现霓虹灯文字效果的方法
2015/08/06 Javascript
原生JS实现美图瀑布流布局赏析
2015/09/07 Javascript
辨析JavaScript中的Undefined类型与null类型
2016/05/26 Javascript
JavaScript中 ES6 generator数据类型详解
2016/08/11 Javascript
js动态生成form 并用ajax方式提交的实现方法
2016/09/09 Javascript
十大 Node.js 的 Web 框架(快速提升工作效率)
2017/06/30 Javascript
javascript ES6 新增了let命令使用介绍
2017/07/07 Javascript
jQuery 操作 HTML 元素和属性的方法
2018/11/12 jQuery
Vue.js 中 axios 跨域访问错误问题及解决方法
2018/11/21 Javascript
Node 代理访问的实现
2019/09/19 Javascript
Vue中keep-alive组件作用详解
2020/02/04 Javascript
[15:58]DOTA2国际邀请赛采访专栏:Tongfu.Sansheng&KingJ,DK.rOtk
2013/08/08 DOTA
Python实现多线程下载文件的代码实例
2014/06/01 Python
scrapy自定义pipeline类实现将采集数据保存到mongodb的方法
2015/04/16 Python
使用Python读写文本文件及编写简单的文本编辑器
2016/03/11 Python
python3使用urllib模块制作网络爬虫
2016/04/08 Python
Python 获得命令行参数的方法(推荐)
2018/01/24 Python
python实现windows下文件备份脚本
2018/05/27 Python
使用python和pygame制作挡板弹球游戏
2019/12/03 Python
python 解决flask uwsgi 获取不到全局变量的问题
2019/12/22 Python
Jupyter notebook如何修改平台字体
2020/05/13 Python
使用Python获取爱奇艺电视剧弹幕数据的示例代码
2021/01/12 Python
亚洲最大旅游体验平台:KKday
2017/10/21 全球购物
全球速卖通西班牙站:AliExpress西班牙
2017/10/30 全球购物
腾讯公司的一个sql题
2013/01/22 面试题
精神文明建设先进工作者事迹材料
2014/05/02 职场文书
2015年话务员工作总结
2015/04/29 职场文书
军训新闻稿范文
2015/07/17 职场文书
Python进程池与进程锁之语法学习
2022/04/11 Python