一 回顾:
上文中 我们可以明白简单的函数作用域、全局作用域中的变量指向和具体值。
- 函数创建时,会生成一个 JS 内部的隐式属性 —— [[scope]] 作用域
- [[scope]] 是函数存储作用域的容器,作用域包含:AO、GO
- 函数的作用域 一层一层串联起来,最后串到底部全局作用域,组合成了作用域链 -- Scope chain
- 函数在定义时的作用域和当前函数外一层函数执行时的作用域一致
- 函数执行时,会生成自己的AO并放在 Scope chain 的顶端,且在执行结束后,会销毁当前AO;如果再次执行,则会重新生成一个新的AO
- 销毁时:外部部函数结束,内部函数的 GO 也会被销毁
二:AO 的创建 --- > 销毁
接下来我们还是根据案例来分析,函数的作用域的生命周期(创建AO --- > 销毁 AO的过程)
案例一:
function a() {
var a = 1
function b() {
var b = 2
function c() {
var c = 3
console.log(c, b, a) // 3 2 1
}
c()
}
b()
}
a()
var str = 'Hello World'
-
执行 c 函数时的作用域、作用域链,如下图:
-
c 函数执行结束,其 AO 立即被销毁:
-
b 函数执行结束,其 AO 立即被销毁, c 函数断开与 GO 的链接
-
a 函数执行结束,其 AO 立即被销毁,b 函数断开与 GO 的链接, 最后剩下 GO 保存在内存中
总结如下:
创建 到 销毁的过程
a 定义: a.[[scope]] --> 0: GO
a 执行: a.[[scope]] --> 0: AO -- a()
--> 1: GO
b 定义: b.[[scope]] --> 0: AO -- a()
--> 1: GO
b 执行: b.[[scope]] --> 0: AO -- b()
--> 1: AO -- a()
--> 2: GO
c 定义: c.[[scope]] --> 0: AO -- b()
--> 1: AO -- a()
--> 2: GO
c 执行: c.[[scope]] --> 0: AO -- c()
--> 1: AO -- b()
--> 2: AO -- a()
--> 3: GO
c 结束: c.[[scope]] --> 0: AO -- b()
--> 1: AO -- a()
--> 2: GO
b 结束: b.[[scope]] --> 0: AO -- a()
1: GO
b.[[scope]] 不存在 --- c 的 GO 销毁,没有 b-AO 这一层的关联,直接与 GO 断开了链接
a 结束 a.[[scope]] --> 0: GO
a.[[scope]] 不存在 --- b 的 GO 销毁, 没有 a-AO 这一层的关联,直接与 GO 断开了链接
因此在整个执行中,GO 会一直保存在内存中,不会被销毁,但是 AO 一般都会被销毁(闭包除外)
tip:
- 外层函数的 AO 销毁时,内层函数的 GO 随之销毁。作用域链在外层函数 AO 这一环节直接断掉了 (闭包除外-- 下一章我们会讲到)
- 全局执行上下文 GO 会一直保存在内存中