闭包的定义

闭包是指有权限访问其他函数作用域的局部变量的一个函数

在JS中,变量的作用域属于函数的作用域,函数执行后作用域被清理、内存随之回收,由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数也就是闭包,拥有了访问上级作用域的变量的权限,即使上级函数执行完毕,作用域的值也不会被销毁。

本质上,闭包就是将函数内部和外部连接的桥梁

  • 可以读取函数内部的变量
  • 变量的值始终保存在内存

使用场景:

  • AJAX请求的成功回调
  • 事件绑定回调方法
  • setTimeout的延时回调
  • 函数内部返回了另一个匿名函数
1
2
3
4
5
6
7
8
9
10
function a(){
function b(){
var bb = 666
console.log(aa); //输出:333
}
var aa = 333
return b
}
var demo = a()
demo()

闭包的用途

1.封装私有变量

1
2
3
4
5
6
7
8
9
10
11
12
function addFn(){
let count = 1
function a(){
count++
console.log(count);
}
return a
}
var acc = addFn()
acc () //2
acc () //3
acc () //4

2.做缓存

函数一旦被执行完毕,其内存就会被销毁,而闭包的存在,就可以保有内部环境的作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function fn1(){
var type ='JavaScript'
let tt1 = 1
const tt2 = 2
var innerBar={
getType: function(){
console.log(tt1);
return type
},
setType:function(newType){
type = newType
}
}
return innerBar
}
var bar = fn1()
console.log(bar.getType()); //输出:1 JavaScript
bar.setName('python')
console.log(bar.setType()); //输出:1 python

3.模块化编程

闭包还可以用于实现模块化编程。模块化编程是一种将程序拆分成小的、独立的、可重用的模块的编程风格。闭包可以用于封装模块的私有变量和方法,以便防止其被外部访问和修改。

1
2
3
4
5
6
7
8
9
10
11
12
const moduleFn = (function() {
let privateVar = '我是私有变量!';
function privateMethod() {
console.log(privateVar );
}
return {
publicMethod: function() {
privateMethod();
}
};
})();
moduleFn.publicMethod(); // 输出: 我是私有的!

4.防抖、节流

4.1防抖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//防抖 避免函数的重复调用 只会调用一次
function antishake(fn,wait){ //第一个参数是函数 第二个参数是毫秒值
let timer = null //声明一个变量来接收延时器 初始值为null
return function(){
clearTimeout(timer)
timer = setTimeout(() => {
fn() //调用这函数
}, wait);
}
}
let an = antishake(function(){ //用一个变量接收
console.log('555');
},2000)
document.querySelector('div').onmouseenter = ()=>{
an() //调用一次
}

4.2节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function throttle(fn,wait){
let timer = null //节点闸
return function(){
if(timer) return //null false 不是null结果减少true 如果上传没有我就直接跳过 没有人我就上去
timer = setTimeout(() => { //上车了
fn()
timer = null //做完之后重新关闭节点闸
}, wait);
}
}
let throttle1 = throttle(()=>{
console.log('我上车了');
},2000)
document.querySelector('div').onclick = ()=>{
throttle1()
}

闭包的缺点

闭包也存在着一个潜在的问题,由于闭包会引用外部函数的变量,但是这些变量在外部函数执行完毕后没有被释放,那么这些变量会一直存在于内存中,总的内存大小不变,但是可用内存空间变小了。 一旦形成闭包,只有在页面关闭后,闭包占用的内存才会被回收,就会造成的内存泄漏。

因此我们在使用闭包时需要特别注意内存泄漏的问题,可以用以下两种方法解决内存泄露问题:

1.及时释放闭包:手动调用闭包函数,并将其返回值赋值为null,这样可以让闭包中的变量及时被垃圾回收器回收。
2.使用立即执行函数:在创建闭包时,将需要保留的变量传递给一个立即执行函数,并将这些变量作为参数传递给闭包函数,这样可以保留所需的变量,而不会导致其他变量的内存泄漏。