编译原理
js中的源代码在执行之前会经历三个步骤,统称为编译:分词/词法分析
这个过程会将由字符组成的字符串分解成有意义的代码块,这些代码块被称为词法单元(token)1
2// 例如
var a = 1;以上的代码会被分解为:
var
,a
,=
,2
,;
如果在某些语言中的空格是有意义的,那么空格也会被编译.- 解析/语法分析
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的书。这个树被称为”抽象语法树”(AST)。如上面的例子中,在抽象语法树中可能会有一个叫做VariableDeclaration
的顶级节点,该节点下面是一个叫做Identifier
的子节点(a),和一个叫做AssignmentExpression
的子节点。AssignmentExpression
节点有一个叫做NumericLiteral
(2)的子节点。 - 代码生成
经过第二步的解析/语法分析之后,AST转换为可执行代码(机器指令)的过程被称为代码生成。该过程和语言、平台都存在关系。
作用域
在理解作用域之前,先了解以下两个概念
引擎:从头到尾负责整个js程序的编译及执行过程
编译器:负责语法分析及代码生成等
在了解了上面的两个概念之后,我们来看作用域:
作用域:负责收集并维护由所有声明的标识符(变量)
组成的一系列查询
,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问呢权限
。也就是说作用域是根据名称查找变量的一套规则。作用域嵌套
当一个块或函数嵌套在另一个块或者函数中时,就会发生作用域嵌套,当在当前的作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或者抵达最外层的作用域为止(全局作用域)词法作用域
词法作用域就是定义在词法阶段的作用域,也就是说,词法作用域是由你写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变。1
2
3
4
5
6
7
8function foo(a){ // (2)
var b = a * 2
function bar(c){
console.log(a,b,c) //(3)
}
bar(b * 3)
}
foo (2); // 2,4,12; (1)在上面的例子中出现了三个嵌套的作用域(1),(2),(3)
变量声明提升
js引擎会在解释js代码之前首先对其进行编译,而编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将他们关联起来,因此,包括变量
和函数
在内的所有声明都会在任何代码被执行前首先被处理
.1
var a = 2;
所以当在js中写入上面的代码时,会经历两个步骤:
- 定义声明
var a
;是在编译阶段完成 a = 2
;赋值会在原地等待执行阶段完成
也就是说首先会进行编译,然后才是执行.
- 定义声明
函数优先
函数声明和变量声明都会被提升。但是函数首先会被提升,然后才是变量.函数声明会被提升到普通变量之前。
作用域
Last updated: