用函数式编程技术编写优美的 JavaScript


Posted in Javascript onNovember 25, 2006

级别: 初级

Shantanu Bhattacharya (shantanu@justawordaway.com), 首席顾问, Siemens Information Systems Limited

2006 年 7 月 20 日

函数式或声明性编程是非常强大的编程方法,正逐渐在软件行业流行起来。这篇文章将介绍一些相关的函数式编程概念,并提供有效使用这些概念的示例。作者将解释如何使用 JavaScript(TM)(JavaScript 能导入函数式编程的构造和特性)编写优美的代码。

简介

函数式编程语言在学术领域已经存在相当长一段时间了,但是从历史上看,它们没有丰富的工具和库可供使用。随着 .NET 平台上的 Haskell 的出现,函数式编程变得更加流行。一些传统的编程语言,例如 C++ 和 JavaScript,引入了由函数式编程提供的一些构造和特性。在许多情况下,JavaScript 的重复代码导致了一些拙劣的编码。如果使用函数式编程,就可以避免这些问题。此外,可以利用函数式编程风格编写更加优美的回调。

函数式编程函数式编程只描述在程序输入上执行的操作,不必使用临时变量保存中间结果。重点是捕捉 “是什么以及为什么”,而不是 “如何做”。与将重点放在执行连续命令上的过程性编程相比,函数式编程的重点是函数的定义而不是状态机(state machine)的实现。 大型知识管理系统应用程序从使用函数式编程风格上受益颇多,因为函数式编程简化了开发。

因为函数式编程采用了完全不同的组织程序的方式,所以那些习惯于采用命令式范例的程序员可能会发现函数式编程有点难学。在这篇文章中,您将了解一些关于如何采用函数式风格,用 JavaScript 编写良好的、优美的代码的示例。我将讨论:

  • 函数式编程概念,包括匿名函数、调用函数的不同方法,以及将函数作为参数传递给其他函数的方式。
  • 函数式概念的运用,采用的示例包括:扩展数组排序;动态 HTML 生成的优美代码;系列函数的应用。

函数式编程概念

请告诉每个人。请把这个提交给: Digg Slashdot

在那些通过描述 “如何做” 指定解决问题的方法的语言中,许多开发人员都知道如何进行编码。例如,要编写一个计算阶乘的函数,我可以编写一个循环来描述程序,或者使用递归来查找所有数字的乘积。在这两种情况下,计算的过程都在程序中进行了详细说明。清单 1 显示了一个计算阶乘的可能使用的 C 代码。


