史前时代

万维网的概念与基础技术,是 1989-1991 年间由 CERN 的 Tim Berners-Lee [2003] 创造的。Web 技术在高能物理圈内流通了几年,但并未在物理社区外引起强烈反响。它真正引发关注的契机,还是 1992-1993 年开发的 Mosaicg。这款由本科生 Marc Andreessen 和伊利诺伊大学香槟分校超算中心(NCSA)的 Eric Bina 研发的应用,本质上定义了「Web 浏览器」这一全新软件类别。

NCSA Mosaic 是不仅易装易用,而且带有图形界面的 Web 客户端。它在物理学界之外普及了万维网的概念,传播相当广泛。到 1994 年初,商业资本开始通过获得 Mosaic 代码许可或从头研发仿 Mosaic 式浏览器的方式,争相加入这波浏览器浪潮。SGI(硅谷图形公司)的创始人 Jim Clark 拉到了风险投资,并招来了 Marc Andreessen 和 Eric Bina 两人。在 1994 年 4 月,他们共同创立了一家公司。这家公司最终定名为 Netscape(网景通讯),目标是推出世界上最流行的浏览器来替代 Mosaic。为此,Netscape 从零开始研发了下一代 Mosaic 式浏览器 Netscape Navigatorg,它于 1994 年 10 月起开始发行。到 1995 年初,Netscape Navigator 达到了初始目标,正在迅速地取代 Mosaic。

Tim Berners-Lee 的 Web 技术的核心,是使用声明式的g HTML 标记语言来描述文档,将它们呈现为网页。但业界对于能方便最终用户编排应用操作的脚本语言g [Ousterhout 1997],也展示出了相当大的兴趣。这些语言诸如微软 Office 中的 Visual Basic 和苹果 AppleScript [Cook 2007] 之类,其设计目标并非用于实现应用核心的复杂数据结构和算法组件。相反地,它们为用户提供了将此类应用组件「粘合」在一起的新方式。在 Netscape 扩大万维网受众范围的途中,一个重要的问题就是脚本语言「是否应该」与「如何」集成到网页中。

Brendan Eich 加入网景

Brendan Eich4 于 1985 年在伊利诺伊大学香槟分校硕士毕业,然后立即入职了 SGI 公司,主要从事 Unix 内核和网络层的工作。1992 年,他在离开 SGI 后加盟了 MicroUnity。这是一家资金雄厚的新兴公司,致力于开发视频媒体处理器。在这两家公司,他都实现了用于支持内核与网络编程任务的小型专用语言。在 MicroUnity,他还在 GCC 编译器g上做了些工作。

1995 年初,Brendan Eich 被 Netscape 以「在浏览器里写 Scheme」5为诱饵打动而跳槽了。但当 Eich 于 1995 年 4 月 3 日加入 Netscape 时,他发现公司在产品营销与编程语言上的现状都很复杂。Netscape 在 1994 年底拒绝了微软的低价收购要约。此后 Netscape 管理层预计自己将直面微软「拥抱,扩展,灭绝」战略 [Wikipedia 2019] 的攻击。在盖茨的直接领导下,微软已经迅速意识到它们即将推出的封闭生态信息应用 Blackbird 项目 [Anderson 2007],在跨平台 Web 的兴起之下将无足轻重。因此,盖茨的「互联网浪潮」备忘录 [Gates 1995] 将微软的战略从 Blackbird 重新引导到了 Internet Explorerg 与一整套服务器产品上,以应对 Netscape 的攻城略地。

网页脚本语言的备选项,包括 Scheme 这样的研究型语言,Perl / Python / Tcl 这样基于 Unix 的实用型语言,以及微软 Visual Basic 这样的专有语言。Brendan Eich 希望的是在浏览器中实现 Scheme。但在 1995 年初,Sun(太阳微系统公司)开始为当时尚未发布的6 Java 发起了游击营销活动 [Byous 1998]。Sun 和 Netscape 迅速达成协议,决定将 Java 集成到 Netscape 2 中。Eich 回忆说,Marc Andreessen 在 Netscape 会议上的口号是「Netscape 加 Java 干掉 Windows」。在 1995 年 5 月 23 日 Sun 的 Java 发布会上,Netscape 宣布了他们授权 Sun 的 Java 技术 [Netscape 1995a] 在浏览器中使用的意向。

