【前端进阶】浏览器的垃圾回收机制及JS中的闭包
浏览器的垃圾回收机制(内存释放机制)
栈内存释放
- 加载页面时,浏览器会形成一个全局上下文,只有页面关闭后,全局上下文才会被释放
- 函数执行时,会形成一个私有上下文进栈执行, 当函数中代码执行完毕,大部分情况下,私有上下文会出栈释放,以此优化栈内存
堆内存释放
- 查找引用(谷歌):浏览器在空闲时或指定时间内,查看所有堆内存,把没有被占用的堆内存释放
- 引用计数(IE):创建了堆内存,被占用时浏览器计数+1,取消占用时浏览器计数-1,当计数为0时,把堆内存释放;某些情况下会导致计数混乱,出现内存泄漏的情况
闭包
原理
函数运行的一种机制
- 函数执行会生成一个是私有上下文
- 里面的私有变量,受到了私有上下文的“保护”,不受外界干扰
- 当函数内部值被其外部占用时,形成不被释放的私有上下文,里面的私有变量和一些值就会被“保存”起来,这些值可被其“下级”上下文调取使用
- 我们把函数的这种“保护”/“保存”机制称为“闭包”
- 弊端:如果大量使用闭包,会导致栈内存太大,页面渲染变慢,性能受到影响;某些代码会导致内存泄漏(例如死递归),因此要合理使用闭包
// fn内部函数f被外部变量f占用,导致fn执行完毕无法出栈释放,形成闭包
function fn(){
let x = 10;
return function f(){
console.log('闭包')
console.log(x)
}
}
let f = fn();
f()
// 死递归 Uncaught RangeError: Maximum call stack size exceeded(内存泄漏)
function die(x){
die(x+1)
}
die()
闭包的应用场景
循环事件绑定(var)
// 由于i为全局变量,所以循环时i的值已变更,当点击button时,打印出的值位i最终的值
var btns = document.querySelectorAll('button')
for(var i = 0;i < btns.length; i++){
btns[i].onClick = function(){
console.log(i) // btns.length
}
}
// 解决方案1:基于闭包机制
var btns = document.querySelectorAll('button')
for (var i = 0; i < btns.length; i++) {
// 每一轮循环都会形成一个闭包,存储私有变量i的值
(function(i){
btns[i].onclick = function(){
console.log(i)
}
})(i)
}
// 或者
for (var i = 0;i < btns.length; i++) {
// 每一轮循环都会形成一个闭包,存储私有变量i的值
btns[i].onclick = (function(i){
return function(){
console.log(i)
}
})