一、什么是闭包
闭包就是一个函数,能够访问其他函数内部变量的函数。
详细来说,闭包就是在一个函数A内部定义一个新的函数B,并且这个新函数B,调用了在函数A内定义的变量,并且在函数A外部被调用,这就形成了闭包。
示例:
function outer() {
var a = '变量1'
var inner = function () {
console.info(a)
}
return inner
// inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
var inner = outer() // 获得inner闭包函数
inner() //"变量1"
当程序执行完var inner = outer(),其实outer的执行环境并没有被销毁,因为他里面的变量a仍然被inner函数作用域链所引用,当程序执行完inner(), 这时候,inner和outer的执行环境才会被销毁调;
二、闭包的优缺点
优点
-
访问其他函数内部变量
闭包可以访问定义时所处的词法作用域中的变量,即使外部函数已经执行完毕。
-
延长变量的生命周期
变量长期驻扎在内存中,不会被内存回收机制回收
-
保护变量
通过闭包,可以创建私有变量,只能通过内部函数访问,外部无法直接访问或修改(只能通过特定的get、set方法进行获取或修改) ,可以避免定义全局变量所造成的污染
缺点
- 常驻内存,增加内存使用量
- 使用不当造成内存泄漏
示例:
变量私有化
function getName() {
let name = '小花';
function get(){
return name;
}
function set(newName){
name = newName
}
return {
get:get,
set:set
}
}
let obj = getName() // 打印出get、set两函数
let myName = getName.get(); // '小花'
getName.set('小麦'); // 修改name为小麦
三、内存泄漏(内存浪费)
内存泄漏是指在程序运行时,分配的内存没有被正确释放,导致内存空间的浪费,最终可能会使程序崩溃或运行缓慢。
四、如何避免闭包引起的内存泄漏
-
避免滥用闭包
过多的闭包会导致代码难以理解和维护,同时也会增加内存开销。
-
及时释放对闭包的引用
在不再需要闭包时,应该及时将闭包的引用释放掉,以便垃圾回收器回收内存。
-
避免闭包中的循环引用
当闭包中包含循环引用时,会导致内存泄漏
-
使用缓存机制
在一些情况下,可以使用缓存机制来避免重复创建闭包。例如,可以使用一个对象来缓存已经创建的闭包,并在需要时进行复用。
示例:
1、在定时器不被使用的时候及时清除定时器
const count = '123123'
var intervalId = setInterval(function () {
console.log('count',count)
}, 1000);
//
// clearInterval(intervalId);
2、在退出函数之前,将不使用的局部变量赋值为null
这段代码会导致内存泄露
window.onload = function(){
var el = document.getElementById("id");
el.onclick = function(){
alert(el.id);
}
}
解决方法为
window.onload = function(){
var el = document.getElementById("id");
var id = el.id;
//解除循环引用
el.onclick = function(){
alert(id);
}
el = null;
// 将闭包引用的外部函数中活动对象清除
}
五、闭包常见的应用场景
- 柯里化函数
- 通过闭包实现变量/方法的私有化
- 自执行函数(IIFE)
- 缓存一些结果
- 定时器setTimeOut
示例:
1、柯里化函数
为了避免频繁地调用具有相同参数的函数,可以将一个多参数的函数转化为一个单参数的函数,其实就是一个高阶函数
//普通函数
function getArea(w,h){
return w * h;
}
const area1 = getArea(10,20);
const area2 = getArea(10,30);
const area3 = getArea(10,40);
//柯里化函数
function getArea(w){
return function(h){
return w * h;
}
}
// 调用1
getArea(10)(20)
// 调用2
const getTenArea = getArea(10);
const area1 = getTenArea(20);
const area2 = getTenArea(30);
const area3 = getTenArea(40);
2、通过闭包实现变量/方法的私有化
function funOne(i){
function getTwo(){
console.log('参数:', i)
}
return getTwo;
}
const fa = funOne(100);
const fb = funOne(200);
const fc = funOne(300);
3、匿名自执行函数
var funOne = (function(){
var num = 0;
return function(){
num++;
return num;
}
})()
console.log(funOne()); // 1
console.log(funOne()); // 2
console.log(funOne()); // 3
4、缓存一些结果
比如:外部函数定义一个变量,内部函数可以获取或修改这个变量的值,从而就延长了这个变量的生命周期
function parent(){
let list = [];
function son(i){
list.push(i);
}
return son;
}
const fn = parent();
fn(1);
fn(2);
fn(3);