Netscape 内部的这项快速决策,使得对 Scheme / Perl / Python / Tcl / Visual Basic 等脚本语言的选型都受到了严重的阻碍,它们在商业利益和(或)上市时间的角度上看都是不可行的。对 Netscape 和 Sun 的高层,尤其是 Marc Andreessen 和 Sun 的 Bill Joy 来说,他们认为唯一可行的方法是设计实现一门「小语言」7来补充 Java。

对这一决策的怀疑者在 Sun 占支配地位,在 Netscape 也占多数。他们质疑是否需要这样一门更简单的脚本语言:Java 是否不适合脚本编写?如何解释为什么两种语言比一种更好?Netscape 是否具备创建新语言的专业能力?

第一个反对意见很容易反驳。1995 年春季的 Java 并不适合初学者使用,人们必须将 Java 主程序的代码体放在包内部g声明下名为 main 的静态方法g中,还必须为所有参数、返回值和变量声明静态类型g。从 Visual Basic 与 Visual C++ 互补,以及许多 Unix 语言与原生代码组件互补的经验来看,Java 明显对于「胶水」脚本编写者来说还不够简单。

克服第二个反对意见的依据,则是对微软产品的参考。对于专业的 Windows 应用程序员,微软向他们出售 Visual C++。而对于业余爱好者、兼职程序员、设计师、会计师和其他人员,微软提供了 Visual Basic 作为脚本语言。这样那些经验不足的兼职程序员就可以「胶合」定制使用由 Visual C++ 构建的组件了。名为「Visual Basic for Applications」(VBA)的 Visual Basic 版本已经集成到了微软 Office 中,以支持这些应用的用户扩展和脚本需求。

克服了前两个反对意见后,Marc Andreessen 提出了浏览器脚本语言的代号「Mocha」。据 Eich 说,这一提议还希望在适当时候将该语言重命名为「JavaScript」。这种 Java 的辅助语言必须「看起来像 Java」,保持易用性并「基于对象」,而不是像 Java 这样基于类。

只剩下最后一个反对意见了:Netscape 是否具备创建有效脚本语言的专业知识,并应用到 1995 年 9 月的 Netscape 2 beta 上?Brendan Eich 的任务就是通过创建 Mocha 来证明这一点。

Mocha 的故事

随着 Java 发布的临近,Brendan Eich 认为时间至关重要。双鸟在林不如一鸟在手,因此他在 1995 年 5 月8花了连续十天进行第一个 Mochag 实现的原型设计。这项工作赶在了可行性论证的最后期限之前完成。这个 Demo 包括语言的最小实现,并最小化地集成到了 Netscape 2 浏览器的 pre-alpha 版本中。

Eich 的原型是在 SGI Indy Unix 工作站 [Netfreak 2019] 上开发的,使用了一个手写的词法分析器和递归下降解析器。这个解析器发出的是字节码指令,而不是语法分析树(parse tree)。字节码解释器g简单而缓慢9

字节码特性源于 Netscape LiveWire 服务器10的需求,其开发人员甚至在将 Mocha 原型化之前就希望将其嵌入。这支团队的前 Borland 管理和工程人员都坚信动态脚本语言的未来,但他们希望使用字节码而非源码解析的方式,加快服务器应用的加载速度。

Marc Andreessen 强调,Mocha 应该非常易于使用,任何人都可以直接在 HTML 文档中编写几行。Sun 和 Netscape 的高层管理人员则重申了 Mocha 应该「看起来像 Java」的要求,明确排除了 BASIC 式的东西。但这种 Java 式的外表也带来了对 Java 式行为的期望,这种期望影响了语言对象g模型的设计,以及原始类型(如 boolean / int / double / string 等)的语义。

