JavaScript引擎、编译器、作用域

2019-10-12  本文已影响0人  viviChen

JavaScript理论

编译器理论

传统的编译器一般包括下面的三种基础步骤:

(1)分词、词法分析;

(2)解析;

(3)代码生成。

我们以 var a = 2;来举例,

(1)首先,编译器将这一句代码分为几个块儿,可能是vara=2;;具体是否将一个词(比如var、空格等)进行切割,是通过词法分析来判断的;

(2)然后编译器需要将代码解析为一棵抽象语法树;

  var
  |-----------|
  a           =
              |
              2

(3)最后编译器将抽象语法树翻译为计算机可以读懂的代码,引擎运行该代码执行程序。

这三个步骤是最基础的,在编译时还会存在优化执行效率等步骤的存在。JavaScript相对于其他语言的编译器更特殊的是,他不存在预编译的过程,JavaScript代码在运行时,先通过几微妙或者更少的时间进行编译,随后代码就立马执行了。

JavaScript的编译器、引擎和作用域之间的关系

想深入了解JavaScript,我们就需要了解JavaScript里面的三个机制 —— 引擎、编译器和作用域;他们三个协同工作从而能使我们的代码正常的运行。首先我们简单了解一下他们的分工:

(1)引擎:负责从始至终的编译和执行我们的JavaScript程序;

(2)编译器:负责解析和生成可执行的JavaScript代码;

(3)作用域:负责管理所有已声明的标识符列表,对当前代码如何访问变量约束了严格的规则。

同样是这段代码var a = 2;,我们使用这三个机制分析:

(1)我们首先可以知道的是JavaScript引擎会看到两段代码,一段是编译器在编译期间处理的,另一段是在执行期处理的代码。

(2)接着说一下编译器在此期间的操作:

(2.1)编译器拿到程序会首先根据词法分析进行分词【分词阶段】,然后再将这些拆分的token生成相应的树【解析阶段】;

(2.2)当编译器遇到var a时,编译器会让作用域在特定的集合中查看是否存在变量a的声明,如果有的话就略过,没有的话就声明a;然后在生成执行期的代码。【代码生成阶段】

(3)最后就是引擎拿着编译器生成的代码开始往后执行a = 2;,引擎会先让作用域在查找当前作用域是否存在a这个变量,如果有的话就继续赋值操作,如果没有的话就到外层查找,最终在全局作用域中也找不到的话就抛出一个错误。


另外的【LHS & RHS】:

当引擎在执行编译器产生的可执行代码时,还有一个很重要的知识点 —— LHS(left-hand-side) 和 RHS(right-hand-side) 查询,简单来讲(举例)

var a = 2;
console.log(a);

① 中,对变量a进行赋值操作,则属于LHS引用;
② 中,需要获取变量a的值,属于RHS引用。

我们以下面的代码进一步解释:

function foo(a) {
    var b = a;
    return a + b;
}

var c = foo( 2 );

下面是引擎、编译器和作用域工作的分析:

(1)编译器进行分析、解析;

(2)在生成代码阶段 ,编译器让作用域创建了全局变量c,function foo,在function foo的作用域内创建了局部变量b和a;

(3)在执行代码阶段,引擎让作用域查看是否存在变量c,foo,并执行了方法foo,最后将foo的返回值赋值给变量c。

下面是关于LHS和RHS的分析:

(1)var c = ...是对c的赋值操作,属于LHS引用(1L);赋值的右边是foo(...),需要RHS引用,找到foo(1R);

(2)在foo(2)执行时,有一个隐含的LHS引用,将2赋值给参数a(2L);

(3)var b = ...属于LHS引用(3L);对于赋值操作右边的a,需要获取a的值,用的是RHS引用(2R);

(4)return a + b这里需要查询a(3R)和b(4R)的值,用了两次RHS查询;

总结

  1. 作用域是一组规则,他规定了一个变量(标识符)在哪里和如何被查找。这种查询也许是为了向这个变量赋值,这时变量是一个LHS引用;也许是为了获取这个变量的值,这时变量是一个RHS引用;

  2. LHS引用是自赋值操作,可以通过=赋值,也可以通过函数参数传递赋值;

  3. LHS和RHS引用查询都是从当前作用域开始,逐步向外层查找,直到在全局作用域中停止,未被满足的RHS查找会抛出一个ReferenceError引用错误;在非严格模式下,未被满足的LHS查找,会在全局作用域中创建一个变量,在严格模式下不可以在全局中创建变量,同样会抛出ReferenceError的引用错误;

  4. 当RHS查询的值被错误的操作,会抛出TypeError的类型错误。

  5. JavaScript引擎在代码运行之前进行编译,因此,他将var a = 2;进行分离的操作;(1)代码执行前,在当前作用于声明a(2)代码执行时,进行LHS引用赋值。

上一篇下一篇

猜你喜欢

热点阅读