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

闭包的简单讲解

什么是闭包

要理解闭包,首先来看下下面这段代码:

var count = 1;function add(){count++;console.log(count);
}add(); // 2
add(); // 3
add(); // 4

可以看到这里调用了3次函数,count的值也从1增长到了4,但是这么写会导致全局变量被污染,所以将count的定义移动到add函数内部,代码如下:

function add() {var count = 1;count++;console.log(count);
}add(); // 2
add(); // 2
add(); // 2

但是这又导致了另一个问题,变为局部变量的count不会自增了,所以那么就可以利用闭包的这个特性将每次调用时的count保存起来这样就可以实现变量的自增了,代码如下:

function add() {var count = 1;return function(){count++;console.log(count);}
}let getCount= add();
getCount(); // 2
getCount(); // 3
getCount(); // 4

可以这样理解,通过将add函数赋值给getCount这个变量,可以看作如下代码

let getCount= function(){count++;console.log(count);
}

每当调用getCount()函数的时候,首先要获取count变量,因为JavaScript中存在作用域链的关系,所以会从add函数下得到对应的count,因为闭包存在着闭包可以访问到父级函数的变量,且该变量不会销毁的特性所以上次的变量会被保留下来,所以可以做到自增的实现。

闭包的特性

根据以上对闭包的讲解,我们可以总结出闭包的特性

  1. 闭包可以访问到父级函数的变量

  2. 访问到父级函数的变量不会销毁

闭包的用途

1.封装私有变量

闭包可以用于封装私有变量,以防止其被外部访问和修改。封装私有变量可以一定程度上防止全局变量污染,使用闭包封装私有变量可以将这些变量限制在函数内部或模块内部,从而减少了全局变量的数量,降低了全局变量被误用或意外修改的风险。

function add(){let count = 0function a(){count++console.log(count);}return a
}
var res = add() 
res() //1 
res() //2
res() //3

在上面的代码示例中,add函数返回了一个闭包a,其中包含了count变量。由于count只在add函数内部定义,因此外部无法直接访问它。但是,由于a函数引用了count变量,因此count变量的值可以在闭包内部被修改和访问。这种方式可以用于封装一些私有的数据和逻辑。

2.做缓存

函数一旦被执行完毕,其内存就会被销毁,而闭包的存在,就可以保有内部环境的作用域。

function foo(){var myName ='张三'let test1 = 1const test2 = 2 var innerBar={getName: function(){console.log(test1);return myName},setName:function(newName){myName = newName}}return innerBar
}
var bar = foo()   
console.log(bar.getName()); //输出:1 张三
bar.setName('李四')
console.log(bar.getName()); //输出:1 李四

这里var bar = foo() 执行完后本来应该被销毁,但是因为形成了闭包,所以导致foo执行上下文没有被销毁干净,被引用了的变量myName、test1没被销毁,闭包里存放的就是变量myName、test1,这个闭包就像是setName、getName的专属背包,setName、getName依然可以使用foo执行上下文中的test1和myName。

闭包的缺点

闭包会导致变量不会被垃圾回收机制回收,造成内存消耗以及对于不恰当的使用闭包可能会造成内存泄漏的问题

如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响。

内存泄漏的解决方案

this.name = 'WindowName'
let myObj = {name: 'beast senpai',get: function(){return function(){console.log(this); // WindowNamereturn this.name;}}
}let myObjname = myObj.get()();//先调用 myObj.get(),再调用它返回的结果
console.log(myObjname); // WindowName

这里发生了内存泄漏使得this指向了Window对象

解决方案1:在get函数使用that保存此时的this

this.name = 'WindowName'
let myObj = {name: 'beast senpai',get: function(){let that = this;return function(){console.log(that); // myObjreturn that.name;}}
}let myObjname = myObj.get()();
console.log(myObjname); // beast senpai

解决方案2:将get函数的返回值改回使用箭头函数的方式做返回

this.name = 'WindowName'
let myObj = {name: 'beast senpai',get: function(){return ()=>{console.log(this); // myObjreturn this.name; }}
}let myObjname = myObj.get()();
console.log(myObjname); // beast senpai

消除闭包

不用的时候解除引用,避免不必要的内存占用

取消fn对外部成员变量的引用,就可以回收相应的内存空间。

function add() {var count = 0return function fn() {count++console.log(count)}
}var a = add() // 产生了闭包
a() // 1
a() // 2
a = null // 取消 a 与 fn 的联系,这个时候浏览器回收机制就能回收闭包空间

参考

JavaScript | 闭包

什么是闭包?(详解闭包)

http://www.xdnf.cn/news/19746.html

相关文章:

  • LeetCode 19: 删除链表的倒数第 N 个结点
  • 捡捡java——4、日志
  • 数据结构:单链表的应用(力扣算法题)第二章
  • MJ Prompt Tool-好用的Midjourney提示词工具
  • 如何测试瞬态电压抑制二极管性能是否达标?-ASIM阿赛姆
  • 同源策略--跨域
  • 盟接之桥说制造:浅谈本分和做正确的事情
  • HBase实战(一)
  • MFC应用防止多开
  • OpenCV 4.1.1 编译错误解决方案(cudaoptflow.hpp not found)
  • Day20 API
  • 数据血缘中的图数据库如何选择
  • Qt UDP 网络编程详解
  • 【学Python自动化】5.1 Python 与 Rust 数据结构对比学习笔记
  • (Arxiv-2025)VACE:一体化视频创作与编辑
  • (纯新手教学)计算机视觉(opencv)实战十一——轮廓近似(cv2.approxPolyDP)
  • C++实时视频抽帧抓图功能(附源码)
  • 几种特殊的数字滤波器---原理及设计
  • 基于springboot生鲜交易系统源码和论文
  • Beego: Go Web Framework 详细指南
  • Eclipse使用教程_自用
  • vite基础讲解
  • 【C++】C++14新特性
  • Jenkins大总结 20250901
  • Abaqus后处理常见问题汇总
  • python生成器与协程深度剖析
  • 腾讯位置商业授权微信小程序获取城市列表
  • 数据分析编程第八步:文本处理
  • flex布局order改变排列顺序
  • 前沿科技竞速:脑机接口、AI芯片与半导体生态上的新突破