在外表接近 Java 的要求之外,Brendan Eich 可以自由选择大多数语言设计细节。加入 Netscape 后,他探索了一些「易于使用」与「教育用途」的语言,包括 HyperTalk 语言 [Apple Computer 1988],Logo 语言 [Papert 1980] 和 Self 语言 [Ungar and Smith 1987]。所有人都认可 Mocha 将会「基于对象」但没有类。因为支持类将花费很长时间,并有与 Java 竞争的风险。出于对 Self 的认可,Eich 选择使用带有单个原型链接的委托g机制,来创建动态的对象模型。他认为这样可以节省实现成本,但最后还是没有足够时间在 Mocha 原型中暴露该机制。

对象是通过为构造函数g应用 new 运算符的方式创建的。名为 Object 的默认对象构造函数,与其他内建对象一起内置在环境中。每个对象由零个或多个属性组成。每个属性g都有一个名称(也叫属性键g)和一个值,该值可以是函数g、对象或其他几种内建数据类型之一。可以通过为未使用的属性键赋值的方式,来创建出新属性。属性没有可见性或赋值限制,构造函数还可以提供一组初始属性。创建对象后,也可以将其他属性添加上去。LiveWire 团队特别喜欢这种非常动态的手法。

尽管 Scheme 的诱惑已经不再,Brendan Eich 仍然发现 Lisp 式的函数一等公民g概念很有吸引力。函数一等公民对应的这套工具深受 Scheme 习惯用法的启发,方法不必被包含在类中。这包括支持顶层的子程序、将函数作为参数传递、对象上的方法,以及事件处理器(event handler)。由于时间限制,函数表达式(也叫 lambda 表达式g,或简称 lambda)被延期,但在语法中得以保留。事件处理器和对象方法通过向 Java(在 C++ 之后)借鉴的 this 关键字得以统一。在所有函数中,它都用于表示该函数在作为方法被调用时的上下文对象。

在与 Marc Andreessen 以及一些早期的 Netscape 工程师11做非正式讨论的激励之下,这个原型支持了 eval 函数。它可以解析执行包含程序的字符串。直觉上,这种动态的「字符串到程序」编程对 Web 浏览器和服务器上的某些应用很重要12。不过,支持 eval 的决策立刻带来了相应的后果。一些场景需要函数通过类似 Java 的 toString 方法,将其源码反编译为字符串。为此 Eich 选择在十天冲刺13内实现字节码反编译器,因为不论将源码放在主存储器(RAM 或 ROM)还是从辅助存储器(硬盘等)中恢复,对某些需支持的目标体系结构而言,代价都可能过于昂贵。对于受 Intel 8086 16 位分段内存模型约束的 Windows 3.1 计算机而言,情况尤其如此。因为对于内存中无边界或大型的结构体,需要覆盖并手动管理内存中的多个段。

十天结束时,原型在一次全体 Netscape 工程人员的会议上进行了演示(图 2)。演示获得了成功,这使人们对于交付更加完整且集成度更高的 Netscape 2 感到过分乐观。Netscape 2 的首个 beta 版本计划于当年 9 月发布。Brendan Eich 在那个夏天的主要工作,则是将 Mocha 更全面地集成到浏览器中。这需要设计实现使 Mocha 程序能与网页交互的 API。同时,他还需要将语言的原型实现转变为可交付的软件,并响应早期内部用户的错误报告、更改建议与特性需求。

这个十天创建 Mocha 故事的更多细节,可以参见 Brendan Eich 的复述 [Eich 2008c, 2011d; JavaScript Jabber 2014; Walker 2018]。通过互联网档案馆,还可以获得 Mocha 生产版本的源码 [Netscape 1997b]。Jamie Zawinski [1999] 的「Netscape 宿舍」也描述了在此期间作为 Netscape 软件开发者的工作经历。