闭包访问过程以及内存泄露

一.闭包的理解

  • 维基百科中解释:闭包又称词法闭包或者函数闭包,是一个结构体,存储了函数和一个关联的环境,和函数最大的区别就是,当捕捉闭包的时候,它的自由变量会在补充的时候被确定,即使脱离了捕捉的上下文,也能照成运行

  • MDN解释:一个函数对其周围的引用绑定在一起,这样的组合就是闭包

我的理解就是:
一个普通函数,如果它可以访问外层作用于的自由变量,那么这个函数就是一个闭包
广义的角度: Js中函数都是闭包
狭义的角度:Js中的一个函数,如果访问了外层作用域的变量,那么它就是一个闭包

二. 闭包的访问过程(V8引擎)

以下代码举个例子:

      function foo(){
          var name = 'bazy'
          function bar(){
              console.log("引用外部作用域变量",name);
          }
          return bar
       }
       var fn = foo()
       fn()
  • JavaScript在parse编译阶段,会在堆内存全局创建一个GO对象,包含全局的一些初始变量,并且赋值undefind

var GobalObject = {
    +String,
    +seTimeout,
    +Data,
    window: GobalObject  //window.window.window

  //自己定义的变量
    fn:undefind
    foo:undefind
}
  • 当JavaScript 执行的时候会建立一个执行栈ECS,和一个堆内存, 当全局执行代码会创建一个全局执行上下文GEC,其中存入了两部分,第一部分是VO对象,指向的就是GO对象,而第二部分就是执行的代码体

  • 之后就开始执行代码,GO对象会被重新赋值,若遇到函数,会在堆内存中开辟一个内存,用来存放当前函数父级作用域和函数体,GO中fn 会指向当前对象的地址

var GobalObject = {
    +String,
    +seTimeout,
    +Data,
    window: GobalObject  //window.window.window

  //自己定义的变量
    fn:0x100  //看这一部分
    foo:0x100
}

此时堆内存会变为:

  • 当foo函数执行前会创建一个函数执行上下文栈FEC包含两部分,并且在编译阶段的时候会创建一个AO对象,默认包含函数的形参,argments等,也有自己定义的变量,默认复制undefind,第一部分是VO,此时指向的是AO对象,VO+父级VO,this绑定等另一部分是代码执行体,

  • 当foo函数执行的时候,AO会重新赋值,由于foo函数内返回一个bar,因此GO也会发生相应变化

此时 foo函数AO会变成

var AO = {
   name: 'bazy',
   bar: 0x200
}

由于bar函数是被return 出去的,所以 全局GO会变化为

var GobalObject = {
    +String,
    +seTimeout,
    +Data,
    window: GobalObject  //window.window.window

  //自己定义的变量
    fn:0x200   //看这一步
    foo:0x100
}
  • 此时bar函数也执行啦,那么bar 也会创建自己的AO对象,也会创建自己的调用栈、

  • 此时bar函数会通过在分配函数的对象的时候确定了作用域,进入会一级一级向上查找,进而打印出结果

三.foo函数执行完,AO对象就会销毁,为什么bar函数依然可以访问外层foo函数的变量name(闭包就体现在这点)

  • 首先我们需要了解一下,JavaScript垃圾回收机制,在JavaScript 主要采用了CG算法进行内存回收,它的的基本原理就是监听一个根对象,例如GO对象,定期检查,如果该对象中,某个属性没有指向堆内存中的某个对象地址,就会被回收

看上图可以由于foo函数内部返回一个bar函数,因此fn指向的最终地址为bar函数对象,函数foo在执行完以后就会销毁,但是它的AO对象,依然是被bar函数指着,而bar 函数,此时被fn指针指向,因此CG算法在检测的时候会检测到该对象仍然是被指针指向,所以就不会被销毁

四. 闭包的内存泄露

  • 在上面的案例中,如果我们后续不在使用fn函数,那么该对象就应该被销毁掉,并且引用的父作用域也该被销毁掉
  • 但是在全局作用域下fn变量对0x200 有引用,而0X200对 0x100有引用,所以最终这些内存是无法被释放掉的
  • 那么该如何解决这个问题呢

我们直接给fn赋值null 就不再对0X100有引用,在GC下一次检测中它们就会被销毁掉

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值