15

JS 是动态语言,任何一段代码在执行之前都需要编译,它跟传统的语言不同,它不是提前编译的,编译结果也不能在分布式系统中进行移植。
但是JS引擎进行编译的步骤和传统的编译语言非常相似,在某些环节可能比预想的要复杂。

传统的编译

  • 分词/词法分析(Tokenizing/Lexing)
    这个过程会将由字符串组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元(token)

e.g. var a = 2;
通常会被解析成var 、a、=、2、;
空格是否被当做此法单元,取决于空格在这门语言中是否具有意义

  • 解析/语法分析(Parsing)
    这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树(抽象语法树,Abstract Syntax Tree, AST)

var a = 2;

VariableDeclaration
|
|------ a
|------AssignmentExpression
    |-----2
  • 代码生成
    将AST转换成可执行代码的过程。

var a = 2;
通过特定方法将var = 2;的AST转化为一组机器指令,用来创建一个叫作a的变量(包括内存分配等),并将一个值存储到a中。

JS 编译

JS 的编译步骤和传统还是非常相似的,只是某些环节比较复杂,这里我详细说一下“预编译”,其他三个步骤同传统的编译

  • 分词/词法分析(Tokenizing/Lexing)

  • 解析/语法分析(Parsing)

  • 预编译
    首先先看一个例子:

function a(b) {    
      alert(b); 
    function b() {            
        alert(b);       
    }        
    b();    
}    
a(1);

答案先不说, 现在看具体的预编译过程:

  1. 预编译--全局
    1). 创建Global Object对象(GO)

2). 查找变量声明

-> 如果GO上还没有该属性,则添加该属性,值为undefined  
-> 如果GO上已经有该属性,则不做任何处理 

3). 查找函数声明(eg. function foo () {})

-> 如果GO上还没有foo属性,则把函数赋值给foo属性  
 -> 如果GO上已经存在foo属性,则直接覆盖
 
  1. 预编译--函数
    1). 函数运行前的一瞬间,生成Activation Object(活动对象),简称AO

2). 分析参数

 -> 把声明的参数形成AO的属性,值全为undefined  
 -> 接收实参,形成AO相应属性的值  

3). 分析变量声明

 -> 如果AO上还没有该属性,则添加该属性,值为undefined  
 -> 如果AO上已经有该属性,则不做任何处理  

4). 分析函数声明(eg. function foo () {})

 -> 如果AO上还没有foo属性,则把函数赋值给foo属性  
 -> 如果AO上已经存在foo属性,则直接覆盖
  • 代码生成
    JS执行过程简单的介绍完了,Do you get it?, 下面看之前例子分析:

function a(b) {    
      alert(b); 
    function b() {            
        alert(b);       
    }        
    b();    
}    
a(1);


// 分析如下
/*
 * 1. 创建GO对象(包含JS全局对象的内置对象Math、String、Date、etc)
 * 2. 查找变量声明,没有
 * 3. 查找函数声明,定义函数a, GO = {a: function () {}}
 * 4. 执行a(1)
 * // 以下为函数a运行前的编译
 * 5. 创建活动对象AO  AO={this, arguments}
 * 6. 分析形参 AO = {this, arguments, b: undefined}
 * 7. 接收实参 AO = {this, arguments, b: 1}
 * 8. 分析变量声明 AO = {this, arguments, b: 1}
 * 9. 分析函数声明 
     AO = {
         this
         argunments,
         b: function () {}
     }
 
 * // 执行
 * alert(b)  // function () { ... }
 * b()   // function () { ... }
 */

从以上分析很清晰就能够知道弹出两个function,是不是很简单啊。其实在执行b(),还有函数b也要编译哦,编译步骤同函数a,这里就不做分析了。
习题:

function a(b) {    
      alert(b); 
    b = function() {            
        alert(b);       
    }        
    b();    
}    
a(1);

自己试着分析一下,结果是1和function,你做对了么?难点:b = function () {}这个是一个赋值语句


Rhinoceros
180 声望12 粉丝

以终为始