JavaScript闭包解析

本文通过两个实例详细解析了JS闭包的工作原理,探讨了闭包的优缺点,包括变量保护、内存维护及性能影响,并提供了内存泄漏的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

说到JS闭包这个问题,想必面试过前端开发的同学多少都会遇到过。但就个人经历来讲,工作这么久还没有在公司项目的代码中看到谁写过闭包,或去写过闭包,当然我也不太想去写,一方面是没遇到这方面的需要,一方面是我怕给自己挖坑。但是当你了解过闭包后,就会明白面试官为什么会问你这个问题了,其实闭包还是包含很多关于js的底层东西,最起码对你以后奇怪代码问题的查找还是有一定的启发作用的。毕竟我们程序员每天的事情基本就是:“卧槽!!!为什么能运行?”、“卧槽!!!为什么不能运行?”。下面是我对闭包的理解,网上找了两个比较好且有助于记忆的例子:

代码一

let s = [];
function foo(){
    for(var i = 0; i < 3; i++){
        s[i] = function(){
                  console.log(i);
                }
    }
}
foo();//这里是倒数第4行
s[0]();//输出结果==>3
s[1]();//输出结果==>3
s[2]();//输出结果==>3

代码解析:
上面这段代码可以直接放到浏览器F12模式的console下面运行,你会惊奇的发现他的输出结果并不是0、1、2,而是3、3、3。下面解释一下,“为什么能运行?”而且还是这种结果,首先js代码运行的时候是单线程的,当程序运行到这段代码的倒数第4行foo()函数时,它做了这些事情:for循环里面代码,把一个简单的输出函数分别赋值给了数组变量s[0]、s[1]、s[2],并且输出函数里面使用到了for循环的i变量;所以,当程序接着往下运行时,这个时候输出的i值经过for的i++之后,已经变成了3,所以输出的结果是3、3、3,而不是0、1、2。注:如果你了解指针这个概念,那么也可以有助于你来理解这段代码,for循环在赋值给数组变量时,里面的i只是一个引用,指向了i变量在物理内存上的存储位置,所以在后面运行最后三行代码时,函数会从i变量所在的物理内存位置上拿取数值,但此时数值经过for的迭代已经变成了3,所以输出结果为3,在情理之中。至于输出结果为0、1、2,只是我们在写代码的时候“想当然”罢了。

代码二

let s = [];
function foo(){
    for(var i = 0; i < 3; i++){
        s[i] = function(index){
                    return function(){
                        console.log(index);
                        index = null;//释放index变量
                    }
                }(i)
    }
}
foo();
s[0]();//输出结果==>0
s[1]();//输出结果==>1
s[2]();//输出结果==>2

代码解析:
与代码一不同的是,我们在给数组变量赋值时,并没有把i变量直接使用,因为随着for循环的进行变量i的值会被改变,并不能达到我们想要的结果,所以声明了一个新变量index来保存那一时刻的i值,并且在输出的时候用一个函数来包裹保护它。有人可能会问,为什么要用一个函数来包裹它,这个是因为在JS代码里面在函数声明的时候加一个括号,表示声明后即运行,所以我们需要通过返回一个函数来保存这一状态值,这也是迭代器实现的原理。也需你还会问,到时候它运行的时候,index的值不会被释放吗?答案是,不会。这个问题的解释稍微涉及到js内存管理机制,在这里简答解释一下,我们假如index变量在声明的时候,我们把它的引用计数看成是1,正常情况下,在函数运行完成后index变量的引用计数减1,变成0,然后被js内存管理机制发现给回收掉,因为它没有地方引用了。但是,我们在return函数里面引用到了它,会使index变量的引用计数再次加1,变成2,所以闭包的外层函数运行结束后,index变量的引用计数减去1,还是为1,所以不会被js内存管理机制给回收掉(它会回收引用计数为0的变量,释放其内存)。那么这个时候,可能又要有人问了,那index变量占用的内存什么时候去释放?答案是,需要程序员在使用完毕后,手动释放,将index变量重置为null,否则将造成内存泄漏。

总结

可能有些同学在读完这篇文章后,感觉对于闭包的理解有了一些,但又感觉没有把握住闭包的轮廓,写不出一个闭包函数。那么下面的总结希望能帮助你清晰这个轮廓:
闭包的特点:
函数嵌套函数,外层函数以内层函数作为返回值,且内层函数访问了外部函数的变量。
闭包好处:
1、保护函数内的变量安全,实现封装,同时也可以防止命名冲突。
2、内存中可以始终维持着一个变量不会释放,可以做缓存来用。(但使用多了会变成缺点,太消耗内存)
3、匿名自执行函数可以减少内存消耗。
闭包的坏处:
1、被内层函数引用的变量被私有化,不能被销毁释放,增大了内存开销,造成了内存泄漏。解决方法是:在使用完后手动释放,将它赋值为null。
2、由于闭包的跨域访问,会使性能方面有所损失。解决方法是:我们可以通过将需要访问的跨域变量,通过传参的方式,存储在函数内部,通过直接访问局部变量,来减轻对执行速度的影响。

参考文档:
https://www.cnblogs.com/itjeff/p/10106855.html
https://www.jianshu.com/p/17c5e91b0a87

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值