AO 和函数紧密相关
函数其实也是一种对象类型/引用类型/引用值,有自己的属性,比如 test.name / test.length / test.prototype
但是对象有些属性是我们无法访问的,就是JS引擎内部固有的隐式属性(私有属性),[[scope]]域 就是其中之一。
[[scope]]
-
是函数创建时,生成的一个JS内部的隐式属性,只能由JS引擎来读取
-
是函数存储作用域链的容器,作用域链存储的就是AO/GO
-
AO(函数的执行期上下文),函数执行完成以后,AO是要销毁的(每一次执行函数时,会生成新的AO),也就是说AO是一个即时的存储容器。
-
GO(全局的执行期上下文)。
-
作用域链就是将AO/GO排列起来,形成一个链式关系
实例说明
function a(){
function b(){
var b = 2;
}
var a = 1;
b();
}
var c = 3;
a();
当a函数被定义时
function a(){}
var c = 3;
系统生成[[scope]]属性
[[scope]]保存该函数的作用域链
| 函数a function a(){} | |
|---|---|
| [[scope]] | scope chain 作用域链 |
该作用域链的第0位存储当前环境下的全局执行期上下文GO
| scope chain 作用域链 | |
|---|---|
| 0 | GO全局执行期上下文 |
GO里存储全局下的所有对象
| GO | |
|---|---|
| this | window |
| window | (object) |
| document | (object) |
| a | (function) |
| c | 3 |
当a函数被执行时(前一刻)-- 预编译
function a(){
function b(){} //b函数还没有执行,里面不看
var a = 1;
}
var c = 3;
a();
作用域链的顶端(第0位)存储a函数生成的函数执行期上下文AO,同时第1位存储GO
查找变量是0往下找
[[scope]]-> scope chain保存该函数的作用域链
| 函数a function a(){} | |
|---|---|
| [[scope]] | scope chain |
| scope chain 作用域链 | |
|---|---|
| 0 | AO |
| 1 | GO |
0 -> AO
| AO | |
|---|---|
| this | window |
| arguments | []无参数 |
| a | 1 |
| b | (function) |
1 -> GO
| GO | |
|---|---|
| this | window |
| window | (object) |
| document | (object) |
| a | (function) |
| c | 3 |
当b函数被定义时
在a函数(上级)环境下,所以 b函数被定义时的作用域链 = a函数被执行时的作用域链
因为b还没有被执行,所以看到的东西和上级执行完看到的东西一样
当b函数被执行时
function a(){
function b(){
var b = 2;
}
var a = 1;
b();
}
var c = 3;
a();
生成函数b的[[scope]],存储函数b的作用域链
| 函数b function b(){} | |
|---|---|
| [[scope]] | scope chain |
顶端第0位存储b函数的AO
a函数的AO和全局的GO依次向下排列
| scope chain 作用域链 | |
|---|---|
| 0 | 函数b的AO |
| 1 | 函数a的AO |
| 2 | 全局的GO |
0 -> 函数b的AO
| 函数b的AO | |
|---|---|
| this | window |
| arguments | []无参数 |
| b | 2 |
a函数的AO和全局的GO不变(引用)
当b函数被执行结束时
b函数的AO被销毁,回到被定义的状态
当a函数被执行结束时
a函数的AO被销毁的同时
b函数的[[scope]]也不复存在(因为b函数存储在a函数的AO里,整个函数都不存在了)
a函数回归被定义时的状态
总结:
全局函数被定义时,也就是全局在执行时,生成作用域和作用域链(因为每个函数都必须要有GO)
函数被执行时那一刻,生成自己的AO,同时内部函数被定义(拿上级的环境作为基础)
被销毁的时候,AO销毁
上层AO销毁时,该函数的[[scope]]也没了
每次执行时,都会生成新的AO
例:
function a(){
function b(){
function c(){}
c();
}
b();
}
a();
a定义:a.[[scope]] -> 0: GO
a执行:a.[[scope]] -> 0: a -> AO
1: GO
b定义:b.[[scope]] -> 0: a -> AO
1: GO
b执行:b.[[scope]] -> 0: b -> AO
1: a -> AO
2: GO
c定义:c.[[scope]] -> 0: b -> AO
1: a -> AO
2: GO
c执行:c.[[scope]] -> 0: c -> AO
1: b -> AO
2: a -> AO
3: GO
c结束:c.[[scope]] -> 0: b -> AO
1: a -> AO
2: GO
b结束:b.[[scope]] -> 0: a -> AO
1: GO
c.[[scope]] ×
a结束:a.[[scope]] -> 0: GO
b.[[scope]] ×
所以外部访问不到内部的变量的原因就是,外部的作用域链,没有内部函数的AO
函数不执行的话,不会看函数里面的值,不会生成AO,相当于只存了个名号