深刻理解Javascript中的闭包概念,需要换一种视角。

去年10月写了一篇Javascript原型链的文章,反响不错(看评论里有说有其他博主没有注明原文出处还替我打抱不平,哈哈,你们好可爱,也不能怪他们呀,忽然想起了去年野狗复制我的文章到他们的微信平台也没有注明出处,我还傻傻的感谢他们,如果这篇也敢搬运就更好了,不用注明出处都可以的,还能让平权前进20年,哈哈^_^),看到那么多鼓励的评论,文章能帮助到部分上进的盆友真的很开心,也很有成就感!新年开始,未来的日子里,不断学习和提升生活品质,工作上继续在其位谋其职,写博客,分享能量,感恩生活,敬畏生命,就这样。

同样,闭包和原型链都是Js中有点难度的知识点,所以看本文之前需要较熟悉Javascript基础语法。

这篇文章纠结了几个月一直想不到合适的栗子来一气呵成的讲解JS中闭包的概念,上一篇介绍原型链的时候举的是生物进化链和遗传的例子。终于过了一个年,想到了一个也许有些敏感但是蛮合适的栗子,受去年的毕业设计启发, 想到了这样的一个栗子————不同国家对同性婚姻的态度对家庭和个人的作用。

要理解闭包,就不得不先理解什么是作用域、作用域链。

一、作用域

开篇那张图能帮助你理解作用域么?稍作解释下。

不同国家可以看作是不同的编程语言,亦或是运行环境,亦或是不同函数体。

不同国家法律对于同性婚姻的态度,可以看作是不同编程语言的语法规则,运行环境的系统要求,不同函数的某一局部变量。

国家、家庭、个人都可以看作是一个个闭包。

(1)、“家庭”、“个人”这些闭包可以访问到“国家”这一全局作用域下关于同性婚姻是否受法律保护这一布尔属性。

不同国家规定的“游戏规则”(同性是否可以结婚)这件事;决定了每个家庭是否是一男一女结婚(“家庭”可以访问“国家”关于同性是否可以结婚这个布尔属性,最后“renturn”出符合国家要求的所谓“正确”的婚姻方式),因为你在这个如此定义游戏规则的国家,家庭处于该国家所有“属性”起作用的作用域内,so...;决定了男人要娶女人,女人要去嫁男人结婚这回事,因为每个人可以间接访问到(爸爸妈妈就是一男一女结婚的)国家关于同性是否可以结婚这一布尔属性。每个人都在家庭传统观念属性集合起作用的作用域内,每个人也都在国家的属性起作用的作用域内,so...;

(2)、“国家”未必能访问到“家庭”、“个人”这些闭包的“小心思和行踪”。

