JS闭包 - 先说基础

我最初对闭包的定义

关于闭包,很多地方都有所谓的标准定义;但我相信很多人和我一样,看了标准定义之后就进入了蒙B状态。下面是我给出的定义;

闭:封闭
包:作用域
闭包:封闭的作用域

我知道看完我的定义你一样进入到了蒙B状态,但是没关系,且听我慢慢道来。

你一定想问我,是不是只要有一个封闭的作为域就能形成闭包呢?我很想说Yes,但是我不能。因为是不是闭包,还需要看它能否表现出闭包的特性。

关于闭包的特性,在基础里是说不完的,稍候我会列举几个闭包基本的特性;关于闭包特性的全面分析,我会再出专门的文章来讲述我的理解。

说闭包,就必须说清楚它所处的语言环境,在不同的语言环境下,闭包的特性是不完全一样的。所以我定的标题是JS闭包,在javascript语言环境下的闭包。

看了对闭包的标准定义后我的疑问

现在,不得不引用一些标准定义,来简化我的表达:

MDN官方解释:

Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, these functions 'remember' the environment in which they were created.

我的翻译:

Closures 就是一个关联了“独立(自由)变量”(这些变量在封闭的作用域内定义,并且只能在这个封闭的作用域内使用)的函数;也就是说,作为闭包的函数记住了创建他们的上下文环境。

关于这个定义是否准确,在此我先不发表意见,但对于以上定义,我有以下疑问:

  1. 什么叫独立变量或自由变量?
  2. 怎么记住的?
  3. 变量只能在封闭的作用域内使用,这个封闭的作用域与闭包是什么关系?

并且我相信很多人与我一样,有这些疑问。接下来我就一一解释这些疑问,当所有的疑问被消除后,你应该就能对闭包有一个基本的认知了。

上面的英文在括号中有对“独立(自由)变量”进行解释说明,但这句话非常具有误导性,想要真正理解闭包,你必须忘了它。

通过其他版本的闭包定义消除第一层疑问

好,我们再来看看下而来自于“必应网典”对闭包的定义:

闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。

通过上面这段来自于必应网典的定义,我们可以把上面的第一个疑问消除了。下面给出我的理解:

独立(自由)变量:

  1. 首先是未绑定到特定的对象,也就是无法使用 obj.attr 的方式访问到的变量。(obj是一个对象,而attr是独立变量的变量名)
  2. 不是在这个代码块内或者全局上下文件中定义的,而是在定义代码块的环境中定义的局部变量。

如果你对“什么是绑定到特定对象”不了解,你可以去看一下JS中是如何支持面向对象的,相信你看完后会明白“什么是绑定到特定的对象”。

我相信有人会问,上面 2 中提到的“代码块”是什么?其实我最初读“必应网典”上的这段文字时也有同样的疑问。

请再读一遍必应网典上的定义闭包的那段话吧,第一句:

闭包是指可以包含自由(未绑定到特定对象)变量的“代码块”

然后继续往下读……,是不是明白了,这段话中的“代码块”其实就是指闭包。那我们把“代码块”三个字换成“闭包”然后再来读一下 2 里面的话:

不是在这个“闭包”内或者全局上下文件中定义的,而是在定义“闭包”的环境中定义的局部变量。

什么是“局部变量”我就不再解释了,如果你不懂,那么请放弃编程;经过以上分析我们对“独立(自由)变量”的含意已经非常清楚了。下面再重写一遍,加深印象。

独立(自由)变量:

  1. 首先是未绑定到特定的对象
  2. 不是在这个“闭包”内或者全局上下文件中定义的,而是在定义“闭包”的环境中定义的局部变量。

通过分析推理消除第二层疑问

好了,疑问一已经消除,那就向疑问二进军吧。先看一段闭包的代码:

function ClosuresOuter(){
  var random = Math.random();
  function ClosuresInner(){
    return random
  }
  return ClosuresInner;
} 
Closures = ClosuresOuter();

请仔细思考后告诉我,上面这段代码中,谁是闭包?

我知道有人说 ClosuresOuter 是闭包,也有人说 ClosuresInner 是闭包;这样理解也不能说是错,但真正意义上的闭包应该是上述代码中的 Closures ,通过调用 ClosuresOuter 返回的函数。

有争议,没关系,我们先不讨论谁才是真正意义上的闭包,我们回到最初的目的,解决疑问二:闭包是如何记住独立(自由)变量的?

不过要先确认一点,所有人都认同以上代码中是创建了闭包的。如果你觉得上述代码没有创建闭包,那么请回避。

在函数内可以访问函数外声明的变量,看上面的代码,ClosuresInner 内部使用了在 ClosuresInner 外部定义的变量 random ,所以如果ClosuresInner 是闭包的话,它就是这样记住“独立(自由)变量”的。

在javascript中,上面代码中的ClosuresInner 不仅记住了 random, 还记住了运行函数 ClosuresOuter 所创建的整个环境,这是javascript的语言特性,也是javascript支持闭包特性的前提条件。

现在疑问二告破;

究竟谁是闭包?

在上一节的代码中,random 是否满足“独立(自由)变量”的条件呢?

  1. 首先这个变量没有绑定到任何对象
  2. 然后这个变量不能在闭包内或全局作用域内定义,需要在定义闭包的作用域内定义。

好吧,ClosuresOuter 已经不可能是闭包了。有些认为ClosuresInner是闭包的同学们是不是开始洋洋得意,沾沾自喜了?不要这样,请往下看。

实验截图

请仔细看上面的“实验截图”,ClosureOuter 就是上面代码中定义的那个。每次运行 ClosureOuter 都会产生一个新的 random ,请问是谁记住了random?答案是 ClosureInner,但也不是。原因是,每次运行ClosureOuter 同样会产生一个新的 ClosureInner。

这样说吧,ClosureInner其实并不存在,它只是ClosureOuter 作用域内的一个局部变量(函数);如果这个ClosureInner没有被 ClosureOuter 返回并被外层接收返回值的变量接收的话,这个ClosureOuter 运行所创建的临时作用域和作用域内的变量(包含 random 和ClosureInner)会很快被垃圾回收器收回。

所以结论是 ClosureOuter 返回的函数才是闭包(也就是上述代码中的 Closure)。

闭包的基本特性

这个小节只是为了履行前方的承诺,所以不打算用心写,见谅!

  1. 记住闭包所在的环境;创建闭包的外层函数运行时所创建的环境不会被垃圾回收,只有这样才能让闭包记住它所在的环境以及该环境内的独立(自由)变量;所以使用闭包有得有失啊。

自圆其说

最开始我对闭包的定义是:封闭的作用域。这明显很不靠谱啊。所以现在细化如下:

闭:封闭
包:作用域,环境
闭包:能够记忆被创建时环境的封闭作用域

好了,也是址淡的定义,初学者看后也只能蒙了一B。

我为什么不说闭包是“函数”,而是说成“作用域”

简单了说,是因为我知道java的“内部类”也是一种闭包。

装B一点的说法是:只要能够表现出闭包的特性,就可以称之为闭包。在javascript中只有函数有自己的作用域,所以也只有函数有条件成为闭包。但在其他语言中有很多拥有自己作用域的概念,如:包,类,一个代码块都可以有自己的作用域。所以把说成是拥有闭包特性的函数也只在javascript语言中成立。

总结

之所以有这个小节,是我的一种习惯。
闭包基础就写到这儿吧。有不同的见解的,欢迎吐槽。

为什么不解释疑问三

我都说了这句话:“这些变量在封闭的作用域内定义,并且只能在这个封闭的作用域内使用”,非常具有误导性,想要真正理解闭包,你必须忘了它。你为什么不听呢?哼!

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

推荐阅读更多精彩内容