理解Javascript中的闭包概念也会如此沉重

2024-05-05 16:38

本文主要是介绍理解Javascript中的闭包概念也会如此沉重,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文链接:http://shuaihua.cc/article/javascript/closure-of-javascript.php

转载请注明出处,谢谢合作。


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

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

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

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

一、作用域

Javascript闭包概念与同性婚姻合法化异同

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

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

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

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

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

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

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

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

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

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

深入研究会有些沉重感,抛开这一具体社会问题来讲,语言成为暴力道理很简单,就像某A理所当然的规划了某B的人生;为了自己而编出为天下苍生的名句,某些看客不假思索不加批评的将某A的话当作人生的至理名言到处宣扬。我只想说,请让我知道你的脑袋的存在不只是为了自拍。人是,国家也是,谁又不是在每天扮演那个某A呢(虽善、恶有别,但行径无差)。我所看不惯的,会有一些人在为所谓的“深明大义”辩解。请权力者不要做侩子手,因为作用域越大,责任越大,暴力远比艺术单纯,弄权者要慎重啊。根深蒂固的,就交给时间交给春风吧,无法强硬推翻的就温柔的推翻,把世界变成我们希望的样子。都有难言之隐,没有对错。

大家可以对这个栗子结合闭包进行再演绎,其实还有很多细节没有深挖。

二、作用域链(scope chain)

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

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

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

Javascript闭包概念与同性婚姻合法化异同

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

function funA(){var funA_age = 28;return function funB(){var funB_age = 20;return function funC(){var funC_age = 18;return function funD(){var funD_age = 12;}}}
}
//将函数funA的属性输出;
console.dir(funA);

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

理解Javascript中的闭包概念也会如此沉重

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

接着,我将输出改一下。

function funA(){var funA_age = 28;return function funB(){var funB_age = 20;return function funC(){var funC_age = 18;return function funD(){var funD_age = 12;}}}
}
//将funA()的返回值(也就是函数funB)的属性输出;
console.dir(funA());

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

function funA(){var funA_age = 28;return function funB(){	var funB_age = 20;//在函数funB中访问funA中定义的funA_age,能获取到么?;console.log(funA_age);return function funC(){var funC_age = 18;return function funD(){var funD_age = 12;}}}
}
//将funA()的返回值(也就是函数funB)的属性输出;
console.dir(funA());

理解Javascript中的闭包概念也会如此沉重

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

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

function funA(){var funA_age = 28;return function funB(){var funB_age = 20;return function funC(){var funC_age = 18;return function funD(){var funD_age = 12;console.log(funA_age);console.log(funB_age);console.log(funC_age);console.log(funD_age);}}}
}
//执行funD(),将其属性输出;
console.dir(funA()()());

理解Javascript中的闭包概念也会如此沉重

这下明白了,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(var i=0; i<3; i++){setTimeout(function(){console.log(i);},1000);
}

理解Javascript中的闭包概念也会如此沉重

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

闭包来了~

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

理解Javascript中的闭包概念也会如此沉重

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

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

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

function A(){var _age = 0;return function(){console.log(_age++);}
}
var a = A();
a();
a();
a();
a();

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

理解Javascript中的闭包概念也会如此沉重

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

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

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


function Person(){this.age = 0;
}
Person.prototype.add = function(){console.log(this.age++);
}
var csh = new Person();
csh.add();
csh.add();
csh.add();
csh.add();

理解Javascript中的闭包概念也会如此沉重

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

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


原文链接:http://shuaihua.cc/article/javascript/closure-of-javascript.php

转载请注明出处,谢谢合作。


这篇关于理解Javascript中的闭包概念也会如此沉重的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/962161

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt