前言
几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在对这个值进行访问和修改。事实上,正是这种储存和访问变量的值得能力将状态带给了程序。
若没有了程序这个概念,程序虽然也能够执行一些简单的任务,但它会受到高度限制,做不到非常有趣。
但是这就产生了几个问题:
- 1、这些变量住在哪里?
- 2、程序需要时如何找到它们?
在JavaScript解析的过程中,有三个重要的角色:引擎、编译器、作用域。
- 引擎主要负责的是JavaScript的编译和执行。
- 编译器主要负责的是语法分析及代码生成
- 作用域则是维护所有的标识符,并对其进行一系列的查询,且实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
引擎、编译器和作用域是如何协同工作的呢?例如我们将var a = 2分解来看。
- 1、例如声明var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a。
- 2、接下来编译器会为引擎生成运行时所需要的代码,这些代码用来处理a = 2这个赋值操作。引擎运行时会首先询问作用域,在当前作用域集合中是否存在一个叫做a的变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量。
总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有生命过),然后在运行时引擎会在作用域中查找该变量,如果能找到就会对它赋值。
上面我们说到了引擎会查找变量,那么是如何查找的呢?
- LHS:变量出现在赋值操作的左侧时进行了LHS查询,可以理解为“给XXX赋值”
- RHS: 变量出现在赋值操作的右侧。RHS查询与简单地查找某个变量的值别无二致,也可以理解为“找XXX的值”
var a = 10; //这里引擎对a的查询是LHS
var b = a; //这里引擎对a的查询是RHS,对b的查询是LHS
可以看看下面这个练习,看看自己理解程度。
function foo(a){
var b = a;
return a + b;
}
var c = foo(2);
1、找到其中LHS查询(有3处)
2、找到其中RHS查询(有4处)
作用域嵌套(作用域链是什么?)
作用域嵌套,当一个块或者函数嵌套在另一个块或者函数中时,就发生了作用域嵌套。因此,当在当前作用域中无法找到某个变量的时候,引擎就会在外层嵌套的作用域中继续查找,直到查找到该变量或抵达最外层的作用域为止。
考虑以下代码:
function foo(a){
console.log(a +b)
}
var b= 2
foo(2)
对b进行RHS引用,在当前作用域没有找到,但可以在上一级作用域中找到。
image.png这个建筑代表程序中的嵌套作用域。第一层代表当前的执行作用域,建筑的顶层代表全局作用域。
LHS和RHS查找都会在当前楼层中进行查找,如果没有找到,就继续往上找,一旦抵达顶层,查找过程都会停止。
不成功的RHS引用会导致抛出ReferenceError异常。不成功的LHS引用会导致自动隐式的创建一个全局变量(非严格模式下),严格模式下会抛出同样的异常,
网友评论