喋喋不休

真正的勇士,胖还贪吃,困还熬夜,穷还追星,丑还颜控

0%

JavaScript 闭包

闭包之前我们来看看函数的作用域以及作用域链

函数的生命周期:

1、程序开始执行:
创建一个Execution Context Stack(ECS)
依次保存正在调用的函数的执行环境的栈结构
创建全局作用域对象:window
在ECS中压入第一个全局执行环境EC,全局EC引用window

node

2、定义函数时:
创建函数对象,封装函数的定义
在函数对象中,设置scope属性,引用函数来自的作用域,通常scope都是window
用函数名创建全局变量,引用函数对象

node

3、调用函数时:
创建一个活动对象Actived Object(AO): 活动对象:保存函数的局部变量的函数作用域对象。
向ECS中压入本次函数调用的执行环境EC
EC引用AO
设置AO引用函数的scope(window)

node

4、函数调用后:
EC出栈,导致AO无人使用,被释放
导致,AO中的局部变量一同被释放!

node

作用域: 一个变量的可用范围
其实window对象就是全局作用域
AO对象就是函数作用域
AO对象又引用了window对象

作用域链: 由各级作用域对象,逐级引用形成的链式结构,就是作用域链。
作用域链的末尾是window对象
作用域链控制着变量的使用顺序:
优先使用AO中的局部变量
如果AO中没有,就延作用域链向下找。
如果到window还没找到,就报错

什么是闭包?

简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。

MDN 上面这么说:闭包是一种特殊的对象。
它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

为什么使用:

全局变量: 优: 随处可用,可反复使用
          缺: 易被全局污染

局部变量: 优: 仅在函数内可用,不会被污染
          缺: 无法反复使用

何时使用: 如果希望重用一个变量,且不希望该变量被随意篡改时。

如何实现: 3步

1. 用外层函数将受保护的变量和操作变量的函数封装在内部

2. 外层函数将内层函数返回

3. 调用外层函数,获得返回的内层函数对象。

说明: 1. 两次外层函数调用返回的闭包中,受保护的变量是各自独立的,没有任何关系。

创建闭包

1.开始几步和函数的声明创建一样

node

2.函数创建完成后的各自引用

node

3.调用外层函数形成活动AO,向全局EC中压入本次函数的EC

node

4.外层函数返回内层函数,形成内层函数的对象,这样图中绿色的箭头形成引用

node

5.当外层函数的调用结束后,外层函数的作用域并没有释放,留了下来,就形成了大名鼎鼎的bibox

node

6.调用执行函数时,n++

node

7.函数调用结束后,getNum出栈

node

8.当执行n=1时,并没有改变outer AO中的局部变量,所以最后的调用n=2

node

function outer(){
    var n = 1;
    return function(){
        console.log(n++)
    }
}
var getNum = outer();
getNum();   //1
n = 1; 
getNum()  //2

闭包缺点:

占用更多内存空间

且一旦形成闭包,无法自动释放。

至此,闭包的全部解析就这样了