理解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

相关文章

SpringBoot基于沙箱环境实现支付宝支付教程

《SpringBoot基于沙箱环境实现支付宝支付教程》本文介绍了如何使用支付宝沙箱环境进行开发测试,包括沙箱环境的介绍、准备步骤、在SpringBoot项目中结合支付宝沙箱进行支付接口的实现与测试... 目录一、支付宝沙箱环境介绍二、沙箱环境准备2.1 注册入驻支付宝开放平台2.2 配置沙箱环境2.3 沙箱

使用Java发送邮件到QQ邮箱的完整指南

《使用Java发送邮件到QQ邮箱的完整指南》在现代软件开发中,邮件发送功能是一个常见的需求,无论是用户注册验证、密码重置,还是系统通知,邮件都是一种重要的通信方式,本文将详细介绍如何使用Java编写程... 目录引言1. 准备工作1.1 获取QQ邮箱的SMTP授权码1.2 添加JavaMail依赖2. 实现

Java嵌套for循环优化方案分享

《Java嵌套for循环优化方案分享》介绍了Java中嵌套for循环的优化方法,包括减少循环次数、合并循环、使用更高效的数据结构、并行处理、预处理和缓存、算法优化、尽量减少对象创建以及本地变量优化,通... 目录Java 嵌套 for 循环优化方案1. 减少循环次数2. 合并循环3. 使用更高效的数据结构4

java两个List的交集,并集方式

《java两个List的交集,并集方式》文章主要介绍了Java中两个List的交集和并集的处理方法,推荐使用Apache的CollectionUtils工具类,因为它简单且不会改变原有集合,同时,文章... 目录Java两个List的交集,并集方法一方法二方法三总结java两个List的交集,并集方法一

Spring AI集成DeepSeek三步搞定Java智能应用的详细过程

《SpringAI集成DeepSeek三步搞定Java智能应用的详细过程》本文介绍了如何使用SpringAI集成DeepSeek,一个国内顶尖的多模态大模型,SpringAI提供了一套统一的接口,简... 目录DeepSeek 介绍Spring AI 是什么?Spring AI 的主要功能包括1、环境准备2

Spring AI集成DeepSeek实现流式输出的操作方法

《SpringAI集成DeepSeek实现流式输出的操作方法》本文介绍了如何在SpringBoot中使用Sse(Server-SentEvents)技术实现流式输出,后端使用SpringMVC中的S... 目录一、后端代码二、前端代码三、运行项目小天有话说题外话参考资料前面一篇文章我们实现了《Spring

Spring AI与DeepSeek实战一之快速打造智能对话应用

《SpringAI与DeepSeek实战一之快速打造智能对话应用》本文详细介绍了如何通过SpringAI框架集成DeepSeek大模型,实现普通对话和流式对话功能,步骤包括申请API-KEY、项目搭... 目录一、概述二、申请DeepSeek的API-KEY三、项目搭建3.1. 开发环境要求3.2. mav

Springboot的自动配置是什么及注意事项

《Springboot的自动配置是什么及注意事项》SpringBoot的自动配置(Auto-configuration)是指框架根据项目的依赖和应用程序的环境自动配置Spring应用上下文中的Bean... 目录核心概念:自动配置的关键特点:自动配置工作原理:示例:需要注意的点1.默认配置可能不适合所有场景

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链