什么是闭包和作用域链?

1. 什么是闭包

闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。

举个栗子,createCounter 接受一个参数 n,然后返回一个匿名函数,这个匿名函数是闭包,它可以访问外部函数 createCounter 的局部变量 n。因为这个内部函数在外部有被引用,该函数会不会被销毁,n的值也会被保存。

    function createCounter(n) {return function () {return n++};};const a = createCounter(1)a()//1a()//2
    function createCounter2() {let n = 1return function increment() {console.log(++n)};};const b = createCounter2()b()//2b()//3

return返回的函数那个函数外部有引用,才会被保存;而show函数每次被调用都会重新被加载。

    function createCounter3() {return function () {let n = 1function show(){console.log(++n)}show()};};
const c = createCounter3() c()//2c()//2
    function createCounter4() {return function () {let n = 1return function show(){console.log(++n)}};};const d = createCounter4()()//show函数外部有引用d()//2d()//3

2. 作用域链

理解作用域链的创建和使用,对理解闭包非常重要。

在调用一个函数时,会为这个函数调用创建一个执行上下文,并创建一个作用域链

function compare(value1, value2) { if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } 
} 
let result = compare(5, 10); 

这里定义的 compare()函数是在全局上下文中调用的。

定义函数时,就会为它创建作用域链,预装载全局变量对象,并保存在内部的[[Scope]]中。在调用这个函数时,会创建相应的执行上下文,然后通过复制函数的[[Scope]]来创建其作用域链。

第一次调用 compare()时,会为它创建一个包含 argumentsvalue1value2活动对象,这个对象是其作用域链上的第一个对象。什么是arguments对象?

compare()作用域链上的第二个对象是全局上下文的变量对象,其中包含 this、result 和 compare。
在这里插入图片描述
全局上下文中的叫变量对象,它会在代码执行期间始终存在。比如在浏览器中是 window 对象。

函数局部上下文中的叫活动对象,只在函数执行期间存在。当函数执行完毕后,活动对象会被销毁。

函数内部代码在访问变量时,会从作用域链中查找变量。函数执行完毕后,局部活动对象会被销毁内存中就只剩下全局作用域。不过,闭包就不一样了。

在一个函数内部定义的函数会把其包含函数的活动对象添加到自己的作用域链中。
因此,在createCounter()函数中,匿名函数的作用域链中实际上包含createCounter的活动对象(也就是arguments和它的形参),所以在内部的函数可以访问到外部的参数。因为匿名函数中有对n的引用,所以执行完毕后createCounter不会被销毁。

    function createCounter(n) {return function () {return n++};};//1.创建函数const a = createCounter(1)//2.调用函数a()//1 a()//2//3.除对函数的引用,这样就可以释放内存了a = null

创建的createCounter 函数被保存在变量 a 中。把 a设置为等于 null 会解除对函数的引用,从而让垃圾回收程序可以将内存释放掉,作用域链也会被销毁。

3. 闭包的优缺点

闭包的优点

1. 封装性

闭包允许创建私有变量,这对于封装和隐藏实现细节非常有用。通过在函数内部定义变量,并返回一个访问这个变量的函数,可以创建一个私有作用域。

function createCounter() {let count = 0;return function() {count++;return count;};
}const counter = createCounter();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2

2. 模块化开发

闭包可用于实现模块化开发,创建私有作用域,防止变量污染全局命名空间。

const module = (function() {let privateVariable = 'I am private';return {getPrivateVariable: function() {return privateVariable;},setPrivateVariable: function(value) {privateVariable = value;}};
})();console.log(module.getPrivateVariable()); // 输出 'I am private'
module.setPrivateVariable('Updated value');
console.log(module.getPrivateVariable()); // 输出 'Updated value'

3. 事件处理程序

在事件处理程序中,闭包可以用来维持对外部作用域的引用,以便在事件触发时访问外部变量。

function setupEventListener() {let count = 0;document.getElementById('myButton').addEventListener('click', function() {count++;console.log(`Button clicked ${count} times`);});
}setupEventListener();

4. setTimeout 和 setInterval

在定时器中使用闭包,可以保存定时器内的变量状态,而不受外部环境的影响。

function startTimer() {let seconds = 0;const timer = setInterval(function() {seconds++;console.log(`Timer: ${seconds} seconds`);}, 1000);return function stopTimer() {clearInterval(timer);};
}const stopTimer = startTimer();
// ...一些代码后
stopTimer(); // 停止定时器

