闭包的理解

在理解闭包之前,我们首先得知道,JavaScript垃圾回收,以及JavaScript作用域链

JavaScript垃圾回收

JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收

如果不了解JS的内存管理机制,非常容易造成内存泄露(内存无法被回收)的情况

内存的生命周期

JS环境中分配的内存的生命周期

1、内存分配:每当声明变量、函数、对象的时候,系统会自动为他们分配内存

2、内存使用:读写内存,也就是使用变量、函数等

3、内存回收:使用完毕,由垃圾回收器自动回收不在使用的内存

注意:全局变量一般不会被回收,一般局部变量的值,不再使用后,会被回收

总结:所谓垃圾回收,核心思想就是如何判断内存已经不再会被使用后,就视为垃圾,释放掉

两种方式判断是否回收:引用计数算法和标记清除算法

引用计数算法

IE采用的就是引用计数算法

引用计数算法,定义内存不再使用的标准,就是看一个对象是否有指向它的引用

如果每当多一个引用,则会在当前内存空间的引用次数+1,

如果每当少一个引用,则会在当前内存空间的引用次数-1,直到0为止,说明当前引用的对象不再需要

但它却存在一个致命的问题:循环引用。

如果两个对象相互引用,尽管不再使用,垃圾回收器不会进行回收,导致内存泄露

 

 

标记清除算法

现代浏览器通用的大多是基于标记清楚算法的某些改进算法

标记清楚算法将“不再使用的对象”定义为“无法达到的对象”

简单来说,就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象

凡是能从根部开始到达的对象,都是还需要使用的,无法由根部出发触及到的对象则会被标记为不再使用,稍后进行回收

从概念可以看出,无法触及的对象包含了没有引用的对象这个概念(没有任何引用的对象也是无法触及的对象)

JavaScript 作用域链

JavaScript在执行过程中会创建一个个的可执行上下文。(每个函数执行都会创建这么一个可执行上下文)

每个可执行上下文的词法环境中包含了对外部词法环境的引用,可以通过引用来获取外部词法环境中的变量和声明

这些引用串联起来,一直指向全局的词法环境,形成一个链式结构,被称为作用域链

简而言之:函数内部可以访问函数外部作用域的变量,而且外部函数还可以访问到全局作用域的变量

       这样的变量作用域访问的链式结构,被称之为作用域链

 

闭包

MDN的官方解释:闭包是函数和声明该函数的词法环境的组合

通俗一点就是:内部函数,调用外部函数的变量,从而形成闭包

一个简单的闭包实例:

function fn () {
  let count = 0

  function add () {
    count++
    console.log('fn函数被调用了' + count + '次')
  }

  return add
}
const addFn = fn()
addFn()

在实际开发中,闭包最大的作用就是用来做“变量私有化”

如果count定义成全局变量,太容易被别人修改,所以可以利用闭包解决

闭包的缺点:由于标记清除法能够从根部访问到闭包的内存空间,会认为一直在被应用,从而不发释放,导致内存泄露

解决方法:断开该内存地址的引用

addFn = null

 

posted @ 2021-03-29 23:25  ShuaiBiZou  阅读(86)  评论(0)    收藏  举报