JavaScript --闭包(Closure)

概念 

闭包是一种特殊的函数,它可以访问并操作在其定义时所处的作用域中的变量,即使这些变量在函数执行时不再存在。要理解闭包,我们需要掌握以下几个关键概念:

  1. 作用域(Scope):作用域是定义变量的有效范围,在 JavaScript 中通常由函数确定。每当创建一个新的函数时,就会创建一个新的作用域。

  2. 词法环境(Lexical Environment):词法环境是一个存储变量和函数声明的数据结构,它与特定的作用域相关联。在 JavaScript 中,每个函数都有自己的词法环境。

  3. 闭包(Closure):闭包是指一个嵌套的内部函数引用了外部函数的变量或函数,并且这个内部函数被返回或在其它上下文中使用,从而形成了一个封闭的作用域。这样的组合就产生了闭包。

条件

为了创建一个闭包,必须满足以下条件:

  • 存在一个嵌套结构,即内部(子)函数定义在外部(父)函数内部。
  • 内部(子)函数引用了外部(父)函数的变量或函数。
  • 外部(父)函数将内部(子)函数作为返回值返回,或传递给其他函数进行调用。

概括来说,闭包是嵌套的内部(子)函数引用了外部(父)函数的变量或函数,并且能够访问和操作这些外部作用域的内容。 

作用 

闭包的作用主要有两个方面:

  1. 保持变量的状态:闭包使得内部函数可以访问外部函数的变量,即使外部函数执行结束后,这些变量的值仍然存在于内存中。这样可以实现对变量的保持和共享,常用于实现数据私有化、封装和延长变量的生命周期。
  2. 访问外部作用域:闭包能够让内部函数访问外部函数的作用域链,包括外部函数的参数、局部变量等。这样可以在内部函数中使用外部函数的上下文和变量,增强了灵活性和扩展性,常用于实现回调函数、异步操作、实现模块化等。

综上所述,闭包是指一个嵌套的内部函数引用了外部函数的变量或函数,在其它上下文中使用时形成了封闭的作用域。闭包能够保持变量状态、访问外部作用域,具有重要的编程和设计应用。

工作原理

闭包的工作原理如下:

  1. 当外部函数被调用时,它会创建一个新的作用域,并在该作用域中声明变量。
  2. 在外部函数中定义的内部函数会捕获并保持对外部函数作用域的引用,包括其中的变量。
  3. 外部函数返回内部函数,使得内部函数可以在外部函数执行完毕后继续访问外部函数的作用域和变量。
  4. 当调用内部函数时,它可以访问并操作其闭包中的变量。

闭包生命周期 

闭包的生命周期可以描述为以下几个阶段:

  1. 创建:当嵌套函数在外部函数中定义时,闭包就会被创建。在这个阶段,内部函数会捕获并保存其外部作用域中的变量引用。
  2. 活跃:一旦闭包被创建,并且其内部函数可以被调用或传递给其他函数使用时,闭包就处于活跃状态。在此阶段,闭包可以访问和修改其捕获的外部变量。
  3. 释放:当闭包不再被引用或需要时,它可能会进入释放阶段。当闭包成为垃圾对象,并且无法通过其他代码引用时,它将被垃圾回收机制回收。

重要的是要注意闭包的释放及时性,特别是在使用闭包的流程中,确保在不再需要时及时释放对闭包的引用,以避免内存泄漏问题。这可以通过避免循环引用、显式解除引用等方式来实现。

总结来说,闭包的生命周期由其创建时捕获外部变量的引用开始,在活跃期间可以访问和修改这些变量,直到最后被垃圾回收释放为止。

用途 

  1. 闭包的一个常见用途是创建私有变量。通过将变量封装在闭包中,并通过返回一个内部函数来访问和修改该变量,可以实现数据的封装和隐藏,防止被外部直接访问和修改。
  2. 另一个常见的应用是在异步编程中。由于闭包能够维持其创建时的词法环境,可以在回调函数中访问和操作外部的变量,从而解决了一些异步操作中的共享数据或状态管理问题。
  3. 闭包对内存管理也有一定的影响。由于闭包保留了对外部作用域的引用,如果闭包使用不当,可能导致内存泄漏的问题。因此,在使用闭包时需要注意及时释放不再使用的资源,以避免造成不必要的内存消耗。
  4. 缓存:闭包可以用于创建一个缓存函数,以提高函数的执行效率。通过在闭包内部维护一个缓存对象,并根据输入参数来判断是否需要重新计算结果,可以避免重复计算耗时的操作。
  5. 部分应用(Partial Application):闭包可以用于实现部分应用函数,即固定某些参数,返回一个接受剩余参数的函数。这样可以减少重复的代码,增强了函数的灵活性和复用性。
  6. 函数柯里化(Currying):闭包可以用于实现函数柯里化,将多个参数的函数转化为接受单个参数的嵌套函数序列。这样可以将函数的调用形式更加灵活,方便函数的组合和复用。

