如何构建基于函数式编程的JavaScript应用程序?

时间:2010-04-20 18:09:27

标签: javascript functional-programming node.js serverside-javascript underscore.js

我在聊天应用程序上使用node.js一段时间(我知道,非常原创,但我认为这是一个很好的学习项目)。 Underscore.js提供了许多看起来很有趣的函数式编程概念,因此我想了解如何设置JavaScript中的函数式程序。

从我对函数式编程的理解(可能是错误的),整个想法是避免副作用,这些副作用基本上都有一个函数来更新函数之外的另一个变量,所以像

var external;
function foo() {
   external = 'bar';
}
foo();

会产生副作用,对吗?因此,作为一般规则,您希望避免在全局范围内干扰变量。

好的,那么当你处理对象时它是如何工作的呢?例如,很多时候,我将有一个构造函数和初始化对象的init方法,如下所示:

var Foo = function(initVars) {
   this.init(initVars);
}

Foo.prototype.init = function(initVars) {
   this.bar1 = initVars['bar1'];
   this.bar2 = initVars['bar2'];
   //....
}

var myFoo = new Foo({'bar1': '1', 'bar2': '2'});

所以我的init方法故意造成副作用,但是处理相同情况的功能方法是什么?

此外,如果有人可以指向我尝试尽可能功能的程序的Python或JavaScript源代码,那么也将非常感激。我觉得我接近“得到它”,但我只是不在那里。主要是我对函数式编程如何与传统的OOP类概念一起工作感兴趣(或者如果出现这种情况的话,可以将其用于不同的东西)。

4 个答案:

答案 0 :(得分:29)

你应该读这个问题:

Javascript as a functional language

有很多有用的链接,包括:

现在,我认为。很多人misunderstand JavaScript,可能是因为它的语法看起来像大多数其他编程语言(其中Lisp / Haskell / OCaml看起来完全不同)。 JavaScript 面向对象,它实际上是prototype-based language。它没有类或经典继承,因此不应该真正与Java或C ++进行比较。

与Lisp相比,JavaScript可以更好;它有闭包和一流的功能。使用它们,您可以创建其他函数式编程技术,例如partial application(currying)。

我们举一个例子(使用node.js中的sys.puts):

var external;
function foo() {
    external = Math.random() * 1000;
}
foo();

sys.puts(external);

为了消除全局副作用,我们可以将它包装在一个闭包中:

(function() {
    var external;
    function foo() {
        external = Math.random() * 1000;
    }
    foo();

    sys.puts(external);
})();

请注意,我们无法对范围之外的externalfoo执行任何操作。他们完全被自己的封闭所包裹,不可触碰。

现在,摆脱external副作用:

(function() {
    function foo() {
        return Math.random() * 1000;
    }

    sys.puts(foo());
})();

最后,该示例并非纯函数,因为它不能。使用随机数从全局状态读取(获取种子)并打印到控制台是一个副作用。

我还想指出,将函数式编程与对象混合完全没问题。以此为例:

var Square = function(x, y, w, h) {
   this.x = x;
   this.y = y;
   this.w = w;
   this.h = h;
};

function getArea(square) {
    return square.w * square.h;
}

function sum(values) {
    var total = 0;

    values.forEach(function(value) {
        total += value;
    });

    return total;
}

sys.puts(sum([new Square(0, 0, 10, 10), new Square(5, 2, 30, 50), new Square(100, 40, 20, 19)].map(function(square) {
    return getArea(square);
})));

如您所见,使用函数式语言中的对象可以很好。有些Lisps甚至还有一些称为属性列表的东西,它们可以被认为是对象。

在功能样式中使用对象的真正诀窍是确保您不依赖于它们的副作用,而是将它们视为不可变的。一种简单的方法是,只要您想要更改属性,只需使用新的详细信息创建一个 new 对象并将其传递给它(这是Clojure和Haskell中经常使用的方法)。

我坚信功能方面在JavaScript中非常有用,但最终,你应该使用任何使代码更具可读性的东西,并且对你有用。

答案 1 :(得分:5)

你必须明白,函数式编程和面向对象编程在某种程度上是相互对立的。 <{3}}和纯粹面向对象都不可能。

purely functional就是无状态计算。 Functional programming完全是关于状态转换。 (Paraphasing Object oriented programming。希望不是太糟糕)

JavaScript更面向对象而不是功能。这意味着如果你想以纯粹的功能性风格进行编程,你必须放弃大部分语言。特别是所有面向对象的部分。

如果你愿意更加务实,那么你可以使用纯粹功能性世界的灵感。

我尝试遵守以下规则:

执行计算的函数不应改变状态。并且改变状态的函数不应该执行计算。此外,改变状态的函数应该尽可能少地改变状态。目标是拥有许多只做一件事的小功能。然后,如果你需要做大事,你可以编写一些小函数来完成你需要的工作。

遵循这些规则可以获得许多好处:

  1. 易于重复使用。函数越长越复杂,它也越专业化,因此它可以重用的可能性越小。反过来的含义是,较短的函数往往更通用,因此更容易重用。

  2. 代码的可靠性。如果代码不太复杂,则更容易推断代码的正确性。

  3. 当他们只做一件事时,更容易测试功能。这样就可以减少要测试的特殊情况。

  4. 更新

    来自评论的建议。

    更新2:

    添加了一些有用的链接。

答案 2 :(得分:2)

我认为,http://documentcloud.github.com/underscore/应该非常适合您的需要 - 它为函数式编程提供了最重要的高阶函数,并且没有您不需要的DOM操作的客户端函数服务器端。虽然我没有经验。

作为旁注:IMHO功能编程的主要特征是函数的Referential transparency - 函数结果仅取决于其参数 - 函数不依赖于其他对象的更改,并且除了其结果之外不会引入任何更改值。它可以很容易地推断出程序的正确性,并且对于实现可预测的多线程(如果相关)非常有价值。虽然JavaScript不是FP的赌注语言 - 但我希望不可变数据结构的性能非常昂贵。

答案 3 :(得分:0)

有两点要指出,

  1. 在你的第一个例子中,你的变量不会泄漏到全局区域,并且是它应该完成的方式,尝试永远不要使用变量而不声明它们,即test ='data'会导致数据泄漏到全球区域。

  2. 你的第二个例子也是正确的,bar1和bar2只会在Foo对象上声明。

  3. 要记住的事情尽量不要过度使用原型,因为它适用于您创建的每个对象,这可能会占用大量内存,具体取决于对象的复杂程度。

    如果您正在寻找应用开发框架,请查看ExtJs。就我个人而言,我认为它非常适合您正在尝试开发的模型。在投入大量资金之前,请记住他们的许可模式是如何运作的。