JS - 作用域、作用域链 - 2

181 阅读2分钟

一 回顾:

上文中 我们可以明白简单的函数作用域、全局作用域中的变量指向和具体值。

  • 函数创建时,会生成一个 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 会一直保存在内存中