JavaScript是什么?让我们确认一下JavaScript的定义:JavaScript是一种解释型动态语言。解释型语言相对于编译型语言存在。源代码不是直接编译成目标代码,而是转换成中间代码,然后由解释器解释并运行中间代码。主流的编程语言包括编译型(如C++)、解释型(如JavaScript)和半解释半编译型(如Java)。代码如何工作?首先,让我们了解代码的工作原理。我们知道代码是由CPU来执行的,而现在的CPU并不能直接执行if...else等语句,只能执行二进制指令。但是二进制指令对人类来说真的很不友好:我们很难快速准确地判断一条二进制指令1000010010101001代表什么?于是科学家发明了汇编语言。汇编语言汇编语言实际上是二进制指令的助记符。假设10101010代表读取内存的操作,内存地址为10101111,寄存器地址为11111010,那么完整的操作10101010101011111111010代表读取某个内存地址的值并加载到寄存器中,汇编语言不会改变这种运行方式,它只是二进制指令的映射:LD:10101010id:10101111R:11111010这样,上面的指令就可以表示为LDidR,大大增强了代码的可读性。但这还不够友好,CPU只能执行三地址表达式,与人类的思维方式和语言模式相去甚远。于是伟大的科学家又发明了高级语言。高级语言“代码是写给人看的,不是给机器看的,只是给计算机执行的。”高级语言之所以被称为“高级”,是因为它们更符合我们的思维和阅读习惯。if...else语句看起来比1010101010舒服多了。但是计算机不能直接执行高级语言,所以需要将高级语言转换成汇编语言/机器指令才能执行。这个过程就是编译。JavaScript需要编译吗?JavaScript无疑是一门高级语言,所以必须先编译后才能执行。但是为什么我们称它为解释型语言呢?它和编译型语言、半解释型和半编译型语言有什么区别?让我们从编译开始。在编译之前,我们已经了解了编译的概念。再来说说平台:同样的C++代码,在Windows上会编译成.obj文件,但在Linux上会生成.o文件。两者不能共同使用。这是因为一个可执行文件除了需要代码外,还需要操作系统API、内存、线程、进程等系统资源,不同的操作系统有不同的实现。比如大家熟悉的I/O多路复用(事件驱动的灵魂),在Windows上的实现方案是IOCP方案,在Linux上是epoll。因此,对于不同的平台,编译语言需要单独编译甚至编写,生成的可执行文件格式也不尽相同。跨平台Java更进一步,引入字节码实现跨平台运行:.java文件无论在什么操作系统下编译成.class文件(这是字节码文件,一种中间形式的目标代码).然后Java为不同的系统提供了不同的Java虚拟机来解释和执行字节码文件。解释和执行不会生成目标代码,但最终会转换成汇编/二进制指令供计算机执行。如果我们完全独立编写一个简单的操作系统,它能运行Java吗?显然不是,因为这个系统没有对应的JVM。因此,Java的跨平台和其他任何语言的跨平台都是有局限性的。Java中使用半解释半编译的好处是大大提高了开发效率,但相应地降低了代码的执行效率。毕竟虚拟机是有性能损耗的。解释和执行JavaScript更进一步。它被完全解释和执行,或称为即时编译。它将没有中间代码生成,也没有目标代码生成。这个过程通常由主机环境(例如浏览器、Node.js)负责。编译过程现在我们已经确认,即使是解释型和执行型语言也需要编译。那么代码是如何编译的呢?让我们来看看。词法分析词法分析会将语句分解为词法单元,即Token。functionsquare(n){returnn*n;}这个函数会被词法分析器识别为function,square,(,n,),{,return,,n,*,n,}并标记它们,表示是否这是一个变量或操作。语法分析的过程会将Token转化为抽象语法树(AST):{type:'function',id:{type:'id'name:'square'},params:[{type:'id',name:'n'}]...}优化和代码生成在这一步中,编译器会做一些优化工作,比如删除冗余操作,删除不用的赋值,合并一些变量等,最后生成目标代码。由于即时编译语言的编译通常发生在执行前几微秒,编译器无暇做太多的优化工作。这也是早期JavaScript与编译型语言相比性能较弱的原因之一。但就目前而言,得益于V8引擎(相对于早期的JavaScript引擎转换成字节码或解释执行,Node.js可以使用V8提供的JS2C工具将JavaScript翻译成C++代码),JavaScript与其他语言的性能差距是不再微不足道。链接和加载目标代码基本上不能独立运行。一个应用程序通常由多个部分(模块)组成。例如,C++中的简单输出需要导入标准库iostream:#include
