当前位置: 首页 > news >正文

JavaScript中闭包详解+举例,闭包的各种实践场景:高级技巧与实用指南

目录

闭包的各种实践场景:高级技巧与实用指南

一、什么是闭包?

1、闭包的基本概念

2、闭包的工作原理

3、闭包的用途

二、闭包的实际应用场景

1、模拟私有变量

2、事件处理和回调函数

3、延迟函数和异步操作

4、柯里化

5、备忘录模式(Memoization)

三、闭包的性能问题

1、内存泄漏

四、最佳实践与建议

五、总结


作者:watermelo37

涉及领域:Vue、SpingBoot、Docker、LLM、python等

-----------------------------------------------------------------------------------

-------温柔地对待温柔的人,包容的三观就是最大的温柔。-------

-----------------------------------------------------------------------------------

闭包的各种实践场景:高级技巧与实用指南

        闭包在很多现代编程语言中都存在。常见支持闭包的语言有 JavaScript、Python、Ruby、Swift、Kotlin、Scala 等。本文将着重讲在JavaScript中闭包的常见用法及实操意义。

        在JavaScript中,闭包(Closure)是一个非常重要的概念,几乎贯穿于日常开发的方方面面。尽管它强大且用途广泛,但对于初学者而言,理解闭包的原理和实际应用常常是一个挑战。在这篇文章中,将通过理论与实践相结合的方式,帮助你全面理解闭包的本质、原理及其在实际项目中的应用。

一、什么是闭包?

1、闭包的基本概念

        闭包可以简单理解为:一个函数和其词法环境的组合。当一个函数能够记住并访问其词法作用域,即使函数在其词法作用域之外执行,这个函数就被称为闭包。

        在JavaScript中,函数在创建时会形成一个包含函数内部变量和外部环境的闭包。这意味着,闭包可以“记住”其创建时的上下文,并能在稍后调用时访问这些变量。

2、闭包的工作原理

        要理解闭包的工作原理,我们首先需要理解JavaScript的执行上下文和作用域链。当一个函数在另一个函数内部被定义时,它会包含对外部函数变量的引用。这些引用在外部函数执行完毕后不会被销毁,而是被闭包所保留。

function outerFunction() {let outerVariable = 'I am from outer scope';function innerFunction() {console.log(outerVariable);}return innerFunction;
}const closure = outerFunction();
closure();  // 输出: I am from outer scope

        在上面的例子中,innerFunction保留了对outerVariable的访问权,即使outerFunction已经执行完毕。这就是闭包的基本特性。

3、闭包的用途

  • 数据封装和私有化(模拟私有变量)
  • 维持变量初始状态
  • 柯里化(Currying)
  • 函数工厂(创建函数的函数)

二、闭包的实际应用场景

        闭包不仅仅是一个理论概念,在实际开发中它有很多重要的应用场景。

1、模拟私有变量

        JavaScript中没有私有变量的概念,但通过闭包可以模拟出类似私有变量的效果。

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

        在这个例子中,count变量无法直接从外部访问,只能通过increment和decrement方法进行操作。这种方式在编写模块化代码时尤为有用。

2、事件处理和回调函数

        闭包在事件处理和回调函数中非常常见,尤其是在需要保持状态或访问外部变量的情况下。

function setupClickHandlers() {const buttons = document.querySelectorAll('button');for (let i = 0; i < buttons.length; i++) {buttons[i].addEventListener('click', function() {console.log('Button ' + i + ' clicked');});}
}setupClickHandlers();

        每个按钮点击时,都会输出其对应的索引。这是因为闭包“记住”了当时的 i 值。

3、延迟函数和异步操作

        闭包在处理延迟函数或异步操作时也非常有用。

for (var i = 1; i <= 5; i++) {setTimeout(function() {console.log(i);}, i * 1000);
}// 通过闭包解决变量提升问题
for (var i = 1; i <= 5; i++) {(function(j) {setTimeout(function() {console.log(j);}, j * 1000);})(i);
}

        在第一个例子中,所有的setTimeout函数都会输出5,因为它们共享了同一个i变量。而在第二个例子中,通过闭包创建了新的作用域,每个setTimeout函数都有自己的j变量,从而正确输出1到5。

4、柯里化

        柯里化(Currying)是一种将接受多个参数的函数转换成一系列接受单个参数的函数的技术。这种技术以数学家哈斯凯尔·柯里(Haskell Curry)的名字命名。在柯里化过程中,原函数被转换成一个函数,这个函数接受一个参数并返回一个新的函数,后者再次接受下一个参数,依此类推,直到收集完所有需要的参数。

function add(x) {return function(y) {return x + y;};
}const addFive = add(5);
console.log(addFive(3)); // 输出 8

        在这个例子中,add 函数返回一个新的函数,它记住了 x 的值,并接受另一个参数 y 来完成加法。

5、备忘录模式(Memoization)

        备忘录模式特别适用于递归算法,如计算斐波那契数列,或者任何重复调用相同参数的递归函数。以下是使用闭包实现备忘录模式的一个例子。

