ClojureScript通过Javascript将Clojure引入到浏览器端

 本文来源于我在InfoQ中文站翻译的文章,原文地址是:http://www.infoq.com/cn/news/2011/08/clojurescript

你可以通过ClojureScript使用Clojure语言编写代码,然后将其编译为Javascript。ClojureScript是由Clojure的创建者Rich Hickey发布的(这里是Rich的ClojureScript声明ClojureScript声明的视频)。

ClojureScript是Clojure的一个子集,目前缺失的特性与库要么是尚未实现,要么是由于对Javascript VM毫无意义而不会添加进来,比如线程支持、Java集成等等。ClojureScript Wiki上详细列出了Clojure与ClojureScript之间的差异列表

设计ClojureScript的缘由

ClojureScript的基本原理(摘录自文档)就是“做Javascript所能做到的事情”。Javascript VM正变得越来越快,应用的领域也越来越广。借助于ClojureScript,我们可以将Clojure用于GUI客户端编程、包括移动平台(带有强大的HTML组件与Javascript VM)。运行Clojure需要客户端安装有Java VM;虽然桌面操作系统可能都有Java VM,但浏览器的应用却更加普遍,并且每个客户端操作系统都会有浏览器。这种局面在移动平台上就更清晰了——目前还没有哪个主流的移动平台带有可以直接运行Clojure的Java VM。Android并不带Java VM;它需要将所有字节码转换为Dalvik VM字节码;现在人们还在为能让Clojure顺利运行在Android上而不断努力

ClojureScript还有另一个使用场景:命令行程序。Java的命令行程序使用的并不多,特别是需要频繁加载的程序,原因在于JVM的启动时间(诸如Nailgun之类的解决方案就是为了解决这个问题而出现的)。正如Rich Hickey所述,如果将命令行程序编译为Javascript,然后使用Node.js运行会大大降低启动时间。

关于在服务器端使用Javascript的一个普遍观点是可以在服务器端和客户端重用相同的代码;特别是像输入验证这样的逻辑。借助于ClojureScript依然可以做到这一点:使用ClojureScript编写的算法可以编译为Javascript并运行在浏览器中,同时还可以用在服务器端的Clojure代码基中,这时算法会被编译为Java字节码。它还为在服务器端完全抛弃Java提供了一种可能——使用ClojureScript编写应用,然后在Javascript栈如Node.js上运行服务器端。

ClojureScript的实现方式

ClojureScript编译器并没有任何Javascript代码,它使用Clojure编写,这意味着编译器需要运行在Java VM上。因此,ClojureScript缺少eval和其他类型的运行时代码加载能力。ClojureScript的目的在于编写程序并将其编译为Javascript而非作为浏览器中的REPL。

然而,确实有ClojureScript REPL,它位于ClojureScript仓库中,使用Clojure实现,它用于启动Rhino——使用Java编写的Javascript运行时。要想运行ClojureScript代码,需要将其发送给基于Clojure的ClojureScript编译器,后者会返回Javascript代码,然后由Rhino运行。在浏览器中不通过Clojure实例来这么做显然是不可行的——至少需要将Clojure编译器工具链编译成Javascript才行。

ClojureScript代码可以使用Clojure宏。宏是个编译期的特性;如果ClojureScript代码引用了宏调用,那么宏扩展就会被ClojureScript编译器所执行,在Clojure中执行。

虽然目前ClojureScript编译器只能运行在Clojure上,但ClojureScript却带有Clojure Reader。说明一下,Reader基本上是个Clojure解析器;它会将文本形式的Clojure程序转换为Clojure数据结构,然后再对该数据结构求值。ClojureScript自带的Reader可以解析Clojure(Script)符号中的数据,然后以数据的形式将其传递给ClojureScript代码。ClojureScript的Reader只会解析,由于运行期并没有ClojureScript编译器,因此它并不会求值。

