📌 作用域
作用域 [[ scope ]] 决定了代码区块中变量和其他资源的可见性
- JavaScript函数都是一个对象,其中 [[ scope ]] 是不可访问,仅供JavaScript引擎存取属性之一
- [[ scope ]]即作用域,其存储了运行期上下文集合
作用域分类
- 全局作用域 :直接在script标签(单独JS文件)中的js代码,页面打开创建,关闭销毁
- 局部作用域 :直只能在固定代码片段内可访问到,外部是无法访问的
- 函数作用域 **:只能在函数内部可访问,函数调用时创建,执行完毕销毁,调用一次创建一次,相互独立**
- 块级作用域 **:花括号内部声明了const或let变量,只能在花括号内部访问**
📌 作用域链
作用域链 (Scope Chain) [[ scope ]]中存储的执行期上下文对象的集合,该集合呈链式即作用域链
function a(){function b(){var b = 2;}var a = 1;}var c = 3;a()
- 当a函数被定义时
- 系统生成[[ scope ]]属性,其保存a函数的作用域链
- 该作用域链第0位,存储当前环境下的全局执行期上下文GO
- 全局执行期上下文GO存储全局下的所有对象
- 当a函数被执行时(前一刻)
- 作用域链第0位,存储a函数生成的执行期上下文AO
- 同时,该作用域链第0位的全局执行期上下文GO被挤至第1位
- 查找变量是从该作用域链顶端依次向下查找
- 当b函数被定义时
- 因b函数在a函数内部,所以此时执行期上下文与a函数执行时(上述)时一样的
- **当b函数被执行时(前一刻)
- 生成b函数的[[ scope ]]属性,其保存b函数的作用域链
- 作用域链第0位,存储b函数的执行期上下文AO
- 同时,a函数的AO和全局的GO被挤依次向下排列
- b函数执行结束
- b函数的AO被销毁,作用域链回归其被定义时的状态
- a函数的AO升至作用域链第0位,全局的GO至第1位
- a函数执行结束
- a函数的AO被销毁,回归其被定义时的状态
- 全局GO升至作用域链第0位
- 因其内部存储有b函数的值,所以此时b函数的 [[ scope ]]将不存在
小试牛刀——分析作用域链 当外部环境执行,其内部函数才会被定义
function a(){function b(){function c(){}c();}b();}a();【作用域链分析】:页面刷新预解析定义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]] 销毁;
📌 闭包基础
闭包
- 当内部函数被返回到外部并保存时,一定会产生闭包
闭包会产生原来的作用域链不释放,过渡闭包可能会导致内存泄漏或加载过慢
function test1(){function test2(){var b = 2;console.log(a)}var a = 1;return test2;}var c = 3;var test3 = test1();test3()
当test1函数被定义时
- 系统生成test1的[[scope]]属性,其保存test1函数作用域链
- 该作用域链第0位,存储当前环境下的全局执行期上下文GO
- 全局执行期上下文GO存储全局下的所有对象
- 当test1函数被执行时,其内部test2函数被定义
- 作用域链第0位,存储test1执行期上下文AO,全局GO挤至第1位
- test1执行期上下文AO存储期内部所有对象
- 同时,系统生成test2的[[scope]]属性,其保存test2函数作用域链
- 因test2函数在test1函数内部,此时test2函数作用域链与test1相同都指向test1的执行期上下文AO
- 当test1函数被结束时,test2被return抛出,被全局变量test3接收
- 此时,test1函数的作用域链指向test1执行期上下文AO被断开
- 因test2函数作用域链同时也指向test1执行期上下文AO,所以test1的AO并不会被销毁
- 当test3函数被执行时,实际是接收的test2被执行
- 作用域链第0位,存储test2执行期上下文AO,test1的AO和全局GO依次向下排列
- 当打印a变量时,自己的AO上没有,沿作用域链向下找到test1的AO中a=1
- 当test3函数执行结束,实际是接收的test2执行结束
- test2函数的AO被销毁,但其指向的test1执行期上下文AO仍存在
技巧:**闭包找到的是同一地址中父级函数中对应变量最终的值 **
