我在聊天应用程序上使用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类概念一起工作感兴趣(或者如果出现这种情况的话,可以将其用于不同的东西)。
答案 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);
})();
请注意,我们无法对范围之外的external
或foo
执行任何操作。他们完全被自己的封闭所包裹,不可触碰。
现在,摆脱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更面向对象而不是功能。这意味着如果你想以纯粹的功能性风格进行编程,你必须放弃大部分语言。特别是所有面向对象的部分。
如果你愿意更加务实,那么你可以使用纯粹功能性世界的灵感。
我尝试遵守以下规则:
执行计算的函数不应改变状态。并且改变状态的函数不应该执行计算。此外,改变状态的函数应该尽可能少地改变状态。目标是拥有许多只做一件事的小功能。然后,如果你需要做大事,你可以编写一些小函数来完成你需要的工作。
遵循这些规则可以获得许多好处:
易于重复使用。函数越长越复杂,它也越专业化,因此它可以重用的可能性越小。反过来的含义是,较短的函数往往更通用,因此更容易重用。
代码的可靠性。如果代码不太复杂,则更容易推断代码的正确性。
当他们只做一件事时,更容易测试功能。这样就可以减少要测试的特殊情况。
更新
来自评论的建议。
更新2:
添加了一些有用的链接。
答案 2 :(得分:2)
我认为,http://documentcloud.github.com/underscore/应该非常适合您的需要 - 它为函数式编程提供了最重要的高阶函数,并且没有您不需要的DOM操作的客户端函数服务器端。虽然我没有经验。
作为旁注:IMHO功能编程的主要特征是函数的Referential transparency - 函数结果仅取决于其参数 - 函数不依赖于其他对象的更改,并且除了其结果之外不会引入任何更改值。它可以很容易地推断出程序的正确性,并且对于实现可预测的多线程(如果相关)非常有价值。虽然JavaScript不是FP的赌注语言 - 但我希望不可变数据结构的性能非常昂贵。
答案 3 :(得分:0)
有两点要指出,
在你的第一个例子中,你的变量不会泄漏到全局区域,并且是它应该完成的方式,尝试永远不要使用变量而不声明它们,即test ='data'会导致数据泄漏到全球区域。
你的第二个例子也是正确的,bar1和bar2只会在Foo对象上声明。
要记住的事情尽量不要过度使用原型,因为它适用于您创建的每个对象,这可能会占用大量内存,具体取决于对象的复杂程度。
如果您正在寻找应用开发框架,请查看ExtJs。就我个人而言,我认为它非常适合您正在尝试开发的模型。在投入大量资金之前,请记住他们的许可模式是如何运作的。