闭包详细图解

2020-11-20  本文已影响0人  灯下草虫鸣314

前言

在我们了解了执行上下文EC之后,我们应该知道了,js执行的正常流程。但是我们在工作、面试时经常会遇到闭包这个奇怪的东西,今天我们就来详细的了解一下。闭包是如何形成的,闭包在ECS中是如何执行的。

正文

作用域和作用域链

讲闭包之前我们需要先了解作用域和作用域链。

作用域: 表示一个变量的可用范围,从而避免不同范围的变量之间相互干扰。

作用域分为两种:

作用域链

变量取值到创建这个变量的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向通过parent对象去父作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

闭包

为什么会使用闭包?

我们先总结下全局变量和局部变量的优缺点。

闭包是怎么形成,在ECS如何执行的呢?

我们以一下代码为例:

function add(){
  var n = 0
  return function(){
    n++
    console.log(n)
  }
}
const num = add()
num()
num()
num()

代码开始执行时

首先创建全局执行上下文,并将全局执行上下文压入执行上下文栈底。

那么全局执行上下文活动对象AO=>window对象中会有以下内容


image

执行顺序:

add()执行

image

执行顺序:

add()执行完成,出栈

image.png

add()执行完成。add的执行上下文出栈。

此时问题来了。

add()执行上下文已经出栈了。但是add的活动对象AO没有被释放!

为什么呢? 根据图片的我标注出来的红线,这里形成了一个循环引用。所有add的活动对象AO无法被释放。这样就形成了一个闭包。

num()执行

image

执行顺序:

num()执行完成,出栈

image

num()执行完成。num的执行上下文出栈。num的活动对象AO被垃圾回收。
接下来的num()执行,和上述步骤相同。

到这里闭包函数是如何执行也就解释清楚了。

闭包的缺点

通过上面的分析。我们发现没生成一个闭包就会有一个活动对象AO常驻内存,不会被销毁,所以有可能会导致内存溢出。

如何取消闭包

通过上面的分析。我们发现生成闭包就是因为有一个循环引用。导致活动对象AO无法被销毁垃圾回收。所以我们如果想要取消闭包,只需要打断循环引用就好了: num = null。

上一篇下一篇

猜你喜欢

热点阅读