我们都知道JS作为一种单线程解释性语言,它有它自己的执行机制。
JS代码执行过程分三个过程:
- 首先,进行语法分析。
- JS预编译。
- 最后解释性执行****。
那预编译中的作用域是什么意思呢?
作用域
- [[scope]]: 每个javascript的函数都是一个对象,对象中有些属性是我们可以访问的,但有些不可以,[[scope]]这个属性就是其中之一,它只能被javascript引擎存取.
function a(){
console.log(a.name);
console.log(a.prototype);
}
如函数a中 a.name a.prototype 都是可以访问到的,叫做显示属性
而a.[[scope]] 访问不到的属性,叫做 隐式属性
- [[scope]] 指的是我们说的作用域,其中储存了运行期的上下文集合
那什么是运行期的上下文集合,作用域又是如何产生的呢?
这里需要先解释俩个概念——AO、GO对象。
AO对象:Activation Object,指活动性对象,也叫执行期上下文,就是我们通常所说的作用域。这里指函数的局部作用域。
GO对象:Gobel Object,是全局对象,GO对象跟window对象是同一个对象。可以理解为window对象有两个名字 window == GO。
运行期的上下文
当函数执行时,会创建一个称为执行期上下文的内部对象,一个执行期上下文定义了一个函数执行时的环境 函数每次执行时对应的执行上下文都是独一无二的,所以多次调用函数会创建多个上下文,当函数执行完毕,它所产生的上下文会被销毁
看一个例子:
function a(){ //函数声明
function b(){
var bb = 234
a = 0
}
var a = 123
b()
console.log(a); //0 为什么打印的是函数b里面的a?
}
var glob = 100
a() // 函数调用
函数声明->[[scope]]->scope chain ->{GO,AO}
1. 函数a分析:全局作用域下有一个a函数,有一个glob变量,a函数的调用。
- 在a函数声明时,会在作用域链中产生一个GO对象,对象中存在一些全局下的属性和变量。
- 在a函数执行时,会产生一个AO对象,并去到作用域链的顶部。
a defined a.[[scope]] -->0:GO{} //GO 全局作用域
a doing a.[[scope]] -->0:AO{} -->1:GO{} //函数自己的作用域
2. 函数b分析:1.函数a的执行带来了函数b的声明,函数b所在的是a的作用域中。 2. 函数b在执行的时候,会创建一个执行期上下文的内部对象AO
下图一是函数b刚被定义的时候,图二是函数b执行的时候,函数b的AO会来到作用域链的顶端。
结果:: 因为在查找a的时候,遵从作用域的查找变量,从作用域的顶端依次向下查找。 b函数的执行的时候作用于链中的AO{}和a函数执行时作用于链中的AO{}是同一个。,因为函数外部不能访问函数内部的变量,所以只有b函数执行完后,将a=0,同时b函数作用域链中的AO的a=0,函数a作用域链中的a也等于0,所以才能访问到。
作用域链(scope chain)
[[scope]]中存储的运行期的上下文集的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链
- 查找变量:从作用域的顶端依次向下查找
最后看一个例子:
function a() {
function b(){
function c(){
}
}
}
a()
分析其作用域:
-
a defined a.[[scope]] --> 0:GO{} a doing a.[[scope]] --> 0:aAO{} --> 1:GO{}
-
b defined b.[[scope]] --> 0:aAO{} --> 1:GO{} b doing b.[[scope]] --> 0:bAO{} --> 1:aAO{} --> 2:GO{}
-
c defined c.[[scope]] --> 0:bAO{} --> 1:aAO{} --> 2:GO{} c doing c.[[scope]] --> 0:cAO{} --> 1:bAO{} --> 2:aAO{} --> 3:GO{}