前言
用一句官方的话来讲,闭包是指在嵌套函数里的内部函数可以访问外部函数的变量。这种特性使得代码具有更好的封装性和模块化,也让函数能够更方便地处理异步事件。但是,闭包的滥用可能会导致性能问题和内存泄漏。
那用不官方的来讲怎么说呐?
闭包 指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
还要再通俗一点?
一个函数访问了此函数的父级及父级以上的作用域变量,那么这个函数就是一个闭包。
这要再不理解就说不过去了!!
闭包的写法
匿名的立即执行函数
var a = 1;
// 匿名的立即执行函数,因访问了全局变量a,所以也是一个闭包
(function test (){
alert(a);
})()
本质上,JS中的每个函数都是一个闭包,因为每个函数都可以访问全局变量。
函数嵌套
function a() {
var i = '初始值';
i = i + "—_执行a"
// 此处的函数b访问了父级函数a中的局部变量i,成为了一个闭包
function b() {
i = i + "_执行b"
console.log(i)
}
return b;
}
var c = a(); // 此时 i 的值为 :初始值—_执行a
c() // 此时 i 的值为 :初始值—_执行a_执行b
c() // 此时 i 的值为 :初始值—_执行a_执行b_执行b
闭包的产生条件
当前函数执行会形成一个私有的执行上下文,函数执行完毕之后,这个私有执行上下文中的某些变量或方法被该上下文以外的上下文所占用,导致该函数私有上下文无法被释放从而就形成了所谓的——闭包
以嵌套函数代码的写法为例:
1.将函数a赋值给全局变量c时,a会执行一次,局部变量 i 的值变为初始值—_执行a,最终返回函数b,此时全局变量c的值为闭包函数b的引用。
此时函数a虽然已执行完,但因为内部包含闭包函数b,所以函数 a 的执行期上下文会继续保留在内存中,不会被销毁,所以局部变量 i 仍是初始值—_执行a。
2.第一次执行c()时,闭包函数b第一次执行,局部变量 i 的值变为初始值—_执行a_执行b 。
3.第二次执行 c() 时,闭包函数b第二次执行,局部变量 i 的值变为初始值—_执行a_执行b_执行b。
闭包的作用
模仿块级作用域,保护函数中私有变量;
function fn(num){
(
function (){
for(let i=0;i<5;i++){
num +=num
}
}
).call();//声明⼀个函数⽴即调⽤以后,浏览器刷新页⾯会报错,可以⽤⼀个⼩括号把整段函数包起来。
console.log(num);
}
fn(5)//160
可以读取或储存函数内部的变量;
function fn() {
var a = 1;
return {
function() {
return a
}
}
}
let d = fn()
可以用于封装私有变量;
//变量作⽤域为函数内部,外部⽆法访问
let name = "default ";
return {
getName() {
return name;
},
setName(newName) {
name = newName;
}
};
}();
console.log(person.name);//undefined
console.log(person.getName());//default
person.setName('jack');
闭包的优缺点
优点
- 避免全局污染;
- 变量长期存储在内存中; 在函数体内,随着函数每一次执行完毕,函数内部定义的对象(如果没有被闭包函数引用),会被销毁;如果被闭包引用,则被保留下来。所以闭包的存在,导致变量不会被销毁,保留了下来,从而长期存储在内存当中;
缺点
- 内存泄漏(消耗):应用程序中不再用到的内存,由于某些原因,无法及时释放,导致了内存的泄漏。闭包的存在,使得某些变量不能被及时销毁,内存也无法及时释放,也就导致了内存的泄漏。但是闭包存在,不一定就会导致内存泄漏。 闭包导致内存的泄漏只存在 IE 浏览器中,其他浏览器中不会导致内存的泄漏。
- 常驻内存,增加内存使用量:这个内存浪费不仅因为它常驻内存,更重要的是,对闭包的使用不当,会造成无效内存的产生;
- 性能问题:使用闭包时,会涉及跨作用域的访问,每次访问都会导致性能损失;