你的位置:首页 > Java教程

[Java教程][原创译书] JS函数编程 3.2 开发和生产环境


🏠 Functional Programming in Javascript 主目录第三章 建立函数式编程环境

开发和生产环境

环境

编程风格与应用所部署或者将要部署的环境没啥关系。但是库就有关系了。

浏览器

主要的Javascript应用还是跑在客户端的,也就是浏览器。基于浏览器的环境对于开发来说非常好, 因为浏览器无处不在,你可以在本地机器上写代码,解释器是浏览器的Javascript引擎, 所有的浏览器都有开发者终端。火狐的FireBug提供了非常有用的错误信息,并支持断点等等, 不过同样的代码运行在Chrome和Safari上的交叉引用的错误输出会很有用。甚至连IE都包含开发者工具。

浏览器的问题是他们对Javascript的解析不尽相同!尽管不是普遍现象,但是可能有些代码在不同的浏览器上会得到非常不同的结果。 不过主要的差别在于它们如何处理DOM,而不是在原型和函数如何工作上。举个明显的例子,Math.sqrt(4) 在任何浏览器和shell上都会返回2。但是scrollLeft方法依赖于浏览器的布局策略。

编写针对浏览器的特定代码是在浪费时间,这也是为何要使用库的另外一个原因。

服务器端Javascript

Node.js已经成为创建服务器端和基于网络应用的标准平台。函数式编程可以用于服务器端应用的编程吗? 可以!OK,不过现在的这些函数式库是为这种注重性能的环境设计的吗?答案仍然是肯定的。

这章提到的所有的函数式库都可以工作在Node.js上,有些需要依赖browserify.js模块来处理浏览器元素。

服务器端环境的一个函数式用例

在我们的网络系统这个无畏的新世界里,服务器端应用开发人员总在担心并发问题,这是应该的。 经典的例子就是一个应用允许多个用户修改同一个文件,如果他们打算同时修改这个文件,你将会陷入令人作呕的混乱。 这就是困扰了程序员几十年的状态维持问题。

假设下面这个情景:

  1. 一天早晨,亚当打开了一份报告开始编辑,但是出去吃午饭的时候没有保存。
  2. 比利打开了同一份报告,添加了内容,并且保存了报告。
  3. 亚当吃完午饭回来,又往这份报告里添加了内容,并且保存,不知情地覆盖了比利的内容。
  4. 第二天,比利发现他的内容消失了。他的老板冲他咆哮,所有的人都一起冲着开发人员发飙,结果开发人员丢了工作。

长期以来,解决这个问题的办法就是给文件建立状态。当有人编辑这个文件的时候就把状态切换为加锁, 这就防止其他人编辑这个文件,并在保存这个文件后把状态切换为解锁。在我们的情景里,比利应该无法修改报告, 直到亚当吃完饭回来。并且只要文件没被保存就没有其他人可以编辑它。

这正是函数式编程关于不可变数据和状态的思想具有实际意义之处。函数式的实现并不是直接修改文件, 而是修改文件的一个拷贝,也就是一个新的版本。如果要保存这个版本而此时一个新的版本已经存在, 我们就知道已经有别人修改了原来的文件。危险规避了。

现在这个场景可以这样展开了:

  1. 一天早晨,亚当打开了一份报告开始编辑,但是出去吃午饭的时候没有保存。
  2. 比利打开了同一份报告,添加了内容,保存为了一个新的版本。
  3. 亚当吃完饭回来继续添加内容,当他要保存时,系统告诉他现在已经存在一个新版本了。
  4. 亚当打开了这个新版本,添加了自己的内容,并保存为另一个新版本。
  5. 通过查看版本历史,老板看到了一切在平稳运行。所有人都很开心,应用的开发人员也得到了晋升和奖赏。

这个叫做事件源。不需要维护明确的状态,只需要事件。这个过程非常清晰,整个事件的历史都可以回顾。

这个思想以及其它一些优势是函数式编程在服务器端日益增长的原因。

CLI

尽管web和node.js是两个主要的Javascript环境,但是一些务实和进取的用户在寻找把Javascript用于命令行的方式。

把Javascript作为一个命令行界面(CLI)脚本语言使用会是应用函数式编程的一个绝佳机会。 想象一下在搜索本地文件时使用惰性求值,或把整个bash脚本用函数式的Javascript写成一行。

与其它Javascript模块一起使用函数式库

web应用由许多东西组成:框架、库、API等等。他们可以互相依赖,作为对方的插件,或者仅仅是共存的对象。

  • Backbone.js
    • 一个使用RESTful JSON的MVP(model-view-provider)框架
    • 需要underscore.js库,这是backbone的唯一必需依赖
  • jQuery
    • Bacon.js通过绑定融入jQuery
    • Underscore和jQuery很好地互补
  • Prototype
    • 提供与Ruby的Enumerable风格相近的一系列函数
  • Sugar.js
    • 修改原生对象及其方法
    • 和其它库混合使用时需小心,尤其是Prototype

编译为Javascript的函数式语言

有时Javascript函数式核心之外那厚重的C外衣让你想要换一个函数式语言。好,可以滴!

  • Clojure和ClojureScript
    • Clojure是一个现代的Lisp实现,是一个具备全部函数式特征的语言
    • ClojureScript将Clojure编译为Javascript
  • CoffeeScript
    • CoffeeScript是一个函数式语言及其编译器的名称,它编译为Javascript
    • CoffeeScript表达式和Javascript表达式可以一一对应

这样的语言还有很多,包括Pyjs、Roy、TypeScript、UHC、PureScript等等。

第三章总结

你选择使用哪个数据库取决于你的需要是什么。需要函数响应式编程来处理事件和动态值?使用bacon.js。 需要无限流而不需要别的?用stream.js。想要一个函数式助手来补充jQuery?试试underscore.js。 需要严格特定多态的结构化环境?看看bilby.js。需要面面俱到的函数式编程工具?使用Lazy.js。 用这些都不爽?你自己写一个。

任何库都只擅长于它所使用的方式。尽管这章提到的库里面有几个缺点很少,大多数错误都会在不经意间就出现。 这取决于你选择的库是否正确,是否符合你的需求。

如果我们在Javascript环境中引入了代码库,也许我们同时还引入了想法和原则。也许我们可以通过 《Python之禅》来表达。作者:Tim Peter。

Beautiful is better than ugly
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one—and preferably only one—obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than "right" now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea—let's do more of those!

下一章 在Javascript中实现函数式编程的技术