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

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

因为函数式编程采用了完全不同的组织程序的方式,所以那些习惯于采用命令式范例的程序员可能会发现函数式编程有点难学。在这篇文章中,您将了解一些关于如何采用函数式风格,用 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 中的函数式编程,以优美的方式完成日常活动。这篇文章中的示例只介绍了几种情况。如果您找到了函数式编程的合适场景,并应用这些概念,那么您就会有更多的理解,并且可以增加您的优美程度。

时间: 2024-10-04 11:31:05

用函数式编程技术编写优美的 JavaScript的相关文章

翻译连载 | 附录 C:函数式编程函数库-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTML 最坚实的梁柱:分享,是 CSS 里最闪耀的一瞥:总结,是 JavaScript 中最严谨的逻辑.经过捶打磨练,成就了本书的中文版.本书包含了函数式编程之精髓,希望可以帮助大家在学习函数式编程的道路上走的更顺畅.比心. 译者团队(排名不分先后):阿希.blueken.brucecham.cfanlife.d

在JavaScript函数式编程里使用Map和Reduce方法

所有人都谈论道workflows支持ECMAScript6里出现的令人吃惊的新特性,因此我们很容易忘掉ECMAScript5带给我们一些很棒的工具方法来支持在JavaScript里进行函数编程,这些工具方法我们现在可以使用了.在这些函数方法里主要的是基于JavaScript 数组对象的map()方法和reduce()方法. 如果你如今还没有使用map()和reduce()方法,那么现在是时候开始使用了.如今绝大部分的JavaScript开发平台都与生俱来的支持ECMAScript5.使用Map方

JS函数式编程 3.1 Javascript的函数式库

?? Functional Programming in Javascript 主目录第三章 建立函数式编程环境 Javascript的函数式库 据说所有的函数式程序员都会写自己的函数库,函数式Javascript程序员也不例外. 随着如今开源代码分享平台如GitHab.Bower和NPM的涌现,对这些函数库进行分享.变得及补充变得越来越容易. 现在已经有很多Javascript的函数式变成苦,从小巧的工具集到庞大的模块库都有. 每一个库都宣扬着自己的函数式编程风格.从一本正经的数学风格到灵活松

如何编写高质量的 JS 函数(3) --函数式编程[理论篇]

本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/EWSqZuujHIRyx8Eb2SSidQ作者:杨昆 [编写高质量函数系列]中, <如何编写高质量的 JS 函数(1) -- 敲山震虎篇>介绍了函数的执行机制,此篇将会从函数的命名.注释和鲁棒性方面,阐述如何通过 JavaScript 编写高质量的函数. <如何编写高质量的 JS 函数(2)-- 命名/注释/鲁棒篇>从函数的命名.注释和鲁棒性方面,阐述如何通过 JavaScri

如何编写高质量的 JS 函数(4) --函数式编程[实战篇]

本文首发于 vivo互联网技术 微信公众号? 链接:https://mp.weixin.qq.com/s/ZoXYbjuezOWgNyJKmSQmTw 作者:杨昆 ?[编写高质量函数系列],往期精彩内容: <如何编写高质量的 JS 函数(1) -- 敲山震虎篇>介绍了函数的执行机制,此篇将会从函数的命名.注释和鲁棒性方面,阐述如何通过 JavaScript 编写高质量的函数. ?<如何编写高质量的 JS 函数(2)-- 命名/注释/鲁棒篇>从函数的命名.注释和鲁棒性方面,阐述如何通

javascript 函数式编程

编程范式 编程范式是一个由思考问题以及实现问题愿景的工具组成的框架.很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等. 函数式编程范式 函数式编程就像一辆氢燃料驱动的汽车——先进的未来派,但是还没有被广泛推广.与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态.函数式编程将计算转化作表达式求值.这些表达式全由纯数学函数组成,这些数学函数都是一流的(可以被当做一般值来运用和处理),并且没有副作用. 函数式编程

JavaScript:了解一下函数式编程

一.简介 在JavaScript中,函数就是第一类公民,它可以像字符串.数字等变量一样,使用var修饰并作为数据使用.它可以作为数值.可以作为参数.还可以作为返回结果.可以说JavaScript就是函数式编程.ES6新语言特性中,箭头函数.扩展运算符会极大地帮助开发者利用函数式编程技术进行开发. //定义一个箭头函数 var print = (message) => console.log(message) 1.函数作为数值使用 //将函数作为数值使用 const array = ["XY

JavaScript函数式编程(0):函数基础 arguments、this、apply()、call()

1 函数参数 函数的实参和形参个数可以不等,之所以会这样,原因是 ECMAScript 中的参数在内部是用一个数组来表示的.函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话).如果实参个数大于形参个数,多余的实参不传递值,但是在arguments中可以访问:如果形参个数大于实参,没有传递值的实参将自动被赋予 undefined 值. 2 arguments和this 所有的函数调用都会传递两个隐式参数:arguments和this.实际上,在函数体内可以通过 argume

javaScript函数式编程

第1章 JavaScript函数式编程简介 11.1 JavaScript案例 11.2 开始函数式编程 41.2.1 为什么函数式编程很重要 41.2.2 以函数为抽象单元 71.2.3 封装和隐藏 91.2.4 以函数为行为单位 101.2.5 数据抽象 141.2.6 函数式JavaScript初试 171.2.7 加速 191.3 Underscore示例 221.4 总结 23第2章 一等函数与Applicative编程 242.1 函数是一等公民 242.2 Applicative编