一、作用域是什么?
储存和访问变量的值的能力将状态带给了程序。但是变量储存再哪里,当程序需要时如何找到它们?
一)、编译原理:
JS通常被称作 动态、解释型语言,但它实际上是 编译型语言。但与传统的编译语言不同,它不是提前编译的,编译结果也不能再分布式系统中进行移植。
传统编译语言的编译会经历三个步骤:
-
分词/词法分析(Tokenizing/Lexing):
将由字符组成的字符串分解成有意义的(对编程语言来说)代码块(词法单元,token)。
例: var a = 2;
会被分解为:var、a、=、2、;
- 分词和词法分析之间是有区别的。
-
解析/语法分析(Parsing):
将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树——抽象语法树(Abstract Syntax Tree,AST)。
-
代码生成:
将前面生成的AST转换成一组机器指令。
JavaScript的编译过程要比上面所说的过程复杂许多。
简单地说,任何JavaScript代码片段在执行前都要进行编译(通常就在执行前)。因此,JavaScript编译器首先会对var a = 2;这段程序进行编译,然后做好执行它的准备,并且通常马上就会执行它。
二)、理解作用域:
概念介绍:
-
引擎:
从头到尾负责整个JavaScript程序的编译及执行过程。
-
编译器:
引擎的好朋友之一,负责语法分析及代码生成等脏活累活。
-
作用域:
引擎的另一位好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
编译过程中编译器会不断询问作用域内是否有所需变量!
引擎的查找方式:
LHS:进行赋值,例:a = 2;
RHS:取值,例:console.log(a); b = a; // 将a的值赋给b
三)、作用域嵌套:
在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(即全局作用域)为止。
例:
function foo(a) {
console.log(a + b);
}
var b = 2;
foo(2);
在函数foo内对b进行RHS查询,当在foo作用域内无法完成时。会到上一层作用域寻找到b。
四)、异常:
要学会区分LHS与RHS。
-
ReferenceError:
RHS或严格模式下的LHS查询失败。
-
TypeError:
对RHS查询到的变量进行不当操作。
-
非严格模式下,LHS查询失败(即全局作用域也无法找到该变量)时,会自动创建一个该名称的全局变量,并返还给引擎。