5. 在循环中使用闭包

在循环中使用闭包,可以保持对每次迭代的独立作用域,防止变量共享问题。

for (let i = 1; i <= 5; i++) {setTimeout(function() {console.log(`Delayed log: ${i}`);}, i * 1000);
}

6. 缓存函数结果

可以实现对函数调用结果的缓存,提高性能。

function memoize(fn) {const cache = {};return function(...args) {const key = JSON.stringify(args);if (cache[key]) {console.log('Result retrieved from cache');return cache[key];} else {const result = fn(...args);cache[key] = result;return result;}};
}const memoizedAdd = memoize(function(x, y) {console.log('Performing expensive calculation');return x + y;
});console.log(memoizedAdd(2, 3)); // 输出 'Performing expensive calculation' 和 5
console.log(memoizedAdd(2, 3)); // 输出 'Result retrieved from cache' 和 5

闭包的缺点

  1. 内存占用:
    • 闭包可能导致内存泄漏,因为闭包中的函数引用了外部函数的变量,导致外部函数的作用域无法被垃圾回收
  2. 性能影响:
    • 闭包的使用可能对性能产生一些影响,尤其是在涉及大量闭包的场景中。每个闭包都会创建一个新的作用域链,可能会导致额外的计算成本。
  3. 复杂性:
    • 过度使用闭包可能导致代码复杂性增加,降低代码的可读性和可维护性。在某些情况下,闭包的嵌套可能会变得难以理解。
  4. 变量共享问题:
    • 闭包可以访问外部函数的变量,这可能导致变量共享的问题。在循环中创建闭包时,需要注意变量在每次迭代中的值。
  5. 潜在的安全问题:
    • 如果闭包中的函数依赖于外部作用域的变量,并且这些变量在函数调用时可能被修改,可能会导致潜在的安全问题。

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

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

相关文章

智能优化算法应用:基于鲸鱼算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鲸鱼算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鲸鱼算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鲸鱼算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

Nginx常见的中间件漏洞

目录 1、Nginx文件名逻辑漏洞 2、Nginx解析漏洞 3、Nginx越权读取缓存漏洞 这里需要的漏洞环境可以看&#xff1a;Nginx 配置错误导致的漏洞-CSDN博客 1、Nginx文件名逻辑漏洞 该漏洞利用条件有两个&#xff1a; Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7 php-fpm.conf中的s…

百度 Comate 终于支持 IntelliJ IDEA 了

大家好&#xff0c;我是伍六七。 对于一直关注 AI 编程的阿七来说&#xff0c;编程助手绝对是必不可少的&#xff0c;除了 GitHub Copilot 之外&#xff0c;国内百度的 Comate 一直是我关注的重点。 但是之前&#xff0c;Comate 还支持 VS code&#xff0c;并不支持 IntelliJ…

mybatis的使用,mybatis的实现原理,mybatis的优缺点,MyBatis缓存,MyBatis运行的原理,MyBatis的编写方式

文章目录 MyBatis简介结构图Mybatis缓存&#xff08;一级缓存、二级缓存&#xff09;MyBatis是什么&#xff1f;mybatis的实现原理JDBC编程有哪些不足之处&#xff0c;MyBatis是如何解决这些问题的&#xff1f;Mybatis优缺点优点缺点映射关系 MyBatis的解析和运行原理MyBatis的…

【单片机学习笔记】STC8H1K08参考手册学习笔记

STC8H1K08参考手册学习笔记 STC8H系列芯片STC8H1K08开发环境串口烧录 STC8H系列芯片 STC8H 系列单片机是不需要外部晶振和外部复位的单片机&#xff0c;是以超强抗干扰/超低价/高速/低功耗为目标的 8051 单片机,在相同的工作频率下,STC8H 系列单片机比传统的 8051约快12 倍速度…

数组题目:645. 错误的集合、 697. 数组的度、 448. 找到所有数组中消失的数字、442. 数组中重复的数据 、41. 缺失的第一个正数

645. 错误的集合 思路&#xff1a; 我们定义一个数组cnt&#xff0c;记录每个数出现的次数。然后我们遍历数组&#xff0c;从1开始&#xff0c;如果cnt[i] 0 那就说明这个是错误的数&#xff0c;如果 cnt[i] 2&#xff0c;那就说明是重复的数。 代码&#xff1a; class So…

RabbitMQ之消费者可靠性

