作用域链按函数定义时的词法嵌套关系静态形成,从当前ao开始,依次向上链接外层词法环境直至全局;查找变量时严格遵循此链,不依赖调用位置;var可访问未赋值状态,let/const在tdz内报错;闭包通过保留对定义时作用域链的引用实现变量持久化。

函数执行上下文嵌套时,变量查找不是看谁调用了它,而是看它在代码里写在哪一层——也就是词法位置。作用域链就是按这个静态嵌套关系形成的查找路径,从当前函数开始,逐级向上直到全局。
作用域链怎么形成
每个函数在定义时就确定了自己的作用域链起点。引擎会把函数声明时所处的**外层词法环境**保存进内部属性 [[Scope]](ES6 后更准确叫“词法环境链”)。这个链是只读的,不会因为函数被哪里调用而改变。
- 全局函数的 [[Scope]] = [全局变量对象]
- 在
outer函数内定义的inner,其 [[Scope]] = [outer 的 AO, 全局 VO] - 函数真正执行时,会把当前的活动对象(AO)加到该链最前面,构成完整作用域链:Scope = [AO].concat([[Scope]])
嵌套执行时的查找过程
当一个嵌套函数运行并访问某个变量时,引擎会严格按作用域链顺序查找:
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
- 先查当前函数的 AO(比如参数、
let/const/var声明) - 没找到,就查上一级函数的 AO(注意:是定义时的父级,不是调用时的父级)
- 继续向上,直到全局 VO;全程不看调用栈,只看书写嵌套结构
例如:inner 在 outer 内定义,即使它被从另一个函数里调用,它的作用域链仍是 [inner AO, outer AO, global VO],不是调用者的上下文。
var、let/const 在查找中的差异
它们都走同一条作用域链,但初始化时机不同,直接影响能否访问:
-
var声明会被提升并初始化为undefined,所以查找时总能拿到值(哪怕还没赋值) -
let/const存在暂时性死区(TDZ):从块头部到声明语句之间,变量存在但不可访问;此时沿作用域链查到它,也会抛ReferenceError
闭包与作用域链的关系
闭包的本质,就是内部函数保留了对定义时作用域链的引用。即使外部函数执行结束、AO 理论上该销毁,只要内部函数还活着,引擎就会维持这条链上的变量对象不被回收。
- 返回一个内部函数,或把它传给定时器、事件监听器,都可能形成闭包
- 闭包捕获的是变量对象的引用,不是值的快照;所以多次调用可能共享同一变量