演示说明: 

<!DOCTYPE html>
<html><head><title>闭包趣味</title><style>body {text-align: center;font-family: Arial, sans-serif;}h1 {color: #FF0000;}</style>
</head><body><h1 id="title">欢迎!</h1><button id="colorBtn">改变标题颜色</button><button id="textBtn">改变标题文本</button><button id="resetBtn">重置标题颜色</button><script>// 使用闭包创建事件处理函数function changeColorOnClick() {var title = document.getElementById("title");return function () {var colors = ["#FF0000", "#00FF00", "#0000FF"];var index = 0;return function () {title.style.color = colors[index];index = (index + 1) % colors.length;};};}var colorBtn = document.getElementById("colorBtn");var changeColor = changeColorOnClick();colorBtn.addEventListener("click", changeColor());// 通过闭包修改标题文本function changeTextOnClick() {var title = document.getElementById("title");var texts = ["Hello!", "你好!", "こんにちは!"];var index = 0;return function () {title.innerHTML = texts[index];index = (index + 1) % texts.length;};}var textBtn = document.getElementById("textBtn");var changeText = changeTextOnClick();textBtn.addEventListener("click", changeText);// 重置标题颜色var resetBtn = document.getElementById("resetBtn");resetBtn.addEventListener("click", function () {var title = document.getElementById("title");title.style.color = "#FF0000"; // 重置为红色});// 另一种使用闭包的方式,将颜色用闭包封装起来function createColorChanger() {var colors = ["red", "green", "blue"];var index = 0;return function () {return colors[index++ % colors.length];};}var getColor = createColorChanger();setInterval(function () {title.style.color = getColor();}, 1000);</script>
</body></html>

注意:

尽管闭包在编程中具有广泛的应用,但过度或不合理地使用闭包可能会导致代码可读性、性能和内存管理等方面的问题。因此,在使用闭包时需要权衡利弊,并考虑其适用的场景和限制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/26405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

再次斩获第一,文心3.5霸榜国内大模型

目录 1 什么是文心一言&#xff1f;2 体验与文心一言对话3 文心3.5霸榜国内大模型 1 什么是文心一言&#xff1f; 文心一言是百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#xff0c;回答问题&#xff0c;协助创作&#xf…

【数据结构】Disruptor环形数组无锁并发框架阅读

Disruptor 是苹国外厂本易公司LMAX开发的一个高件能列&#xff0c;研发的初夷是解决内存队列的延识问顾在性能测试中发现竟然与10操作处于同样的数量级)&#xff0c;基于Disruptor开发的系统单线程能支撑每秒600万订单&#xff0c;2010年在QCn演讲后&#xff0c;获得了业界关注…

linux内网穿透应用场景有哪些?快解析有什么用处?

随着网络技术的不断发展&#xff0c;无论是工作上还是在生活中人们对网络的依赖和需求越来越高。Linux内网穿透作为一种创新的解决方案&#xff0c;为我们提供了无限可能。 首先我们了解一下Linux操作系统。Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基…

用Python获取链家二手房房源数据,做可视化图分析数据

前言 数据采集的步骤是固定: 发送请求, 模拟浏览器对于url地址发送请求获取数据, 获取网页数据内容 --> 请求那个链接地址, 返回服务器响应数据解析数据, 提取我们需要的数据内容保存数据, 保存本地文件 所需模块 win R 输入cmd 输入安装命令 pip install 模块名 (如果你…

DNS:DNS问题故障排查

写在前面 分享一些 DNS 排故的笔记给小伙伴博文内容涉及 DNS 解析顺序&#xff0c;常见排故顺序理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。所有其它的路都是不…

UML-构件图

目录 1.概述 2.构件的类型 3.构件和类 4.构件图 1.概述 构件图主要用于描述各种软件之间的依赖关系&#xff0c;例如&#xff0c;可执行文件和源文件之间的依赖关系&#xff0c;所设计的系统中的构件的表示法及这些构件之间的关系构成了构件图 构件图从软件架构的角度来描述…

leetcode357周赛

2810. 故障键盘 核心思想&#xff1a;自己想的笨办法&#xff0c;枚举s&#xff0c;然后遇到i就翻转。比较好的方法就是双端队列&#xff0c;遇到i字母原本往后加的就往前加&#xff0c;然后读的时候反过来读&#xff0c;往前加的就往后加&#xff0c;读的话就从前往后&#x…

git 版本控制与合并

一 git概述&#xff1a; - Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理软件开发项目中的代码变更。 - 它允许多人协同工作&#xff0c;记录代码历史变更&#xff0c;并轻松管理多个项目版本。 **Git的主要特点**包括&#xff1a; 1. **分布式系统**&#xff1a;…

小研究 - MySQL 分区分表的设计及实(一)

随着信息技术的快速发展&#xff0c;数据量越来越大&#xff0c;海量的表查询操作需要消耗大量的时间&#xff0c;成为影响数据库访问性能提高的主要因素。为了提升数据库操作的查询效率和用户体验&#xff0c;在关系型数据库管理系统(MySQL)中通过 range 分区和 Merge 存储&am…

c++:day4

1.思维导图 2.shell函数获取uid和gid&#xff0c;并用变量接 #!/bin/bashfunction fun() {read -p "输入用户名" necho uid:id -u $necho gid:id -g $n } afun echo $a3.冒泡、选择和快排代码整理 /**************************************************************…

嵌入式一开始该怎么学?学习单片机

学习单片机&#xff1a; 模电数电肯定必须的&#xff0c;玩单片机大概率这两门课都学过&#xff0c;学过微机原理更好。 直接看野火的文档&#xff0c;芯片手册&#xff0c;外设手册。 学单片机不要纠结于某个型号&#xff0c;我认为stm32就OK&#xff0c;主要是原理和感觉。…

窥探系列之Mybatis-plus XML分页查询

mybatisPlus分页查询原理 searchCount字段控制是否查询总记录数 com.baomidou.mybatisplus.plugins.PaginationInterceptor 该插件拦截sql&#xff0c;如果searchCounttrue&#xff0c;则使用sql解析包jsqlparser根据原sql生成count语句&#xff0c;另外关键

分布式应用:Zookeeper 集群与kafka 集群部署

目录 一、理论 1.Zookeeper 2.部署 Zookeeper 集群 3.消息队列 4.Kafka 5.部署 kafka 集群 6.FilebeatKafkaELK 二、实验 1.Zookeeper 集群部署 2.kafka集群部署 3.FilebeatKafkaELK 三、问题 1.解压文件异常 2.kafka集群建立失败 3.启动 filebeat报错 4.VIM报错…

服务器数据恢复-raid5同步过程中又有一块磁盘报警的数据恢复案例

服务器数据恢复环境&#xff1a; 某研究院一台DELL存储&#xff0c;15块硬盘搭建的一组RAID5磁盘阵列。 该RAID5阵列只有一个卷组&#xff0c;该卷组占用了阵列的全部空间&#xff1b;该卷组只有一个起始位置为0扇区的XFS裸分区。 服务器故障&初检&分析&#xff1a; 该…

设计模式十:装饰器(Decorator)

现实生活中&#xff0c;常常需要对现有产品增加新的功能或美化其外观&#xff0c;如房子装修、相片加相框等&#xff0c;都是装饰器模式 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许在运行时为对象动态地添加额外的行为或功能…

Android前沿技术?Jetpack如何?

Jetpack Compose是Android开发领域的一项前沿技术&#xff0c;它提供了一种全新的方式来构建用户界面。近年来&#xff0c;Jetpack Compose在各大招聘等网站上的招聘岗位逐渐增多&#xff0c;薪资待遇也相应提高。本文将从招聘岗位的薪资与技术要求入手&#xff0c;分析Jetpack…

多线程的创建,复习匿名内部类,Thread的一些方法,以及lambda的变量捕捉,join用法

一、&#x1f49b; Java的Thread类表示线程 1.创建类&#xff0c;继承Thread重写run方法 2.创建类&#xff0c;实现Runnable重写run方法 3.可以继承Thread重写run基于匿名内部类 4.实现Runnable重写run基于匿名内部类 5.lamdba表达式表示run方法的内容&#xff08;推荐&#x…

适配器模式

**适配器模式&#xff08;Adapter Pattern&#xff09;**是一种结构型设计模式&#xff0c;它允许将一个类的接口转换成客户端所期望的另一个接口&#xff0c;从而使原本由于接口不兼容而无法一起工作的类能够一起工作。 适配器模式主要有三个角色&#xff1a; 目标接口&…

16-3_Qt 5.9 C++开发指南_使用QStyle 设置界面外观_实现不同系统下的界面效果的匹配

文章目录 1. QStyle的作用&#xff08;实现不同系统下的界面效果的匹配&#xff09;2. Qt内置样式的使用3. 源码3.1 可视化UI设计3.2 mainwindow.cpp 1. QStyle的作用&#xff08;实现不同系统下的界面效果的匹配&#xff09; Qt 是一个跨平台的类库&#xff0c;相同的界面组件…

算法与数据结构-跳表

文章目录 什么是跳表跳表的时间复杂度跳表的空间复杂度如何高效的插入和删除跳表索引动态更新代码示例 什么是跳表 对于一个单链表来讲&#xff0c;即便链表中存储的数据是有序的&#xff0c;如果我们要想在其中查找某个数据&#xff0c;也只能从头到尾遍历链表。这样查找效率…