清单 1. 过程风格的阶乘
int factorial (int n) { if (n

这类语言也叫做过程性 编程语言,因为它们定义了解决问题的过程。函数式编程与这个原理有显著不同。在函数式编程中,需要描述问题 “是什么”。 函数式编程语言又叫做声明性 语言。同样的计算阶乘的程序可以写成所有到 n 的数字的乘积。计算阶乘的典型函数式程序看起来如 清单 2 中的示例所示。


清单 2. 函数式风格的阶乘
factorial n, where n

第二个语句指明要得到从 1 开始的前 n 个数字的列表(take n [1..]),然后找出它们的乘积,1 为基元。这个定义与前面的示例不同,没有循环或递归。它就像阶乘函数的算术定义。一旦了解了库函数(takefoldr)和标记(list notation [ ])的意义,编写代码就很容易,而且可读性也很好。

只用三行 Miranda 代码就可以编写例程,根据参数,使用广度优先或深度优先遍历处理 n 叉树的每个节点,而且元素可以是任何通用类型。

从历史上看,函数式编程语言不太流行有各种原因。但是最近,有些函数式编程语言正在进入计算机行业。其中一个例子就是 .NET 平台上的 Haskell。其他情况下,现有的一些语言借用了函数式编程语言中的一些概念。一些 C++ 实现中的迭代器和 continuation,以及 JavaScript 中提供的一些函数式构造(functional construct),就是这种借用的示例。但是,通过借用函数式构造,总的语言编程范例并没有发生变化。JavaScript 并没因为函数式构造的添加就变成了函数式编程语言。

我现在要讨论 JavaScript 中的函数式构造的各种美妙之处,以及在日常编码和工作中使用它们的方式。我们将从一些基本功能开始,然后用它们查看一些更有趣的应用。

匿名函数

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

function sum(x,y,z) { return (x+y+z); }

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

function(x,y,z) { return (x+y+z); }

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


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

使用函数作为值

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

var sum = function(x,y,z) { return (x+y+z); } alert(sum(1,2,3));

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

调用函数的不同方法

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


清单 7. 典型的函数应用
alert (“Hello, World!");


清单 8. 用函数作为表达式
(alert) (“Hello, World!");

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


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

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

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

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


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

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

使用函数式概念

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

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

这一节将介绍一些有效使用这些概念编写优美的 JavaScript 代码的示例。(使用 JavaScript 函数式风格,可以做许多超出这个讨论范围的事。)

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

清单 11. 比较函数

function (x,y) { return x.date ? y.date; }

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


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

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

  • 每个 JavaScript 对象都有一个 date 属性。
  • JavaScript 的数组类型的排序函数接受可选参数,可选参数是用来排序的比较函数。这与 C 库中的 qsort 函数类似。
动态生成 HTML 的优美代码
在这个示例中,将看到如何编写优美的代码,从数组动态地生成 HTML。可以根据从数据中得到的值生成表格。或者,也可以用数组的内容生成排序和未排序的列表。也可以生成垂直或水平的菜单项目。

清单 13 中的代码风格通常被用来从数组生成动态 HTML。


清单 13. 生成动态 HTML 的普通代码
var str=' '; for (var i=0;i

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


清单 14. 生成动态 HTML 的通用方式
Array.prototype.fold=function(templateFn) { var len=this.length; var str=' '; for (var i=0 ; i

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

系列函数的应用
考虑以下这种情况:想用一组函数作为回调函数。为实现这一目的,将使用 window.setTimeout 函数,该函数有两个参数。第一个参数是在第二个参数表示的毫秒数之后被调用的函数。清单 15 显示了完成此操作的一种方法。
清单 15. 在回调中调用一组函数
window.setTimeout(function(){alert(‘First!');alert(‘Second!');}, 5000);

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


清单 16. 调用系列函数的更好的方式
Function.prototype.sequence=function(g) { var f=this; return function() { f();g(); } }; function alertFrst() { alert(‘First!'); } function alertSec() { alert(‘Second!'); } setTimeout( alertFrst.sequence(alertSec), 5000);

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

Javascript 相关文章推荐
在iframe里的页面编写js,实现在父窗口上创建动画效果展开和收缩的div(不变动iframe父窗口代码)
Dec 20 Javascript
浅析Node.js查找字符串功能
Sep 03 Javascript
javascript实现简单的省市区三级联动
May 14 Javascript
酷! 不同风格页面布局幻灯片特效js实现
Feb 19 Javascript
浅谈JS继承_借用构造函数 & 组合式继承
Aug 16 Javascript
vue如何通过id从列表页跳转到对应的详情页
May 01 Javascript
vue在手机中通过本机IP地址访问webApp的方法
Aug 15 Javascript
vue+axios 前端实现登录拦截的两种方式(路由拦截、http拦截)
Oct 24 Javascript
JavaScript之数组扁平化详解
Jun 03 Javascript
详解使用JWT实现单点登录(完全跨域方案)
Aug 02 Javascript
微信小程序停止其他视频播放当前视频的实例代码
Dec 25 Javascript
使用javascript解析二维码的三种方式
Nov 11 Javascript
通过JAVASCRIPT读取ASP设定的COOKIE
Nov 24 #Javascript
键盘控制事件应用教程大全
Nov 24 #Javascript
Javascript操纵Cookie实现购物车程序
Nov 23 #Javascript
删除重复数据的算法
Nov 23 #Javascript
在JavaScript中实现命名空间
Nov 23 #Javascript
js变量作用域及可访问性的探讨
Nov 23 #Javascript
bcastr2.0 通用的图片浏览器
Nov 22 #Javascript
You might like
PHP删除目录及目录下所有文件的方法详解
2013/06/06 PHP
PHP实现简单爬虫的方法
2015/07/29 PHP
YII Framework框架教程之安全方案详解
2016/03/14 PHP
利用PHP访问带有密码的Redis方法示例
2017/02/09 PHP
PHP实现电商订单自动确认收货redis队列
2017/05/17 PHP
JQuery 学习笔记 element属性控制
2009/07/23 Javascript
c#和Javascript操作同一json对象的实现代码
2012/01/17 Javascript
javascript与cookie 的问题详解
2013/11/11 Javascript
javascript的渐进增强与平稳退化浅谈
2013/11/12 Javascript
使用firebug进行调试javascript的示例
2013/12/16 Javascript
在JavaScript应用中实现延迟加载的方法
2015/06/25 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之主动触发事件和模拟冒泡处理
2015/11/24 Javascript
深究AngularJS中$sce的使用
2017/06/12 Javascript
JavaScript队列函数和异步执行详解
2017/06/19 Javascript
10 种最常见的 Javascript 错误(频率最高)
2018/02/08 Javascript
基于React+Redux的SSR实现方法
2018/07/03 Javascript
vue src动态加载请求获取图片的方法
2018/10/17 Javascript
vuejs2.0运用原生js实现简单拖拽元素功能
2020/08/21 Javascript
Javascript数组方法reduce的妙用之处分享
2019/06/10 Javascript
vuex(vue状态管理)的特殊应用案例分享
2020/03/03 Javascript
extjs图形绘制之饼图实现方法分析
2020/03/06 Javascript
利用python批量检查网站的可用性
2016/09/09 Python
tensorflow 获取变量&打印权值的实例讲解
2018/06/14 Python
Python3进制之间的转换代码实例
2019/08/24 Python
Python中的相关分析correlation analysis的实现
2019/08/29 Python
python图片指定区域替换img.paste函数的使用
2020/04/09 Python
基于HTML5+CSS3实现简单的时钟效果
2017/09/11 HTML / CSS
给排水工程师岗位职责
2013/11/21 职场文书
初中班级口号
2014/06/09 职场文书
十佳标兵事迹材料
2014/08/18 职场文书
美术教师个人总结
2015/02/06 职场文书
2015年世界无烟日活动方案
2015/05/04 职场文书
付款证明格式范文
2015/06/19 职场文书
2019年最新借条范本!
2019/07/08 职场文书
关于Vue Router的10条高级技巧总结
2021/05/06 Vue.js
win10电脑双屏显示一个黑屏怎么办?win10电脑双屏显示一个黑屏解决方法
2022/07/15 数码科技