- JavaScript语言精髓与编程实践
- 周爱民
- 2591字
- 2020-08-28 21:33:23
1.6 JavaScript的应用环境
在此前的内容中,我讨论的都是JavaScript语言及其规范,而并非该语言的应用环境。在大多数人看来,JavaScript的应用环境都是Web浏览器,这也的确是该语言最早的设计目标。然而从很早开始,JavaScript语言就已经在其他的复杂应用环境中使用,并受这些应用环境的影响而发展出新的语言特性了。
JavaScript的应用环境,主要由宿主环境(host environment)与运行期环境构成。其中,宿主环境是指外壳程序(Shell)和Web浏览器等,而运行期环境则是由JavaScript引擎内建的。图1-9是由它们共同构建的对象编程系统的基本结构。
图1-9 由宿主与运行期构成的应用环境
1.6.1 宿主环境
JavaScript是一门设计得相对“原始”一点点的语言,它被创生时的最初目标仅仅是为Netscape提供一个在浏览器与服务器间都能统一使用的开发语言。简单地说,它原来是想让B/S架构下的开发人员用起来都舒服那么一点点的。这意味着最初的设计者希望JavaScript语言是跨平台的,能够提供“端到端(side to side)”的整体解决方案。
然而,事实上这非常难做到,因为不同的平台提供的“可执行环境”不同。而宿主环境就是为了隔离代码、语言与具体的平台而提出的一个设计。一方面我们不能让浏览器上拥有一个巨大无比的运行期环境(例如像虚拟机那么大),另一方面服务器端又需要一个较强大的环境,因此JavaScript就被设计成了需要“宿主环境”的语言。
ECMAScript规范并没有对宿主环境提出明确的定义。比如说,它没有提出标准输入输出(stdin、stdout)需要确切地实现在哪个对象中。为了弥补这个问题,RWC在WebAPIs规范中首先就提出了“需要一个window对象”的浏览器环境。这意味着在RWC或者浏览器端,是以window对象及其中的Document对象来提供输入输出的。
但这仍然不是全部的真相。因为“RWC规范下的宿主环境”,并不等同于“JavaScript规范下的宿主环境”。本书并不打算讨论与特定浏览器相关的细节问题,因此我们事实上在说的是JavaScript的一个公共语言环境,或者说公共的宿主环境的定义。作为程序运行过程中对输入输出的基本要求,本书设定宿主环境在全局应当支持如表1-3所示的方法。
表1-3 本书对宿主环境在全局方法上的简单设定
*注1:write()与writeln()在浏览器中是Document对象的方法。为遵循这一惯例,在本书的所有测试范例中并不直接使用这两个方法。但这里保留了它们,以描述宿主环境的标准输入输出。
对于不同的宿主来说,这些方法依赖于不同的对象层次的“顶层对象(或全局对象)”。例如浏览器宿主依赖于window对象,而WSH宿主则依赖于WScript对象。但在本书中,调用这些方法时将略去这个对象。因此,至少它看起来很像是Global对象上的方法(事实上,大多数的宿主默认“顶层对象”不需要使用全名的约定)。
下面的代码说明在具体的宿主环境中如何实现本书所适用的alert()方法。例如:
// 示例1: .NET Framework中的JScript 8.0, (当前的)顶层对象取决于import语句 // (注: JScript.NET中的脚本需要编译执行) import System.Windows.Forms; function alert(sMessage) { MessageBox.Show(sMessage); } alert(' Hello, World! ' ) // 示例2:浏览器环境中使用的顶层对象是window alert(' Hello, World! ' ); // 示例3:WSH环境中使用的顶层对象是WScript,但必须使用全名 function alert(sMessage) { WScript.Echo(sMessage); } alert(' Hello, World! ' );
1.6.2 外壳程序
外壳程序(shell)是宿主的一种。不过在其他一些文档中并不这样解释,而是试图将宿主与外壳分别看待。这其中的原因在于将“跨语言宿主”与“应用宿主”混为一谈。
Windows环境中,微软提供的WSH(Windows Script Host)是一种跨语言宿主,在该宿主环境中提供一个公共的对象系统,并提供装载不同的编程语言引擎的能力。如此一来,WSH可以让多个语言使用同一套对象——这些对象由一些COM组件来实现并注册到Windows系统中。所以,我们在IE浏览器中看到,既可以用VBScript操作网页中的对象,也可以用JScript来操作它。基本上来讲,IE浏览器采用的是与WSH完全相同的宿主实现技术。
多数JavaScript引擎会提供一个用于演示的外壳程序。该外壳程序通过一种命令行交互式界面来展示引擎的能力,在UNIX/Linux系统中编程的开发人员会非常习惯这种环境,而在Windows中编程的开发人员则不然。在这种环境下,可以像调试器中的单步跟踪一样,展示出许多引擎内部的细节。图1-10是SpiderMonkey JavaScript随引擎同时发布一个外壳程序,它就是(该脚本引擎的)一个应用宿主。
如同引擎提供的这种外壳程序一样,我们一般所见的Shell是指一种简单的应用宿主,它只负责提供一个宿主应用环境:包括对象和与对象运行相关的操作系统进程。但是在另外一些情况下,“外壳(而不是外壳程序)”和“宿主”也被赋予一些其他的含义。例如在WSH中,“宿主”是指整个宿主环境和提供该环境的技术,而Shell则是其中的一个可编程对象(WScript.WshShell)——封装了Windows系统的功能(如注册表、文件系统等)的一个“外壳对象”,而非“外壳程序”。
图1-10 SpiderMonkey JavaScript提供的外壳程序
讨论脚本引擎本身时,我们并不强调宿主环境的形式是WSH这种“使用跨语言宿主技术构建的脚本应用环境”,还是SpiderMonkey JavaScript所提供的这种“交互式命令行程序”。我们只强调:脚本引擎必须运行在一个宿主之中,并由该宿主创建和维护脚本引擎实例的“运行期环境(runtime)”。
1.6.3 运行期环境
在不同的书籍中对JavaScript运行期环境的阐释是不一致的。例如在《JavaScript权威指南》中,它由JavaScript内核(Core)和客户端(Client)JavaScript两部分构成;而在《JavaScript高级程序设计》中,它被描述成由核心(ECMAScript)、文档对象模型(DOM)、浏览器对象模型(BOM)三个部分组成,如图1-11所示)。
图1-11 对“运行期环境”的不同解释
本书是从引擎的角度讨论JavaScript的,因此在本书看来,与浏览器相关的内容都属于“应用环境”:属于宿主环境或属于用户编程环境。图1-9由宿主与运行期构成的应用环境”表达了这种关系。在这样的关系中,运行期环境是由宿主通过脚本引擎(JavaScript Engines)创建的。图1-12说明应用程序,是宿主在这里可以看成一个应用程序,是如何创建运行期环境的。
图1-12 应用(宿主)通过引擎创建“运行期环境”的过程
这相当于是说,在本书中讲述的运行期环境,是特指由引擎创建的初始应用环境。这样解释运行期环境的特点,而并不强调(或包括)在应用、宿主或用户代码混杂作用的、运行过程中的应用环境。在初始状态下的运行期环境主要包括:
█ 一个对宿主的约定。
█ 一个引擎内核。
█ 一组对象和API。
█ 一些其他的规范。
换而言之,这是指一个引擎自身的能力。不过即使如此,不同的JavaScript脚本引擎所提供的语言特性也并不一致。因此,在本书中若非特别说明,JavaScript是指一种通用的、跨平台和跨环境的语言,并不特指某种特定的宿主环境或者运行环境。也就是说,它是指ECMAScript 262所描述的语言规范。目前最常见的实现ECMAScript Ed3或JavaScript 1.5以上版本规范的引擎包括如表1-4所示的几种。
表1-4 最常见的一些JavaScript引擎(部分)
*注1:Brendan Eich为验证JavaScript语言的自实现能力而写的一套代码,被称为“JS implemented in JS”。有许多项目基于该代码进行扩展,例如NarrativEJS基于该项目实现了JavaScript上的解释器、编译器和扩展语法。