JavaScript中的闭包


前言


相信很多小伙伴在工作或者面试过程中都遇到过这个问题,作为经典的前端面试题之一,它高频地出现在我们的求职生涯中。所以,了解和掌握它也就变得十分必要了

读完这篇文章,你或许就会知道:

    1.闭包是什么,它是怎么形成的

    2.为什么要使用闭包

    3.闭包会造成哪些问题

如果文章中有出现纰漏、错误之处,还请看到的小伙伴多多指教,先行谢过

以下↓

作用域


what? 不是在说闭包么,怎么又扯到作用域上面去了

稍安勿躁,在我们了解闭包之前,还是很有必要先了解一下 JavaScript 中的作用域

我们都知道在 JavaScript 中存在着全局变量和局部变量,全局变量可以在任何地方访问到,然而局部变量只能在当前作用域中访问。全局作用域是不能直接访问局部作用域中的变量,而局部作用域可以直接访问全局作用域当中的变量

就像一个代码块儿或函数被嵌套在另一个代码块儿或函数中一样,作用域也会被嵌套在其他的作用域中。所以,如果在直接作用域中找不到一个变量的话,就会咨询下一个外层作用域,如此继续直到找到这个变量或者到达最外层作用域(也就是全局作用域)

说了这么多,其实说白了,所谓 作用域就是一组规则,它决定了一个变量(标识符)在哪里和如何被查找

试想一下,现在有一个这样的需求:我们想在全局作用域拿到局部作用域的某一个变量该怎么去做呢?

初识闭包

在 JavaScript 中闭包无所不在,你只是必须认出它并接纳它

正常情况下,我们并不能拿到局部作用域的变量。但是,我们可以使用变通的方式:定义一个函数

让我们看一下这段代码

这样在函数 foo 中定义一个 bar 函数,在这个函数中我们就能访问到定义在函数 foo 中的变量 a 。既然我们这样就可以访问到 foo 函数里面的变量,那么,只要我们将 bar 这个函数作为返回值输出,不就实现我们的需求了么? 是的!


这就是闭包

让我们来看看这个函数做了什么:

        1.创建了一个函数 foo

        2.函数里面创建了一个变量 a 与函数 bar

        3.返回函数 bar

现在,我们就对闭包有了一个基本的概念:定义在一个函数内部的函数

再遇闭包


词法作用域:简单理解为作用域是由编写时函数被声明的位置定义的

还是来看一下下面的代码

与前面示例不同的是,这里我们并没有将函数 baz 返回,而是将它当做值传递给 bar 这个函数,然后在 bar 这个函数里面执行,函数 bar 保持了函数 baz 的引用

相同的是,实际上函数 baz 都在它被编写时的词法作用域之外被调用,bar() 依然拥有对那个作用域的引用,而这个引用称为闭包

这就是闭包


好了,看到这里是不是还有点懵呢,让我们再来看一个更加常见的示例


毫无疑问,运行上面的代码会输出 5 个 6.很明显,我们得到的结果是 i 在循环之后的最终值

那么,为什么会是这样呢?

其实,由于作用域的工作方式,我们在定时器函数中访问到的 i 是共享到全局作用域的上的,它只有一个,就是最终循环结束的值

想让这个循环显示我们想要的结果也很简单,只需要这样:


使用一个立即执行函数将定时器函数包裹起来,在这个函数中定义一个变量 j,然后将 i 当做值传递进去。这样,在每次迭代的时候,变量 j 都会拥有 i 的一个拷贝,自然得到了我们想要的结果

同样的,这也是一个闭包

当然,我们也可以使用 ES6 中的 let 关键字声明变量 i

通过前面的一些示例,我们不难发现:闭包其实并没有特定的格式,只要满足一些条件,它就是闭包

所以:

闭包就是当一个函数即使是在它的词法作用域之外被调用时,也可以记住并访问它的词法作用域

闭包的用途


闭包最大的用途:

        1.读取函数内部的变量

        2.让这些变量始终保存在内存中

在这段代码中,result 实际上就是闭包 f2 函数。它一共运行了两次,第一次的值是 999 ,第二次的值是 1000 。这证明了,函数 f1 中的局部变量 n 一直保存在内存中,并没有在 f1 调用后被自动清除

    使用闭包模拟私有方法(数据隐藏和封装)

私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分

这个环境中包含两个私有项:名为 privateCounter 的变量和名为 changeBy 的函数。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。

这三个公共函数是共享同一个环境的闭包

闭包的问题


闭包的用途在一定程度上也造成了很多问题,比如:闭包会使函数中的变量始终保存在内存中,不能被 JavaScript 的垃圾回收清理,很容易造成内存消耗过大,影响程序性能


后记


闭包的合理运用,会让我们在开发中写出更优雅和干净的代码。但是,不合理地滥用闭包,也会造成很多性能问题,从而使项目维护成本增加。所以,如何合理地使用这个有趣的东西,还需要我们多多钻研和摸索,相信你一定可以对它越来越熟悉


作者:游荡de蝌蚪

原文地址:https://segmentfault.com/a/1190000019087678#articleHeader4

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

推荐阅读更多精彩内容