JS 是动态语言,任何一段代码在执行以前都须要编译,它跟传统的语言不一样,它不是提早编译的,编译结果也不能在分布式系统中进行移植。
可是JS引擎进行编译的步骤和传统的编译语言很是类似,在某些环节可能比预想的要复杂。编程
分词/词法分析(Tokenizing/Lexing)
这个过程会将由字符串组成的字符串分解成(对编程语言来讲)有意义的代码块,这些代码块被称为词法单元(token)数组
e.g. var a = 2;
一般会被解析成var 、a、=、二、;
空格是否被当作此法单元,取决于空格在这门语言中是否具备意义编程语言
解析/语法分析(Parsing)
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的表明了程序语法结构的树(抽象语法树,Abstract Syntax Tree, AST)分布式
var a = 2;函数
VariableDeclaration | |------ a |------AssignmentExpression |-----2
代码生成
将AST转换成可执行代码的过程。this
var a = 2;
经过特定方法将var = 2;的AST转化为一组机器指令,用来建立一个叫做a的变量(包括内存分配等),并将一个值存储到a中。code
JS 的编译步骤和传统仍是很是类似的,只是某些环节比较复杂,这里我详细说一下“预编译”,其余三个步骤同传统的编译对象
分词/词法分析(Tokenizing/Lexing)token
解析/语法分析(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 () {}这个是一个赋值语句