1.2 理解作用域
Js是一门编译语言,与传统的语言不同之处,js的编译发生在执行代码的前几微秒,因此js引擎(简单理解引擎会根据需要创建并存储变量)会采用各种办法来保证性能(JIT)。
传统编译语言的流程中,一段源代码在执行前会经历三个步骤,也就是编译: 分词/词法分析 -> 解析/语法分析 -> 代码生成。就生成了可执行的代码。
作用域和引擎,编译器,作用域都会有关系。
引擎: 创建存储变量,参与js程序的编译和执行过程
编译器:词法语法分析并且生成可执行代码
作用域:负责收集所有声明的标识符,并且按照一定的规则,控制这些标识符的访问权限。
ex: var a = 3; 分解
1:编译器 -> 作用域 问:作用域啊,我们现在有没有这个变量2的声明啊?有的话我就懒得再编译了。
A:有,编译器会忽略该声明
B:没有:编译器会要求作用域在【当前】作用域合集中声明一个新的变量 a
2:编译器生成 a = 2 赋值操作的可执行代码。
3:引擎运行时: 引擎 -> 作用域:我们现在个环境下,有没有a变量啊?
A: 有,拿来去用
B:没有: 继续寻找该变量(作用域嵌套)
总结:变量的赋值操作会执行两个动作,1:编译器在当前作用域中声明一个变量,2:运行时引擎会在作用域中查找该变量,如果能找到就会对a做赋值。
var a = 2 中,引擎会为a进行LHS查询, 还有一个查询叫做RHS查询。
当变量出现在赋值操作的左侧是进行LHS查询, 出现在右侧时进行RHS查询。
RHS(retrieve his source value)是非左侧 查询。ex:console.log(a)就是RHS查询。赋值操作还有其他几种形式, 因此在概念上最好理解为:“赋值操作的目标是谁LHS”, “谁是赋值操作的源头RHS”
练习:理解这段代码的执行过程
function foo(a) {
console.log(a);
}
foo(2)
引擎 -> 作用域:我需要为foo做RHS引用,当前作用域有吗?
作用域 -> 引擎 :有,编译器声明了它,给你地址。
引擎 -> 作用域:我找到foo了,我要执行foo,这里2需要赋值给形参
A,进行LHS引用,有a的声明吗?
作用域 -> 引擎 :有,编译器把它声明为foo的形参了,给你。
引擎 -> 作用域:我要执行console,进行RHS查询。你见过它吗?
作用域 -> 引擎 :有,给你。console是内置对象。
引擎 -> 作用域:console 有log的函数,ok。
引擎 -> 作用域:作用域,我要对A进行RHS引用,可以访问a 么?
作用域 -> 引擎 :有,给你。
引擎 :把2传给a,传递给log()。
自己总结: 作用域核心是控制变量访问权限。执行过程中存在funA有变量a并且funB有变量a的情况,所以需要有规则控制访问权限。js处理难处是作用域嵌套比如面试必考的闭包问题。
还有一点,理解js的特点,不像java是需要打包的,js是在执行前才通过编译器编译成可执行代码,有时候甚至执行结束后才进行二次编译执行。所以才会出现以前前端的问题js引入的顺序可能影响执行的结果。
这个小结可以从编译器和引擎的理解下更好的理解作用域。