JavaScript中的函数式编程实践

在许多情况下,JavaScript的重复代码导致了一些拙劣的编码。如果使用函数式编程,就可以避免这些问题。此外,可以利用函数式编程风格编写更加优美的回调。因为函数式编程采用了完全不同的组织程序的方式,所以那些习惯于采用命令式范例的程序员可能会发现函数式编程有点难学。

匿名函数

在JavaScript中,可以编写匿名函数或没有名称的函数。为什么需要这样的函数?请继续往下读,但首先我们将学习如何编写这样一个函数。如果拥有以下JavaScript 函数:

 
 
  1. 清单 3. 典型的函数  
  2. function sum(x,y,z) {  
  3.   return (x+y+z);  

然后对应的匿名函数看起来应当如下所示:

 
 
  1. 清单 4. 匿名函数  
  2. function(x,y,z) {  
  3.   return (x+y+z);  

要使用它,则需要编写以下代码:

 
 
  1. 清单 5. 应用匿名函数  
  2. var sum = function(x,y,z) {  
  3.   return (x+y+z);  
  4. }(1,2,3);  
  5. alert(sum); 

使用函数作为值

也可以将函数作为值使用。还可以拥有一些所赋值是函数的变量。在最后一个示例中,还可以执行以下操作:

 
 
  1. 清单 6. 使用函数赋值  
  2. var sum = function(x,y,z) {  
  3.   return (x+y+z);  
  4. }  
  5. alert(sum(1,2,3)); 

在上面清单6的示例中,为变量sum赋的值是函数定义本身。这样,sum就成了一个函数,可以在任何地方调用。

调用函数的不同方法

JavaScript允许用两种方式调用函数,如清单7和8所示。

 
 
  1. 清单 7. 典型的函数应用  
  2. alert (“Hello, World!");  
  3. 或  
  4. 清单 8. 用函数作为表达式  
  5. (alert) (“Hello, World!"); 

所以也可以编写以下代码:

 
 
  1. 清单 9. 定义函数之后就可以立即使用它  
  2. ( function(x,y,z) { return (x+y+z) } ) (1, 2, 3); 

可以在括号中编写函数表达式,然后传递给参数,对参数进行运算。虽然在清单8的示例中,有直接包含在括号中的函数名称,但是按清单9中所示方式使用它时,就不是这样了。

将函数作为参数传递给其他函数

也可以将函数作为参数传递给其他函数。虽然这不是什么新概念,但是在后续的示例中大量的使用了这个概念。可以传递函数参数,如清单10所示。

 
 
  1. 清单 10. 将函数作为参数传递,并应用该函数  
  2. var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };  
  3. var sum = function(x,y,z) {  
  4.   return x+y+z;  
  5. };  
  6. alert( passFunAndApply(sum,3,4,5) ); // 12 

执行最后一个alert语句输出了一个大小为12的值。

使用函数式概念

前一节介绍了一些使用函数式风格的编程概念。所给的示例并没有包含所有的概念,它们在重要性方面也没有先后顺序,只是一些与这个讨论有关的概念而已。下面对JavaScript中的函数式风格作一快速总结:

◆函数并不总是需要名称;
    ◆函数可以像其他值一样分配给变量;
    ◆函数表达式可以编写并放在括号中,留待以后应用;
    ◆函数可以作为参数传递给其他函数。

这一节将介绍一些有效使用这些概念编写优美的JavaScript代码的示例。

扩展数组排序

先来编写一个排序方法,可以根据数组元素的日期对数据进行排序。用JavaScript编写这个方法非常简单。数据对象的排序方法接受一个可选参数,这个可选参数就是比较函数。在这里,需要使用清单11中的比较函数。

 
 
  1. 清单 11. 比较函数  
  2. function (x,y) {  
  3.  return x.date – y.date;  

要得到需要的函数,请使用清单12的示例。

 
 
  1. 清单 12. 排序函数的扩展  
  2. arr.sort( function (x,y) { return x.date – y.date; } ); 

其中 arr 是类型数组对象。排序函数会根据arr数组中对象的日期对所有对象进行排序。比较函数和它的定义一起被传递给排序函数,以完成排序操作。使用这个函数:

◆每个JavaScript对象都有一个date属性。

◆JavaScript的数组类型的排序函数接受可选参数,可选参数是用来排序的比较函数。这与C库中的qsort函数类似。

动态生成HTML的优美代码

在这个示例中,将看到如何编写优美的代码,从数组动态地生成HTML。可以根据从数据中得到的值生成表格。或者,也可以用数组的内容生成排序和未排序的列表。也可以生成垂直或水平的菜单项目。清单13中的代码风格通常被用来从数组生成动态HTML。

 
 
  1. 清单 13. 生成动态 HTML 的普通代码  
  2. var str=' ';  
  3. for (var i=0;i<arr.length;i++) {  
  4.   var element=arr[i];  
  5.   str+=... HTML generation code...  
  6. }  
  7. document.write(str); 

可以用清单14的代码替换这个代码。

 
 
  1. 清单 14. 生成动态 HTML 的通用方式  
  2. Array.prototype.fold=function(templateFn) {  
  3.   var len=this.length;  
  4.   var str=' ';  
  5.   for (var i=0 ; i<len ; i++)   
  6.  str+=templateFn(this[i]);  
  7.   return str;  
  8. }  
  9.  
  10. function templateInstance(element) {  
  11.   return ... HTML generation code ...  
  12. }  
  13.  
  14. document.write(arr.fold(templateInstance)); 

这里使用的是Array类型的prototype属性定义新函数fold。现在可以在后面定义的任何数组中使用该函数。

系列函数的应用

考虑以下这种情况:想用一组函数作为回调函数。为实现这一目的,将使用window.setTimeout函数,该函数有两个参数。第一个参数是在第二个参数表示的毫秒数之后被调用的函数。清单15显示了完成此操作的一种方法。

 
 
  1. 清单 15. 在回调中调用一组函数  
  2. window.setTimeout(function(){alert(‘First!’);alert(‘Second!’);}, 5000); 

清单16显示了完成此操作的更好的方式。

 
 
  1. 清单 16. 调用系列函数的更好的方式  
  2. Function.prototype.sequence=function(g) {  
  3.   var f=this;  
  4.   return function() {  
  5.     f();g();  
  6.   }  
  7. };  
  8. function alertFrst() { alert(‘First!’); }  
  9. function alertSec() { alert(‘Second!’); }  
  10. setTimeout( alertFrst.sequence(alertSec), 5000); 

在处理事件时,如果想在调用完一个回调之后再调用一个回调,也可以使用清单16中的代码扩展。这可能是一个需要您自行完成的一个练习,现在您的兴趣被点燃了吧。

结束语

在许多领域中都可以应用JavaScript中的函数式编程,以优美的方式完成日常活动。这篇文章中的示例只介绍了几种情况。如果您找到了函数式编程的合适场景,并应用这些概念,那么您就会有更多的理解,并且可以增加您的优美程度。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介 · · · · · · JavaScript 是近年来非常受瞩目的一门编程语言,它既支持面向对象编程,也支持函数式编程。本书专门介绍JavaScript函数式编程的特性。 全书共9章,分别介绍了JavaScript函数式编程、一等函数与Applicative编程、变量的作用域和闭包、高阶函数、由函数构建函数、递归、纯度和不变性以及更改政策、基于流的编程、类编程。除此之外,附录还介绍了更多函数式JavaScript。 本书内容全面,示例丰富,适合想要了解函数式编程JavaScript程序员和学习JavaScript的函数式程序员阅读。 作者简介 · · · · · · Michael Fogus是Dynamic Animation Systems的软件架构师,在分布式仿真、机器视觉和专家系统建设方面经验丰富。他是Clojure、ClojureScript以及Underscore-contrib的贡献者,还是《Clojure编程乐趣》的作者。 目录 · · · · · · 第1章 JavaScript函数式编程简介1 1.1 JavaScript案例1 1.2 开始函数式编程4 1.2.1 为什么函数式编程很重要4 1.2.2 以函数为抽象单元7 1.2.3 封装和隐藏9 1.2.4 以函数为行为单位10 1.2.5 数据抽象14 1.2.6 函数式JavaScript初试17 1.2.7 加速19 1.3 Underscore示例22 1.4 总结23 第2章 一等函数与Applicative编程24 2.1 函数是一等公民24 2.2 Applicative编程30 2.2.1 集合心编程31 2.2.2 Applicative编程的其他实例32 2.2.3 定义几个Applicative函数35 2.3 数据思考36 2.4 总结43 第3章 变量的作用域和闭包44 3.1 全局作用域44 3.2 词法作用域46 3.3 动态作用域47 3.4 函数作用域51 3.5 闭包52 3.5.1 模拟闭包53 3.5.2 使用闭包57 3.5.3 闭包的抽象59 3.6 总结60 第4章 高阶函数62 4.1 以其他函数为参数的函数62 4.1.1 关于传递函数的思考:max、finder和best63 4.1.2 关于传递函数的更多思考:重复、反复和条件迭代 (iterateUntil)65 4.2 返回其他函数的函数67 4.2.1 高阶函数捕获参数69 4.2.2 捕获变量的好处69 4.2.3 防止不存在的函数:fnull72 4.3 整合:对象校验器74 4.4 总结77 第5章 由函数构建函数78 5.1 函数式组合的精华78 5.2 柯里化(Currying)83 5.2.1 向右柯里化,还是向左84 5.2.2 自动柯里化参数85 5.2.3 柯里化流利的API88 5.2.4 JavaScript柯里化的缺点89 5.3 部分应用89 5.3.1 部分应用一个和两个已知的参数91 5.3.2 部分应用任意数量的参数92 5.3.3 局部应用实战:前置条件93 5.4 通过组合端至端的拼接函数96 5.5 总结98 第6章 递归100 6.1 自吸收(self—absorbed)函数(调用自己的函数)100 6.1.1 用递归遍历图105 6.1.2 深度优先自递归搜索106 6.1.3 递归和组合函数:Conjoin和Disjoin108 6.2 相互关联函数(函数调用其他会再调用回它的函数)110 6.2.1 使用递归深克隆111 6.2.2 遍历嵌套数组112 6.3 太多递归了114 6.3.1 生成器117 6.3.2 蹦床原理以及回调120 6.4 递归是一个底层操作121 6.5 总结122 第7章 纯度、不变性和更改政策123 7.1 纯度123 7.1.1 纯度和测试之间的关系124 7.1.2 提取纯函数125 7.1.3 测试不纯函数的属性126 7.1.4 纯度与引用透明度的关系127 7.1.5 纯度和幂等性129 7.2 不变性130 7.2.1 如果一棵树倒在树林里,有没有声音?132 7.2.2 不变性与递归133 7.2.3 冻结和克隆134 7.2.4 在函数级别上观察不变性136 7.2.5 观察对象的不变性137 7.2.6 对象往往是一个低级别的操作140 7.3 控制变化的政策141 7.4 总结144 第8章 基于流的编程145 8.1 链接145 8.1.1 惰性链148 8.1.2 Promises152 8.2 管道154 8.3 数据流与控制流158 8.3.1 找个一般的形状161 8.3.2 函数可以简化创建action164 8.4 总结166 第9章 无类编程167 9.1 数据导向167 9.2 Mixins173 9.2.1 修改核心原型175 9.2.2 类层次结构176 9.2.3 改变层级结构179 9.2.4 用Mixin扁平化层级结构180 9.2.5 通过Mixin扩展新的语义185 9.2.6 通过Mixin混合出新的类型187 9.2.7 方法是低级别操作188 9.3.call(“Finis”);190 附录A 更多函数式JavaScript191 A.1 JavaScript的函数式库191 A.1.1 函数式JavaScript191 A.1.2 Underscore—contrib192 A.1.3 RxJS192 A.1.4 Bilby194 A.1.5 allong.es195 A.1.6 其他函数式库196 A.2 能编译成JavaScript的函数式语言196 A.2.1 ClojureScript196 A.2.2 CoffeeScript197 A.2.3 Roy198 A.2.4 Elm198 附录B推荐书目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值