上有“政策(全局变量)”,下有“对策(局部变量,且局部变量如果和全部变量同名冲突,将会导致当前闭包作用域内所有用到被修改的全局变量的数据发生些奇妙的变化)”。该家庭是否是形婚这一布尔属性,“国家”是无法访问到的(除非...这个家庭有某种【闭包】被“return”,导致该秘密对外可访问。

部分个体选择去到那些同性婚姻是否合法为True的国家,组成家庭。全局变量随之改变了,so,家庭,个人的行为也将受到该作用域的影响,随之改变。

如果说伏羲与女娲创始婚姻的目的是为了在原始未开化的时期保护女性、孩子能知道爸爸是谁、而非野合、性行为更合理,那后来这太平盛世婚姻的目的不应该被重新定义么。

二、作用域链(scope chain)

如果能完全看懂我上面对作用域的解释,相信作用域链你也就理解了。这里重新解释下。

一个国家规定了同性婚姻是否合法(全局作用域中有一个数据类型为布尔值的属性被声明)。

在这个国家每出生一个新生命,该国家所有游戏规则伴随着他慢慢长大,他也被动接受者身处该作用域下应该懂得和承担的东西。(全局作用域下的A函数可以调用全局作用域下的其他函数和访问其属性(其实一切皆对象),而A函数下的Ainner函数可以调用A函数作用域下的其他函数和访问其属性及A函数的上一级闭包的作用域下....)

结合上图,我们看这样的一段代码:

functionfunA(){varfunA_age =28;returnfunctionfunB(){varfunB_age =20;returnfunctionfunC(){varfunC_age =18;returnfunctionfunD(){varfunD_age =12;      }    }  }}//将函数funA的属性输出;console.dir(funA);

执行上面代码,看看控制台输出了什么。

我们看到在Scopes内有1个元素,展开他发现,他就是所有全局变量。所以我们才可以在funA函数内直接访问window中的各种方法和属性,比如window.setInterval()等。

接着,我将输出改一下。

functionfunA(){varfunA_age =28;returnfunctionfunB(){varfunB_age =20;returnfunctionfunC(){varfunC_age =18;returnfunctionfunD(){varfunD_age =12;      }    }  }}//将funA()的返回值(也就是函数funB)的属性输出;console.dir(funA());

同样能在控制台看到函数funB内可以访问window中的所有方法和属性。以此类推,结果都一样,不管函数嵌套多深,好像window这个全局变量的方法和属性在哪里都能访问到,这难道就是作用域链起到的神奇作用么。好戏还在后头。

functionfunA(){varfunA_age =28;returnfunctionfunB(){varfunB_age =20;//在函数funB中访问funA中定义的funA_age,能获取到么?;console.log(funA_age);returnfunctionfunC(){varfunC_age =18;returnfunctionfunD(){varfunD_age =12;      }    }  }}//将funA()的返回值(也就是函数funB)的属性输出;console.dir(funA());

真的有哎!而且你有沒有发现,js很聪明,因为在第一次没有console.dir()来输出funA_age而只是输出函数funA的时候,我们并没有在控制台的[[Scopes]]中找到被定义的funA_age变量,所以说,作用域链上每个函数作用域内用到了其父级什么变量就存什么变量,没有用到的就不存。

比如说,我们改写函数funD。

functionfunA(){varfunA_age =28;returnfunctionfunB(){varfunB_age =20;returnfunctionfunC(){varfunC_age =18;returnfunctionfunD(){varfunD_age =12;console.log(funA_age);console.log(funB_age);console.log(funC_age);console.log(funD_age);      }    }  }}//执行funD(),将其属性输出;console.dir(funA()()());

这下明白了,funD中需要输出非funD作用域中的funA_age、funB_age、funC_age变量,所以我们在[[Scopes]]中发现了funA作用域中的funA_age;funB作用域中的funB_age;funC作用域中的funC_age... ...

这就是作用域链!

三、闭包

讲完了作用域和作用域链,闭包显而易见,是我所说的“国家”、“家庭”、“个人”、funA、funB、funC、funD。他们都是闭包。

现在就差一个代码示例来帮助你理解闭包了,

(1)、在for循环中执行耗时的异步请求的正确姿势。

闭包一个用处就是在内存中永久存储数据。

执行Ajax请求很耗时间,如果直接将Ajax写入for循环中,你会的到最后一次循环的异步请求 结果。这里我用延时模拟一下这种糟糕的情况。

for(vari=0; i<3; i++){  setTimeout(function(){console.log(i);  },1000);}

最后输出了3次3,并不是我们希望的0,1,2,难道我写了一个假循环!

闭包来了~

for(vari=0; i<3; i++){  print(i);}functionprint(num){console.log(num);}

也许你会问,print怎么算闭包,闭包不是应该签到在函数里的么,白眼.jpg。请把全局作用域也考虑在内,全局作用域内的函数都是闭包,所以print函数也是啊。

也许你还会问,参数也可以被保存进内存?白眼.jpg。是的!如果没有传进来实参,那么在该函数作用域内访问才参数会返回undefined。如果你觉得不舒服,可以将传入的参数重新赋值给你自己定义的变量,后续就用这个变量,当然我就经常喜欢重新赋值一下。

(2)、面向对象的编程,返回闭包当作对象的公用方法,而“对象”内的变量不会污染全局变量。

functionA(){var_age =0;returnfunction(){console.log(_age++);  }}vara = A();a();a();a();a();

猜猜,控制台输出什么,4个0么?

当然不是啦,每次执行一次函数a,都相当于在当前私有变量_age的基础上加1。注意我喜欢私有变量前边加上一个下划线(JS里变量首字母可以是字母、美刀符号和下划线),不然容易和全局变量同名引起副作用。

也许聪明的你早已看穿了这一切,这个A不就是个构造函数嘛!~没错滴。

我再用另一种写法写出相同的功能。这种写法就不存在同名冲突的问题。

functionPerson(){this.age =0;}Person.prototype.add =function(){console.log(this.age++);}varcsh =newPerson();csh.add();csh.add();csh.add();csh.add();

这就是我理解的作用域、作用域链和闭包,希望你也理解了,甚至比我更好的理解了。

我们的目标是解决实际问题,而不是炫耀技术和理解力,就像闭包,他不是内置函数,只是一个概念,就算你以前不知道他,可你只要写过函数就一定在无时无刻用着它的特性。多像老子口中的哪位智者呐,不见行踪又无处不在,好像无用实则有大用。

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

推荐阅读更多精彩内容

  • 介绍 JavaScript中有一个被称为作用域(Scope)的特性。虽然对于许多新手开发者来说,作用域的概念并不是...
    安_6dd1阅读 896评论 0 8
  • 1.对象是什么 对象就是若干属性的集合。 在JS中一切引用类型都是对象:数组是对象,函数是对象,对象还是对象。对象...
    liushaung阅读 1,185评论 0 2
  • 前言 对于js中的闭包,无论是老司机还是小白,我想,见得不能再多了,然而有时三言两语却很难说得明白,反正在我初学时...
    itclanCoder阅读 4,139评论 1 11
  • 今天下午和孩子在外面吃饭的时候,隔壁桌的一种家三口真的给我上了生动的一课,这个直白真实的逼迫教育法,真是给了...
    尚巾林阅读 202评论 0 0
  • 春光旖旎,几多欢笑,最是梨花深处,照清影。 雨漫漫,不知归处,只是清影淡淡惬。 梦迴几时,堪为拾哉?留的一简疏...
    殊陌苏阅读 188评论 1 4