深入javascript引擎对脚本的处理过程
javascript程序与宿主环境进行交互,是通过一系列预定义的方法和属性实现的,这些方法和属性会再映射成浏览器的内部原生代码,所以与其他很对常规的编程语言不同,浏览器开放的这些借口往往受限且有针对性。
a)无论是独立的窗口还是在框架里面,每个展示在浏览器里面的html文档,都被赋予了一个独立的javascript执行环境实例,在这个环境里面加载的脚本的所有全局变量和函数都拥有一个独立的命名空间。
b)同一个文档的所有脚本都运行在同一个执行环境里面,共享同一个沙箱,并且能够通过浏览器提供的API与其他上下文环境交互。
c)在特定的执行上下文里面,每段javascript代码块都是自成体系处理的,顺序也基本确定。每段代码块都是由若干符合语法格式的独立单元组成,处理的过程包括清晰而且连续的三个步骤:源码处理,函数解析,代码执行。
1)源码处理
源码处理阶段会检查脚本代码块里面的语法,通常会先把代码转换成中间层的二进制映像,这样才能或得到令人满意的执行速度。在彻底完成这一步骤之前,这些二进制代码对全局并无影响。如果源码处理阶段出错,整个有问题的代码块都会被弃用;然后解析器会继续处理下一段代码块。 如下示例:
<script type="text/javascript"> console.log(Benjamin); console.log("Benjamin module01"); </script> <script type="text/javascript"> console.log("Benjamin module02"); </script>
打印信息:
由上结果,代码块一报错,整个有问题的代码块被弃用,解析器继续处理下一段代码块。
2)函数解析
源码处理之后,解析器对当前代码块里所有具名的全局函数进行识别并注册。在这一阶段完成之后,这些函数才能被执行代码所调用。看下面示例2.1
<script type="text/javascript"> getMessage(); function getMessage(){ console.log("Hello Benjamin!"); } </script>
输出Hello Benjamin!,更改成如下表达式声明的代码:
<script type="text/javascript"> getMessage(); var getMessage = function(){ console.log("Hello Benjamin!"); } </script>
会报错,TypeError: getMessage is not a function,因为这样的声明的等价于下面的代码,声明提前了,看示例2.2
<script type="text/javascript"> var getMessage = null; getMessage(); getMessage = function(){ console.log("Hello Benjamin!"); } </script>
如果放到两个代码块中会怎样呢?看示例2.3
<script type="text/javascript"> getMessage(); </script> <script type="text/javascript"> var getMessage = function(){ console.log("Hello Benjamin!"); } </script>
因为javascript处理代码块的属性是由javascript引擎读取代码块的先后顺序决定的。当调用的getMessage()函数的时候 getMessage方法还么哟定义呢。javascript这种全局名称解析模型只针对于函数有效,而对于变量的声明却并非如此。变量是按照执行的时候出现的顺序注册的。
3)代码执行
函数解析完后,javascript引擎就会开始顺序执行在函数区块之外的所有代码。如果在执行过程中,当前代码块产生某些异常,脚本的执行可能会失败。其他代码块中的代码扔能正常执行,具体参见源码处理中的示例。