1、什么是作用域?
先想两个问题,变量存储在哪里?当程序需要的时候是如何找到它们的?
这些问题说明需要一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。而这套设计良好的规则就是作用域。
继续深入理解:这些作用域规则是在哪里?怎样设置的?
请继续阅读
2、编译原理
尽管通常将js归类为“动态”或者“解释执行”语言,但事实上它是一门编译语言。
但是与传统的而编译语言不同的是,它不是提前执行的,编译结果也不能在分布式系统中进行移植。
尽管如此,JS引擎进行编译的步骤和传统的编译语言非常相似,在某些环节可能比预想的要复杂。
下面看看传统编译语言的流程,程序源码在执行之前需要会经历的三个步骤,统称为“编译”。
1> 分词/词法分析(Tokenizing / Lexing)
这个过程会将由字符组成的字符串分解成有意义的代码块,而这些代码块被称为词法单元(Token).
例如:程序var a = 2;
。这段程序通常会被分界为下面这些词法单元:var
、a
、=
、2
、;
。(空格是否是词法单元,取决于空格在这门语言中是否有意义)
2> 解析/语法分析
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树,这棵树被称为“抽象语法树”(Abstract Syntax Tree, AST)
例如: var a = 2;的抽象语法树种可能有一个顶级节点VariableDeclaration, 接下来是一个叫作Idnetifier(它的值是2)的子节点,以及一个叫作AssignmentExpression的子节点。AssignmentExpression节点有一个叫作Numericliteral(它的值是2)的子节点。
3> 生成代码
将抽象语法树转换成可执行代码的过程称之为代码生成。(这个过程与语言、目标平台的息息相关)
抛开具体细节,简单点来说的话就是存在某种方法可以将var a = 2; 的AST转化为一组机器指令,用来创建一个叫作a的变量(包括分配内存等),并将一个值储存在a中。
Tip: 引擎可以根据需要创建并储存变量。
和传统编译语言的三个编译步骤相比,JavaScript引擎要复杂的多。比如:在词法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对购余元素进行优化等。
对于JavaScript来说, 它的编译过程不是发生在构建之前,通常编译发生在代码执行前的几微妙(甚至更短!)的时间内。
源代码 ——》 编译 ——》做好执行前的准备 (通常是马上就会执行)
3、总结
引擎:从头到尾负责整个JavaScript程序的编译和执行过程。
编译器:引擎的好朋友之一,负责语法分析及代码生成等脏活累活。
作用域:引擎的另一个号朋友,负责收集并维护由所标示符组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标示符的访问权限。
举例来说,对于变量赋值操作,会执行两个动作,首先编译器会在当前的作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎在作用域中查找该变量,如果能够找到就会对它赋值。
作用域是一套规则,用于确定在何处以及如何查找变量(标示符)。如果查找的目标是对变量进行赋值,那么就会使用LHS查询;如果目标是获取一个变量的值,就会使用RHS查询。