`
kongxiantao
  • 浏览: 108486 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

javascript 函数式编程

阅读更多

本文来自网上的一片文章,在百度文库上找的,介绍了怎么使用JavaScript的函数式特性。以下是原文,语句不通的地方稍作了修改。


要求:你应当已经对JavaScript和DOM有了一个基本的了解。

写这篇指南的目的是因为关于JavaScript编程的资料太多了但是极少的资料提到了JavaScript的函数式特性。在本指南中,我只会讲解这些基本知识而不会深入其它的函数式语言或这是Lambda算子。

你可以点击所有的例子然后你所看到的代码就会被执行,这样就可以令指南变得具有交互性。你也可以使用这个沙箱来尝试。
第一课 —— 匿名函数

我们将首先介绍[i]匿名函数[/i]。一个匿名函数就是一个没有名字的函数。


你可以认为他们是一次性函数。当你只需要用一次某个函数式,他们就特别有用。通过使用匿名函数,没有必要把函数一直放在内存中,所以

使用匿名函数更加有效率。

例Example:

下面两个函数处理同样的事情,而 average在给z赋值结束之后一直保留——但匿名函数则不会。

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

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

这很自然得引出了我们下面的一节课函数作为值。

第二课 - 函数作为值

事实上,我们一般在JavaScript中声明函数的方式可以看作是一个简化了的语法。


例:下面两个表达式其实完全一样。所以左边的表达式仅仅是右边的简写

function average(x,y)
{  return (x+y)/2;}
alert( average(1,3) );

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

从这里可以得出一个结论,函数是一个值就像字符串、数字或数组一样。这还出现几个问题: 我是否可以把函数作为参数传递? 可以,

见下面的例子。 是否可以实时生成函数? 当然了,这是一个高级的主题,它可以通过eval函数来完成。

例:这个例子演示了如何把函数作为参数传递。

var applyFun = function (f,x,y) { return f(x,y); };var add = function(x,y) {  return x+y;};alert( applyFun(add,3,4) ); // 7

第三课 - 两种方式调用函数

在JavaScript中,有两种调用函数的方式。一般的方式是把参数放在括号中,

如alert(42)。另一种方式是同时把函数和参数都放在括号中,如(alert)(42)。
例:

alert(42);

(alert) (42);

(function(x) { alert(x-13); }) (55);

为什么函数两边的括号很重要:

如果你写了括号,那么在括号中的代码就会被先计算。
在计算之后,括号所在的地方就会有一个值。这个值可能是一个字符串、一个数字或一个函数。

第四课- “短路”条件调用

现在我们将学习如何使用“短路”条件调用。使用这个方法可以缩短源代码同时代码也变得更加可读。
例:
这个语法并不是用在左表达式上,而是用在右表达式上。

var f = false; var t = true;
var z;
if(f)
  z = 4;
else if(t)
  z = 2;
alert(z);

var f = false; var t = true;var z = (f && 4) || (t && 2);alert(z);

第五课 - 它好在哪里

OK,现在我们已经学习了一些函数式JavaScript的内容。那么它好在哪里?函数式JavaScript编程之所以很重要有三条主要的理由:

1.    它有助于写出模块化和可服用的代码。
2.    它对事件处理程序非常有效。
3.    它很有趣!


在下面的篇幅中,我会给出更多关于前两条理由的信息

1. 模块化和可复用的代码现在你已经知道如何将函数作为值使用,那么你也应该试试!

一个很好的例子是数组内建的sort方法。
预定义的sort()把所有的对象转换成字符串并把他们按照词语的顺序排序。
但如果我们有用户自定义的对象或者数字那么它就不是很有用了。

于是这个函数可以让你给他一个进行比较的函数作为参数,
如sort(compareFunction)。
这个方法让我们甚至不用接触实际的sort方法。
例:

var myarray = new Array(6,7,9,1,-1);var sortAsc = function(x,y) { return x-y; };

var sortDesc = function(x,y) { return y-x; };myarray.sort(sortDesc);
alert(myarray);myarray.sort(sortAsc);alert(myarray);

2. 事件处理程序对事件处理程序使用函数式编程也许是最直观的函数作为值得应用了。

既然这样我们马上就演示一个例子。
简单的例子:;ie

现在有一个Button类,带一个自定义的onclick行为。

function Button(clickFunction) {    this.button = document.createElement("button");    this.button.appendChild(document.createTextNode("Press me!"));    this.button.onclick = clickFunction;}var bt = new Button(function() { alert("42"); });

练习:为什么我们要把alert包裹在一个匿名函数中?

高级例子:

现在我们想改进我们的Button类。
每一个按钮都被分配了一个值当按钮被点击时显示该值。

首先我们调整我们的类:

function Button(value) {    this.value = value;    this.button = document.createElement("button");    this.button.appendChild(document.createTextNode("test"));}

下面你也许要尝试写下面的代码:

this.button.onclick = function() { alert(this.value); };

如果你执行它你就会发现提示框中间是空的。

为什么会这样呢?其实原因在于JavaScript的可见性规则。
当onclick函数被执行时this指向的是按钮的DOM节点而非自定义的按钮对象。

我们如何解决这个问题?

使用函数式编程:


this.button.onclick = (function(v) { return function() { alert(v); };}) (this.value);

这种情况下执行该匿名函数会将v绑定到this.value上。

函数式编程概念,包括匿名函数、调用函数的不同方法,以及将函数作为参数传递给其他函数的方式。
    函数式概念的运用,采用的示例包括:扩展数组排序;动态 HTML 生成的优美代码;系列函数的应用。
函数式编程概念
在那些通过描述 “如何做” 指定解决问题的方法的语言中,许多开发人员都知道如何进行编码。例如,要编写一个计算阶乘的函数,我可以编写一个循环来描述程序,或者使用递归来查找所有数字的乘积。在这两种情况下,计算的过程都在程序中进行了详细说明。清单 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); //以前见了这种不明白,现在明白了,现在的好多JavaScript框架   都采用这工方式的。
         

可以在括号中编写函数表达式,然后传递给参数,对参数进行运算。虽然在 清单 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 数组中对象的日期对所有对象进行排序。比较函数和它的定义一起被传递给排序函数,以完成排序操作。使用这个函数:
o    每个 JavaScript 对象都有一个 date 属性。
o    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 中的代码扩展。这可能是一个需要您自行完成的一个练习,现在您的兴趣被点燃了吧

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics