直观理解
- 把函数内部的变量(包括参数)在别的地方被引用
- 如函数中的回调函数进行了引用
- 如函数返回的另一个函数中进行了引用
视频教程:
(https://www.bilibili.com/video/BV1z7411v7T1?p=4&share_source=copy_web)
- 1-3p即可大致了解
- 后面都是细节
适用范围:
1. 作用:
- 使一个函数具有状态
- 每次调用这个函数,都是基于上一次的状态
2. 应用场景:
- 实现函数防抖
debounce, 等待空闲算法 防抖函数 debounce - 把一部分状态或者变量打包保存起来,不暴露在外部,一种私有化策略
前提知识:
- child::函数作用域链
实例说明:
之一
function A(){
let 内部变量;
function a(){
console.log(内部变量);
}
return a;
}
let 闭包 = A();//因为这条语句,导致"内部变量"一直保存在内存中
闭包(); //获取"内部变量"
闭包 = null; //释放闭包, 释放内存等同于:
function A(变量=默认值){
function a(){
console.log(变量);
}
return a;
}
let 闭包 = A();//因为这条语句,导致"内部变量"一直保存在内存中
闭包(); //获取"变量"的值
闭包 = null; //释放闭包, 释放内存之一
for (var i = 0; i < 5; i++) {
(function (a) {
setTimeout(function () {
console.log(a)
}, 1000)
})(i)
}- [base::块级作用域]
- 每次循环,都会声明一个新的匿名函数,并且把i的值赋给函数的a变量
- 即有地址中有5个不同的a变量
- 而这个a变量被settimeout的回调函数所引用,所以a不会被清理释放
- 直到被settimeout执行完,清理回调函数,被引用的a也随着被释放
之一
function a() {
let b = 1
return { a: b, log: () => { console.log(b++) } }
}
let obj = a()
obj.log()//1
obj.log()//2
console.log(obj)// { a: 1, log: [Function: log] }- 因为a函数的作用域中的变量被另一个函数所引用,所以该变量不会被清理
- 而对象的a只是在声明的时候引用了b,并以此获取b变量的值,之后便没有引用了,所以a属性并没有构成闭包
梗概:
- 针对一个函数A
- 函数A返回一个函数a
- 在函数外部(全局作用域), 用一个变量引用调用函数A后返回的函数a
- “引用”即把函数a赋值给变量, 可以理解为用变量缓存了函数a
- 则这个保存了函数a的变量称为闭包
- 通过
()调用这个变量, 函数a就具有了状态 - 状态保存在函数A中
- 通过
变量=null, 把闭包释放掉, 清理内存 - 清理状态
大致原理:
- 通过全局作用域的变量保存了函数A返回的函数a, 所以函数a的作用域不会被释放
- 而函数a中又用到函数A的作用域, 所以函数A的作用域也不会被释放
- 当把闭包的引用覆盖掉之后, 没有被引用的函数a和函数A就会被释放掉