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). 创建Global Object对象(GO)
2). 查找变量声明
-> 如果GO上还没有该属性,则添加该属性,值为undefined
-> 如果GO上已经有该属性,则不做任何处理
3). 查找函数声明(eg. function foo () {})
-> 如果GO上还没有foo属性,则把函数赋值给foo属性
-> 如果GO上已经存在foo属性,则直接覆盖
预编译--函数
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 () {}这个是一个赋值语句
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。