理解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中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input