1.1 Node是什么
在讨论所有Node相关的问题之前,我们必须要明确一个问题,Node是什么?
这看起来是一个再简单不过的问题,但如果不看答案(官网描述)直接回答起来却不是很容易,刚接触的开发者可能会认为Node就是JavaScript(笔者当初也是这么想的),这种看法并不准确。
1.1.1 Node与JavaScript
回过头来看官网的定义:
Node是一个JavaScript(严格来说是ECMAScript)运行时(runtime),所谓的runtime直译过来就是运行时组件,读者可以将其想象成一种编程语言的运行环境。这个运行环境包括了运行代码需要的编译器(解释器)以及操作系统的底层支持等。
对一门编程语言来说,相对于语法本身,更重要的是编译器(解释器)将如何对待这些语法。Node底层使用C++实现,语法则是遵循ECMAScript规范,如果创始人愿意,完全可以将Node创造成一个新的Ruby或者Python运行时,只不过名字大概就要改成Node.rb或者Node.py了。
这里有个扩展问题,编程语言是什么?
编程语言是一种抽象的规范,拿C++来说,真正的C++其实是厚厚的一摞文档,上面规定了每一个语法细节以及每一个有效输入对应的输出值。而开发者平时所使用的C++,例如Visual C++,是C++的一种实现。就好像数学概念里的正方形一样,我们找不到一个抽象的,纯粹的“正方形”,我们平时能看到的都是正方形的物体。
为什么是JavaScript
Ryan Dahl选择了JavaScript和V8,前者提供了灵活的语法,后者为前者的运行提供了足够高的效率和实现,例如非阻塞IO和事件驱动等。
关于Node.js的历史,可以参考朴灵的文章《Node.js与io.js那些事儿》,读者可上网自行搜索。
1.1.2 runtime和VM
1.runtime
最出名的runtime应该是VC++,微软出品的这套应用程序组件可以使开发者编写的C/C++语言程序在其中运行。VC++本身对C++还做了一些扩展,用来开发Windows程序,例如MFC等。
VC++可以编译和执行用户编写的C/C++代码,而开发者不考虑这背后到底是怎样实现的。站在开发者的角度来说,一个X语言的runtime表示开发者可以在这个runtime上运行X语言编写的代码,那么将这个概念扩大一些,Chrome也是一个JavaScript运行时,它靠背后的JavaScript引擎来运行JavaScript代码。
runtime可能会对编程语言做一些扩展,例如Node中的fs模块和Buffer类型就是对ECMAScript的扩展,此外,runtime也不一定支持语言规范定义的全部特性。
如果没有runtime的支持,语言规范就和废纸无异。例如截至当前的时间点(2017年5月),ES2015中的import语句还没有被任何浏览器或者Node支持(不考虑babel等转换工具),那么import语句就仅仅是一个纸面特性而已。
反过来讲,就算一个特性没有体现在标准里,而大多数的运行时都支持它,也可以变成事实上的规范,例如JavaScript(ES6之前)的__proto__属性。
因此当我们谈论一门语言,往往是在谈论它的实现,再具体一点,就是指其运行时实现。例如下面的代码,我们无法分辨这是一段Node代码或是JavaScript代码,虽然它们都能产生相同的输出。
如果一门X语言实现了ECMAScript规范,那么上面也可能是X语言的代码。
2.VM
VM的概念比较广泛,通常可以认为是在硬件和二进制文件的中间层。
C++编译好的二进制文件可以直接被操作系统调用,而对Java而言,编译好的字节码是交给虚拟机来运行的,这样的好处是对开发者屏蔽了操作系统之间的差异,对于不同操作系统环境的具体处理交给了虚拟机来完成,从这个角度来看,VM是对不同计算机系统的一种抽象。