function memoize(fn) {const cache = {};return function(...args) {const key = args.toString(); // 将参数列表转换为字符串作为缓存键if (key in cache) {return cache[key];} else {const result = fn.apply(this, args);cache[key] = result;return result;}};
}const fibonacci = (n) => {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);
};const memoizedFibonacci = memoize(fibonacci);console.log(memoizedFibonacci(10)); // 使用备忘录快速计算

        在这个例子中,memoize 函数是一个高阶函数,它接收一个函数 fn 作为参数,并返回一个新的函数。新函数会检查传入的参数是否已经在缓存 cache 中,如果是,则直接返回缓存的结果;如果不是,则调用原始函数 fn,将结果存入缓存,并返回结果。

三、闭包的性能问题

        虽然闭包功能强大,但滥用闭包可能会带来性能问题。每次创建闭包时,都会在内存中保留一份变量的引用,过多的闭包可能导致内存泄漏。因此,在编写代码时应谨慎使用闭包,避免在不必要的场合创建闭包。

1、内存泄漏

        不当的闭包使用会导致内存泄漏,特别是在循环中创建大量闭包时。如果闭包引用了不再需要的变量或对象,内存将无法释放。

function leakyFunction() {let largeData = new Array(1000000).join('*');return function() {console.log(largeData.length);};
}const leaky = leakyFunction();
// largeData 仍然保存在内存中,即使它不再被需要

        在上述例子中,largeData占用了大量内存,即使在函数执行完毕后也不会被释放。因此,尽量避免在闭包中引用不必要的变量。

四、最佳实践与建议

        为了更好地使用闭包,以下是一些最佳实践和建议:

  1. 控制闭包的数量:不要在不必要的场合创建闭包,尤其是在高频调用的函数中。

  2. 释放不再需要的变量:如果闭包引用了大量数据,应当在适当时候释放这些数据,以避免内存泄漏。

  3. 使用let或const代替var:在使用闭包时,let或const声明的变量具有块级作用域,能更好地避免变量提升问题。

  4. 理解作用域链:深入理解JavaScript的作用域链和执行上下文,有助于更好地掌握闭包的使用。

五、总结

        闭包是JavaScript中不可或缺的部分,它不仅可以增强代码的可维护性,还能在模块化、回调处理等场景中发挥巨大作用。然而,闭包的强大也意味着需要谨慎使用,避免潜在的性能问题和内存泄漏。通过对闭包原理的深入理解以及在实际项目中的灵活应用,你将能够更加高效地编写出简洁且功能强大的代码。

        只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

        更多优质内容,请关注:

        你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

        通过array.filter()实现数组的数据筛选、数据清洗和链式调用

        el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

        极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

        shpfile转GeoJSON且控制转化精度;如何获取GeoJSON?GeoJson结构详解

        Docker 入门全攻略:安装、操作与常用命令指南

        通过array.reduce()实现数据汇总、条件筛选和映射、对象属性的扁平化、转换数据格式等

        巧用Array.forEach:简化循环与增强代码可读性

        Mapbox添加行政区矢量图层、分级设色图层、自定义鼠标悬浮框、添加天地图底图等

        管理数据必备!侦听器watch用法详解

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 4.Redis单线程和多线程
  • Hiprint 打印插件在 Vue3 中的深度剖析
  • Linux磁盘操作之du命令
  • 【Nature】在科研中应用ChatGPT:如何与数据对话
  • “解锁进程间高效沟通,Linux IPC是你的关键钥匙!“#Linux系统编程之进程间通信【下】
  • 一篇内容带你了解Rabbitmq
  • [Leetcode 105][Medium] 从前序与中序遍历序列构造二叉树-递归
  • 接口如何设计
  • 2-76 基于matlab的加权平均融合算法
  • sheng的学习笔记-AI-半监督学习
  • Kubernetes中的Kube-proxy:服务发现与负载均衡的基石
  • Java—双列集合
  • 数据库管理-第234期 2024DTCC,一场数据库盛宴(20240826)
  • debian12 - systemctl 根据状态值判断服务启动成功的依据
  • 机器学习第五十三周周报 MAG
  • “大数据应用场景”之隔壁老王(连载四)
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • dva中组件的懒加载
  • exif信息对照
  • java取消线程实例
  • JS字符串转数字方法总结
  • Map集合、散列表、红黑树介绍
  • mockjs让前端开发独立于后端
  • Next.js之基础概念(二)
  • Python3爬取英雄联盟英雄皮肤大图
  • Twitter赢在开放,三年创造奇迹
  • windows下使用nginx调试简介
  • 半理解系列--Promise的进化史
  • 大主子表关联的性能优化方法
  • 关于for循环的简单归纳
  • 将回调地狱按在地上摩擦的Promise
  • 理解在java “”i=i++;”所发生的事情
  • 码农张的Bug人生 - 见面之礼
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 译有关态射的一切
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • 第二十章:异步和文件I/O.(二十三)
  • 正则表达式-基础知识Review
  • ​低代码平台的核心价值与优势
  • #{}和${}的区别是什么 -- java面试
  • $().each和$.each的区别
  • (06)金属布线——为半导体注入生命的连接
  • (3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (附源码)ssm高校实验室 毕业设计 800008
  • (回溯) LeetCode 46. 全排列
  • (南京观海微电子)——I3C协议介绍
  • (十一)手动添加用户和文件的特殊权限
  • (四)c52学习之旅-流水LED灯
  • (学习日记)2024.02.29:UCOSIII第二节
  • (转)Mysql的优化设置
  • (转)ObjectiveC 深浅拷贝学习