文章目录 前言一、消费者确认机制二、失败重试机制三、失败处理策略四、业务幂等性唯一消息ID业务判断 五、兜底方案总结 前言 当RabbitMQ向消费者投递消息以后&#xff0c;需要知道消费者的处理状态如何。因为消息投递给消费者并不代表就一定被正确消费了&#xff0c;可能出现…

3DMAX平滑布尔插件超级布尔工具使用教程

3dMax平滑布尔插件SmoothBoolean用于在ProBoolean网格之间创建平滑过渡的3dMax插件。再也不需要花几个小时来清理用布尔切割创建的混乱拓扑了。如果你只是想让你的三维模型在渲染图上看起来很棒&#xff0c;或者你需要为游戏烘焙一些东西&#xff0c;那么你就不必浪费时间来建模…

Kotlin学习——kt中的类,数据类 枚举类 密封类,以及对象

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…

个人硬件测试用例入门设计

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f324;️功能测试 进行新增、…

Java中绕过SSL/TLS验证:开发与风险透视

警告: 本文提供的方法绕过SSL/TLS证书验证&#xff0c;这在某些开发场景下可能是有用的&#xff0c;但使用这些方法会导致严重的安全隐患。在生产环境中&#xff0c;你应该始终验证SSL/TLS证书以确保数据的安全传输。 引言 在日常的软件开发中&#xff0c;我们经常需要与其他服…

FFmpeg命令分隔视频

有一个视频如a.mp4&#xff0c;此视频采用帧率为30生成&#xff0c;共有299帧&#xff0c;这里通过FFmpeg命令分隔成1秒一个个的小视频&#xff0c;即每个小视频帧数为30帧。 用到的FFmpeg参数如下所示&#xff1a; (1).-i:指定输入视频文件的名称&#xff1b; (2).-c:指…

name 属性:提高 Vue 应用可维护性的关键

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

对称加密与非对称加密的区别是什么?

对称加密与非对称加密的区别是什么&#xff1f; 对称加密概念&#xff1a;好处和坏处&#xff1a;基本原理 非对称加密概念&#xff1a;工作原理&#xff1a; 两者区别安全性处理速度密钥管理通信双方数量 对称加密 概念&#xff1a; 同一个密钥可以同时用来对信息进行加密和…

Sublime Text 3运行 Python文件出现中文打印乱码的解决方式

很多小伙伴在下载安装好sublime这个编辑器后发现&#xff0c;它虽然能够用来打开python脚本和创建文件编写代码&#xff0c;但是却不能够来运行python代码和程序。所以下面这一篇文章就是会来分享一下&#xff0c;sublime编辑器无法运行python的解决方法&#xff0c;感兴趣的话…

Linux环境下自动化创建大量的账号

参考《鸟哥的Linux私房菜基础篇第四版》13.7.2节微调而成&#xff1a; 下面脚本的目的是为服务器的管理员自动化创建大量的账号&#xff0c;节省生命。 #!/bin/bash # This shell script will create amount of Linux login accounts for you. # 1. check the "accounta…

2006-2023年2月地级市城投债数据

2006-2023年2月地级市城投债数据 1、时间&#xff1a;2006-2023年2月 2、指标&#xff1a;省份、城市、证券代码、证券简称、债券简称、证券全称、债券初始面值单位元、债券最新面值交易日期20221231、发行总额单位亿元、债券余额日期20221231单位亿、起息日期、计息截止日、…

React中通过children prop或者React.memo来优化子组件渲染【react性能优化】

文章目录 前言未优化之前的代码问题解决方案一&#xff0c;通过children prop解决方案二&#xff0c;通过React.memo后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;react.js &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和…

基于PyQT5的图像分类网络训练平台

1.主界面 2.选择数据集路径 里面包含两个文件夹 train和val 3.选择类别标签 以txt为结尾 4.训练基本设置 包括输入图像大小、batch size、轮次、学习率等 5.训练高级设置 是否进行标签平滑、图像增强操作 6.选择训练日志输出地址 为一个文件夹 7.选择训练好的模…

1.如何修改seruat对象的行名 2.FeaturePlot如何把所有阳性表达的spot放到图的前面

本有解决标题中的两个问题 1.答案是修改不了&#xff0c;不如重新制作一个seurat对象。 试图使用rownames&#xff08;obj&#xff09;featurenames是不成功的 记录 客户需求遇到一个问题&#xff1a;作者提供的rds文件行名为ensemble id&#xff0c;如何改成gene symbol。 …