A.1 Oracle Nashorn
Nashorn是Oracle推出的一种开源的服务器端JavaScript方言,从Java 8起,就包含在Java运行时环境(JRE)中,这意味着只要在主流平台(Windows、macOS、Linux和Raspberry Pi)上安装了Java 8(或更高版本),就可使用它。它取代了随Java开发包(JDK)第6版和第7版的Oracle实现提供的JVM JavaScript方言Mozilla's Rhino。
Nashorn类似于流行的使用Google V8 JavaScript引擎的服务器端JavaScript平台Node.js,它们都在服务器上运行JavaScript脚本,而不像客户端JavaScript引擎那样运行在浏览器中。Node.js和Nashorn脚本彼此不兼容,明白这一点很重要。这是因为Node.js和Nashorn都在ECMAScript语言的基础上添加了独特且不兼容的扩展。Node.js和Nashorn的一个重要不同在于,Nashorn没有实现Node.js内置的异步事件系统。
Oracle曾资助一个旨在让Nashorn与Node.js兼容的开源项目,但最后放弃了这个项目。
由于Nashorn是完全在JVM上实现的,因此与JVM库和框架兼容。它能够与Java对象交互,还能向Java库和框架传递JavaScript对象。运行JavaScript代码时,Nashorn在内部将其编译成Java字节码,并运行在内存中动态生成的字节码;这样的情况我们在前几章使用Scala、Clojure和Kotlin的交互式shell运行包含源代码的文本文件时见到过。与这些语言一样,Nashorn也提供了一个交互式REPL shell,可用来交互地输入JavaScript代码。
有关Java 8版Nashorn的官方文档,请参阅如下URL:https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/。
Nodeclipse插件让Eclipse支持Node.js,它有一个与Nashorn兼容的版本;你可通过Eclipse Marketplace安装这个版本,让Eclipse IDE支持Nashorn。

A.1.1 在基于JVM的项目中嵌入Nashorn
Nashorn的一个亮点是,在使用与Java兼容的JVM语言编写的项目中,可轻松地嵌入其引擎。在Java项目中,要支持只适用于特定客户的自定义业务逻辑验证,可让客户或经过训练的业务咨询人员输入将在运行阶段由Nashorn执行的自定义JavaScript脚本。这些脚本无需随应用程序一起发布,而是可以放在不同的目录乃至中心数据库中。
这可能带来严重的安全隐患,因为如果没有采取额外的措施,Nashorn脚本对JVM和应用程序内部的访问将不受任何限制。对外开放项目前,必须对安全实践有深入的了解。
第1章说过,JVM平台的优点之一是,可在同一个基于JVM的项目中包含使用不同JVM语言编写的代码。通过在Web应用程序的后端混合使用Java和JavaScript代码,可提供一些有趣的可能性。很多通常运行在Web浏览器JavaScript引擎上的主流JavaScript框架,不能直接在服务器端JavaScript引擎(如Nashorn)上运行。这是因为Nashorn与Node.js一样,不向JavaScript脚本提供文档对象模型(DOM),这不同于Web浏览器的JavaScript引擎。然而,有少量但越来越多的JavaScript库乃至经过精心设计的JavaScript代码不需要DOM,因此既能够运行在Nashorn等服务器端引擎上,又能运行在Web浏览器的客户端JavaScript上。这使得可以在服务器上运行前端JavaScript代码,并将其服务器端渲染输出提供给生成的HTML。这让搜索引擎能够看到JavaScript生成的HTML输出,而这种输出原本只有支持AJAX的Web浏览器才能看到。
DOM是Web浏览器向JavaScript引擎提供的一项功能,而不是JavaScript语言本身的功能。
JavaScript是一种并不适合用于并发编程的语言。JavaScript代码严重依赖可变的全局变量,这意味着很容易在线程中修改函数和数据,导致其他同时运行的线程出现难以发现的bug。大多数服务器端JavaScript引擎都不允许同时在多个线程中运行代码,但由于Nashorn可使用JVM线程类,因此在Nashorn中,这是可能的,不过必须仔细地规划。
A.1.2 运行Nashorn
由于Nashorn会随Java 8或更高版本一起安装,且包含在Java开发包(JDK)和Java运行时环境(JRE)中,因此运行Nashorn易如反掌,只需执行JDK或JRE的bin目录中的一个命令即可。有关如何执行位于该目录中的命令的更详细信息,请参阅第2章。
在命令提示符或终端中,切换到JRE或JDK的bin目录,并执行如下命令:
jjs
命令jjs表示Java JavaScript。未指定任何命令行选项时,它将启动交互式REPL shell。这个命令还可用来运行JavaScript脚本文件,为此只需向它传递脚本的路径即可。这个命令有很多命令行选项,要查看所有的命令行选项,可使用命令行选项-help。
A.2 Jython(Python)
Python是一种动态语言,既易于学习又功能强大。它提供了庞大的运行时库,其生态系统也生气勃勃,这都是拜它越来越流行所赐。Python支持面向对象编程,但不要求必须这样做;另外,它还支持大量的函数式编程结构。Jython是Python的JVM实现,当前基于Python 2.7。编写本书期间,已宣布即将着手开发基于Python 3的版本。
Python 3修复了更低版中存在的大量问题,但为此付出了在很多地方都不兼容的代价。有鉴于此,多年来很多开发人员依然使用Python 2来开发项目。现在风向正在发生变化,Python开发小组可能在2020年放弃Python 2。
Jython是一种开源的Python语言实现,只能运行在JVM上。它推出的时间早于Groovy,属于第一批JVM语言。那时的Java东家Sun公司对这个项目充满期待,专门招聘了一些开发人员来开发Jython;负责Python核心的Python软件基金会也为这个项目做出了贡献。
Jython可从其主页下载:http://www.jython.org/。
要让Eclipse IDE支持Jython,PyDev插件是很不错的选择。这个插件可通过Eclipse Marketplace来安装:http://www.pydev.org/。

A.2.1 CPython和Jython的不同之处
Python的参考实现为CPython,而名称CPython昭示着它是使用C语言编写的。CPython默认支持多线程编程,但不能在多核CPU的多个内核中运行线程。CPython使用单个CPU内核来运行所有的线程,因此它快速在线程之间切换,而不是同时在多个内核中运行线程。由于Jython基于JVM强大的线程实现,因此不存在这样的限制。另外,众所周知,与其他现代编程语言相比,CPython相对较慢,而JVM因良好而可预测的性能而受到普遍的赞誉。
相比于类似的Java代码,Jython代码的执行速度还是要慢些。这是因为Jython是一种动态语言,很多事情都是在运行阶段决定的,而在Java等静态语言中,这些决定是在编译阶段做出的。第11章对此做了更详细的解释。
Jython不能运行依赖于原生C语言库(或其他随平台而异的库)的Python代码,这意味着Jython无法使用很多流行的Python框架和库,因为它们依赖C语言库来改善性能。这种问题并非Jython独有的,其他Python实现(如PyPy)也存在这样的问题。由于Jython是一种基于JVM的语言,因此Jython代码可使用大多数基于JVM的框架和工具包。
A.2.2 运行Jython
下载并安装Jython后,将其子目录bin添加到环境变量Path中。然后,就可执行如下命令来启动Jython的REPL了:
jython
要退出这个REPL,可执行函数exit()。
要查看所有的命令行选项,可在执行命令jython时加上参数--help。
