闭包详细图解

前言

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

正文

作用域和作用域链

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

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

作用域分为两种:

  • 全局作用域:在浏览器端全局作用域就是window,他可以重复使用,随处可用。但是全局作用的定义的变量容易被污染
  • 函数作用域:函数声明的时候会自动创建函数作用域对象scope,他指向函数声明时的作用域。而每个函数调用时创建的AO对象前会先创建当前函数的作用域,并创建parent对象通过函数声明时的作用域对象scope指向他函数的父级作用域。他不会污染全局,但是不会重复使用。函数执行完毕后会随着函数执行上问出栈=>AO对象销毁而销毁

作用域链

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

闭包

为什么会使用闭包?

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

  • 全局变量:可以重用、但是会造成全局污染而且容易被篡改
  • 局部变量:仅函数内使用不会造成全局污染也不会被篡改、不可以重用
    从上面可以看出全局变量和局部变量的优缺点刚好是相对的。闭包的出现正好结合了全局变量和局部变量的优点。

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

我们以一下代码为例:

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

代码开始执行时

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

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


image

执行顺序:

  • 全局函数add => 内存地址 => add(){....}
  • 全局变量num => undefined
    函数在声明时会自动创建一个scope对象指向创建时的作用域(AO活动对象),此时作用域是window,所以声明的add函数的socpe指向window

add()执行

image

执行顺序:

  • add执行上下文入栈,创建add活动对象AO
    • add函数AO对象中有:
    • 局部变量n => 0
    • parent => window对象(parent根据add函数声明时scope)
  • add执行返回一个函数,函数声明创建scope对象指向当前作用域 => add活动对象AO
  • 返回的函数,函数被全局变量num引用

add()执行完成,出栈

image.png

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

此时问题来了。

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

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

num()执行

image

执行顺序:

  • num执行上下文入栈。创建num活动对象AO
    • num函数AO对象中:
    • parent => add函数AO对象(parent根据num函数声明时scope)
  • 执行n++,发现当前函数作用域中没有n变量,则根据作用域链向上查找。找到add的AO中有n变量。n++

num()执行完成,出栈

image

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

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

闭包的缺点

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

如何取消闭包

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,197评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,254评论 3 410
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 173,718评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,801评论 1 305
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,732评论 6 404
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,200评论 1 318
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,389评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,484评论 0 282
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,024评论 1 328
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,013评论 3 352
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,125评论 1 359
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,698评论 5 353
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,407评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,795评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,996评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,724评论 3 384
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,150评论 2 368

推荐阅读更多精彩内容

  • 下面就来说说闭包的一些基本概念和具体的形成过程。 什么是闭包? 闭包就是既能重用一个变量,又可以保护变量不被污染的...
    hans_431c阅读 1,017评论 0 1
  • 一、闭包的定义 闭包是指有权访问另一个函数作用域中的变量的函数 --《JavaScript高级程序设计》 函数对象...
    沐向阅读 348评论 0 0
  • 文 / 景朝霞来源公号 / 朝霞的光影笔记ID / zhaoxiajingjing图 / 自己画 ❥❥❥❥点个赞,...
    鲸鲸景鲸叻阅读 581评论 0 0
  • 前言 闭包是JS中重要的内容,对大多数人来说都会觉的闭包本身很好理解,不就是一个函数嵌套一个函数吗?但是再深入解释...
    蛙哇阅读 994评论 0 1
  • 在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容。之前我对闭包的理解主要是"通过闭包可以在函数外部...
    CodeMT阅读 180评论 0 0