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


Posted in Javascript onMay 16, 2008

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

函数式编程概念,包括匿名函数、调用函数的不同方法,以及将函数作为参数传递给其他函数的方式。

函数式概念的运用,采用的示例包括:扩展数组排序;动态 HTML 生成的优美代码;系列函数的应用。 
函数式编程概念

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

  
  Digg  

  Slashdot  

 

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

清单 1. 过程风格的阶乘

int factorial (int n)
{
  if (n <= 0)
    return 1;
  else
    return n * factorial (n-1);
}

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

清单 2. 函数式风格的阶乘

factorial n, where n <= 0     := 1
factorial n    := foldr * 1 take n [1..]

第二个语句指明要得到从 1 开始的前 n 个数字的列表(take n [1..]),然后找出它们的乘积,1 为基元。这个定义与前面的示例不同,没有循环或递归。它就像阶乘函数的算术定义。一旦了解了库函数(take 和 foldr)和标记(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<arr.length;i++) {
  var element=arr[i];
  str+=... HTML generation code...
}
document.write(str);

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

清单 14. 生成动态 HTML 的通用方式

Array.prototype.fold=function(templateFn) {
  var len=this.length;
  var str=' ';
  for (var i=0 ; i<len ; i++) 
    str+=templateFn(this[i]);
  return str;
}

function templateInstance(element) {
  return ... HTML generation code ...
}

document.write(arr.fold(templateInstance));

我使用 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 中的函数式编程,以优美的方式完成日常活动。这篇文章中的示例只介绍了几种情况。如果您找到了函数式编程的合适场景,并应用这些概念,那么您就会有更多的理解,并且可以增加您的优美程度。
更多来自

Javascript 相关文章推荐
用javascript自动显示最后更新时间
Mar 15 Javascript
JavaScript 函数惰性载入的实现及其优点介绍
Aug 12 Javascript
兼容最新firefox、chrome和IE的javascript图片预览实现代码
Aug 08 Javascript
JavaScript声明变量名的语法规则
Jul 10 Javascript
使用JS正则表达式 替换括号,尖括号等
Nov 29 Javascript
js中字符型和数值型数字的互相转化方法(必看)
Apr 25 Javascript
jquery+css3实现熊猫tv导航代码分享
Feb 12 jQuery
vue.js-div滚动条隐藏但有滚动效果的实现方法
Mar 03 Javascript
node基于puppeteer模拟登录抓取页面的实现
May 09 Javascript
layui点击数据表格添加或删除一行的例子
Sep 12 Javascript
微信小程序上传图片并等比列压缩到指定大小的实例代码
Oct 24 Javascript
JavaScript实现京东快递单号查询
Nov 30 Javascript
Javascript模块模式分析
May 16 #Javascript
Dom加载让图片加载完再执行的脚本代码
May 15 #Javascript
直接生成打开窗口代码,不必下载
May 14 #Javascript
地震发生中逃生十大法则
May 12 #Javascript
通过修改referer下载文件的方法
May 11 #Javascript
JS启动应用程序的一个简单例子
May 11 #Javascript
一段利用WSH获取登录时间的jscript代码
May 11 #Javascript
You might like
PHP模板引擎Smarty中变量的使用方法示例
2016/04/11 PHP
屏蔽PHP默认设置中的Notice警告的方法
2016/05/20 PHP
php使用FFmpeg接口获取视频的播放时长、码率、缩略图以及创建时间
2016/11/07 PHP
createTextRange()的使用示例含文本框选中部分文字内容
2014/02/24 Javascript
JS使用replace()方法和正则表达式进行字符串的搜索与替换实例
2014/04/10 Javascript
ext前台接收action传过来的json数据示例
2014/06/17 Javascript
document.forms用法示例介绍
2014/06/26 Javascript
JQuery 两种方法解决刚创建的元素遍历不到的问题
2016/04/13 Javascript
Javascript基础知识盲点总结之函数
2016/05/15 Javascript
Bootstrap组件学习之导航、标签、面包屑导航(精品)
2016/05/17 Javascript
微信小程序 Toast自定义实例详解
2017/01/20 Javascript
easyui-datagrid特殊字符不能显示的处理方法
2017/04/12 Javascript
jQuery列表检索功能实现代码
2017/07/17 jQuery
JS获取url参数,JS发送json格式的POST请求方法
2018/03/29 Javascript
详解JavaScript 中 if / if...else...替换方式
2018/07/15 Javascript
Angular2中监听数据更新的方法
2018/08/31 Javascript
uni-app 组件里面获取元素宽高的实现
2019/12/27 Javascript
关于element-ui表单中限制输入纯数字的解决方式
2020/09/08 Javascript
[01:16:50]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第一场 3月7日
2021/03/11 DOTA
python基础教程项目四之新闻聚合
2018/04/02 Python
Python简单实现阿拉伯数字和罗马数字的互相转换功能示例
2018/04/17 Python
如何用Python合并lmdb文件
2018/07/02 Python
Python常见排序操作示例【字典、列表、指定元素等】
2018/08/15 Python
Python多线程编程之多线程加锁操作示例
2018/09/06 Python
python绘图模块matplotlib示例详解
2019/07/26 Python
Python3.7+tkinter实现查询界面功能
2019/12/24 Python
python使用pymongo与MongoDB基本交互操作示例
2020/04/09 Python
css3高级选择器使用方法
2013/12/02 HTML / CSS
小学生环保演讲稿
2014/04/25 职场文书
2014年安全生产工作总结
2014/11/13 职场文书
数学考试作弊检讨书300字
2015/02/16 职场文书
民事起诉状范文
2015/05/19 职场文书
2015年乡镇纪委工作总结
2015/05/26 职场文书
幼儿园大班开学寄语(2015秋季)
2015/05/27 职场文书
java如何实现socket连接方法封装
2021/09/25 Java/Android
JavaScript中的LHS和RHS分析详情
2022/04/06 Javascript