ClojureScript之所以带有Reader的目的在于读取Clojure数据,就像Javascript无需求值就可以读取JSON数据一样。我们可以通过Clojure生成Clojure数据并将其发送给ClojureScript,反之亦然。

事实上,ClojureScript编译器工具链的另一部分是Google Closure工具集。没错,带有字母“s”的Closure,他们的命名很像,但Google的Javascript工具Closure集合,特别是Closure编译器和Advanced Compilation都用到了。

Closure可用于几个目的:其中一个目的就是库与依赖的管理,这需要库与导出符号。ClojureScript命名空间定义与Google Closure的provide与require调用对应。这样,ClojureScript就可以轻松使用Google Closure库了,该库拥有非常丰富的GUI组件和其他特性。

ClojureScript与Clojure语言非常像,但其标准库是Clojure的一个子集。目前,ClojureScript的标准库有clojure.string、clojure.set、clojure.walk、clojure.zip等,同时更多的库将会被添加进来。将Clojure库移植到ClojureScript的难度取决于代码本身;仅使用基本语言元素来转换数据的纯算法代码很容易移植。使用了I/O库、线程、特定Java库的库则需要更多的工作;我们还需要分解平台特定的代码,这都是必要的工作。

Google Closure一个有趣的用法就是作为优化编译器的后端。ClojureScript编译器所生成的代码会进行优化以便与Closure的Advanced Compilation协同工作,它会接收Javascript源代码并对其进行优化,包括内联的函数调用、删除死代码(dead code)与无用的函数。这种方式的一个优势在于ClojureScript编译器无需实现这些优化工作了;它只需将其委托给Google Closure即可。

未来,其他的工作也可以委托给Google Closure,比如为调试器提供源代码图,也就是说数据结构可以匹配生成的Javascript代码和生成它的ClojureScript代码。Google Closure对SourceMaps提供了一定的支持,同时Mozilla与WebKit项目都在致力于扩展其浏览器、调试器和Javascript引擎来支持他们。

不久大家就会看到关于ClojureScript的更多信息;其中一个信息来源就是Michael Fogus的博客,他已经发布了关于编译器系列文章的第一部分。Michael从事于ClojureScript实现。对编译器内部机制感兴趣的开发者们还应该看看ClojureScript开发说明,上面列出了很多信息,比如ClojureScript语言结构及其转换而成的Javascript结构。

Clojure社区已经开始尝试ClojureScript了。Brian McKenna已经开始尝试使用宏来与回调地狱(callback hell)展开斗争,这指的是异步I/O的每个操作都需要提供一个回调来接收操作的结果——这么做一两次还行,但对于连续的算法来说实在过于冗长了。在JS世界中有不少解决方案在尝试解决这个问题,从新的语言如StratifiedJS使用Javascript编写的嵌入式DSL库

Brian混合使用了嵌入式DSL解决方案与ClojureScript,就像是带有宏的LISP一样,效果不错。试验处理了一系列的表达式并将其编译为嵌套的回调,每个回调都在序列中执行一个表达式,然后通过setTimeout调度序列中其他回调的执行。这不禁让人想起了支持Monads(Haskell的do符号)的语言或是F#的Compuational Expressions(Async Workflows)的程序分号方式。接下来将会支持更多的结构化程序设计概念,如循环,使用宏将其转换为这种连续的传递形式。

Justin Grant的一篇博文介绍了如何实现一个算法并使用Google Closure的Canvas支持来绘制图像

ClojureScript位于GitHub。要想了解更多信息,请查看clojure.com上的ClojureScript声明,或是GitHub上的ClojureScript Wiki,上面有大量的信息,包括ClojureScript的基本原理快速起步指南等内容。Clojure邮件列表也是个不错的信息来源,你可以从中了解到ClojureScript的使用方式,社区也在上面讨论开发工作流、工具等信息。

查看英文原文:ClojureScript Brings Clojure To The Browser via Javascript

你可能感兴趣的:(JavaScript)