块级作用域
先讲一下块级作用域在执行上下文的角度是怎么呈现的。
执行上下文包括变量环境和词法环境。
函数内部通过var声明的变量,在编译阶段全部存放在“变量环境”
通过let和const声明的变量,在编译阶段会存放在“词法环境”
词法环境内部维护了一个栈结构,当一个块级作用域的代码执行完毕,则栈顶的词法信息被弹出
函数只会在第一次执行的时候被编译,所以编译时变量环境和顶层的词法环境已经确定
当执行到块级作用域的时候,块级作用域通过let和const声明的变量会被创建被在词法环境中推入一个块级作用域的词法信息,但此时的变量并不能被访问,只有执行到该变量声明的代码处后,才能被访问,否则就是“暂时性死区”
作用域链
再了解一下作用域链。
其实在每个执行上下文的变量环境中,都包含了一个外部引用,用来指向外部的执行上下文,称之为outer
作用域链是是词法作用域的链式结构
词法作用域是静态的作用域,由代码中函数的声明位置有关,跟执行位置无关。所以执行一段代码的时候,查找变量的顺序是确定的。
当一个函数返回值中有函数,那么由于作用域链的关系,内部函数能访问外部函数中的变量,此时就会生成一个闭包。
闭包
直接拿这个例子进行讲述。
- 当js引擎第一次执行到foo函数,首先会编译
- 创建一个空的执行上下文
- 编译过程中,遇到内部函数,js引擎预编译器要对内部函数做一次快速的词法扫描,发现内部函数引用了foo函数的myName和test1,所以js引擎判断这是一个闭包,做一个标记表示执行结束后需要创建闭包。
- 当执行到第9行的时候,局部作用域内有a和b变量,此时堆中不存在闭包
- 当执行到第10行的时候,发现a变量以闭包的形式存在堆中,b数组此时去snapshot中看也看不到,说明销毁了,所以闭包只存在只对被引用的变量
- 说明在foo函数执行结束后在堆空间创建一个closure(foo)(这是一个内部对象,js是无法访问的),用来保持myName和test1
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。