面试题答案
一键面试闭包对内存管理的作用
- 延长变量生命周期:在 JavaScript 中,函数执行完毕后,其作用域链会被销毁,局部变量也会被垃圾回收机制回收。但闭包可以使函数内部的变量在函数执行完毕后依然存活,因为闭包会保持对该变量所在作用域的引用。例如:
function outer() {
let localVar = 10;
function inner() {
return localVar;
}
return inner;
}
let closureFunc = outer();
console.log(closureFunc()); // 10
这里 outer
函数执行完毕后,localVar
没有被回收,是因为 inner
函数(闭包)保持了对 localVar
所在作用域的引用。
- 数据封装与保护:闭包可以用来实现数据的封装和保护,将一些变量和函数隐藏在内部,只暴露必要的接口。这样,外部代码无法直接访问内部变量,从而提高代码的安全性和可维护性。例如:
function counter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
getCount: function() {
return count;
}
};
}
let myCounter = counter();
console.log(myCounter.increment()); // 1
console.log(myCounter.getCount()); // 1
这里 count
变量被封装在 counter
函数内部,通过闭包暴露的 increment
和 getCount
方法来操作和访问 count
,外部无法直接修改 count
。
闭包可能导致的内存问题及解决办法
- 内存问题:闭包可能会导致内存泄漏,特别是在循环中创建闭包时,如果不小心处理,可能会使大量对象无法被垃圾回收。例如:
function createClosures() {
let arr = [];
for (let i = 0; i < 10; i++) {
arr.push(function() {
return i;
});
}
return arr;
}
let closures = createClosures();
// 这里由于闭包对外部变量 i 的引用,即使循环结束,i 也不会被回收
- 解决办法:
- 使用块级作用域:在 ES6 中,可以使用
let
关键字,let
会在每次循环迭代时创建一个新的块级作用域,从而避免闭包共享同一个变量。例如:
- 使用块级作用域:在 ES6 中,可以使用
function createClosures() {
let arr = [];
for (let i = 0; i < 10; i++) {
(function(j) {
arr.push(function() {
return j;
});
})(i);
}
return arr;
}
let closures = createClosures();
- **及时释放引用**:当不再需要闭包时,手动将其引用设为 `null`,让垃圾回收机制可以回收相关内存。例如:
let closureFunc = outer();
// 使用完闭包后
closureFunc = null;