我们要是想知道什么是闭包?就要从以下方法中寻找,想必大家已经迫不及待了吧!!!!
我们在学习中想要学习一个知识,怎么证明学会了呢?
就要了解这个知识点是什么?怎么用?解决了什么问题?原理是什么?等等。
闭包是什么(闭包的概念)?
在《JavaScript高级程设计》说到 : 闭包(closure)指有权访问另一个函数作用域中变量的函数
。
我个人觉得,【一个函数】使用了其【外部函数】中的局部变量,使用变量的地方我们称之为发生了【闭包现象】,变量定义所在的函数我们称为【闭包函数】。
讲到这里,可能还是有一些同学不太了解,没关系,我们来举个例子:
- 举例: js函数A里面有一个函数B,函数B访问了函数A里面定义的局部变量,此时就产生了闭包,变量所在的函数就是闭包函数。这里的A就是闭包函数。
function fn() {
let age = 18
return function () {
// 在这里打断点(debugger)可以看到 closure(fn) // 如果外层是匿名函数 就只会看到closure
console.log(age + 1)
}
}
const sum = fn()
sum()
闭包的作用?
解决全局变量污染,延长作用域
我们可以从闭包的作用中又可以了解到延伸了变量的作用范围,闭包函数中局部变量不会等着闭包函数执行完就销毁,而是等所有的函数都调用完才会销毁,导致内存泄露,内存泄露积累多了就容易导致内存溢出。
什么是内存泄露?我们也来了解一下。
内存泄露也称作"存储渗漏",用动态存储分配函数动态开辟的空间,在使用完毕之后未释放,结果导致一直占据该内存单元,直到程序结束----------即所谓内存泄露
其实说白了就是该内存空间使用之后没有及时回收。
哪些操作会导致内存泄露?
1.垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为0(没有其他对象引用过该对象),或对该对象的唯一引用是循环的,那么该对象的内存即可回收。
2.setTimeout的第一个参数使用字符串而非函数的话,会引发内存泄漏。
3.不规范使用闭包、控制台日志、循环
防止内存泄漏的方法
1.减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收(即赋值为null);
2.注意程序逻辑,避免“死循环”之类的;
3.避免创建过多的对象 原则: 不用了的东西要记得及时归还;
4.减少层级过多的引用
如何判断闭包
代码角度:函数 + 上下文引用
控制台角度: 会提示closure
闭包有两个常用的用途:
- 使我们在函数外部能够访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
-
使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收
在开发中哪些地方会使用到闭包?
- 一般封装防抖函数的时候也会用到。
<!--给一组按钮循环绑定点击事件,点击的时候希望输出对应的索引。 -->
<!-- 其实是做不到的,因为当点击按钮的时候,for 循环早就走完了,i 也就变成了最终的那个数值,输出的结果也永远是最终的那个数值。 -->
<body>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<script>
const Btn = document.querySelectorAll('button')
for (let i = 0; i < Btn.length; i++) {
Btn[i].onclick = function () {
console.log(i)
}
}
</script>
</body>
- 比如说,在早期,给一组按钮循环绑定点击事件,点击的时候期望输出对应的索引,本质上是做不到的,因为当点击按钮的时候,for循环早就走完了,i也变成了最终的那个数值,输出的结果也永远是最终的那个数值,这个时候可以每次循环都产生一个闭包函数,并把循环时候的变量i传递过去,这个循环时候的就会常驻内存,就做到了内
部使用的i就是循环时候那一刻的i
<body>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<script>
const Btn = document.querySelectorAll('button')
for (let i = 0; i < Btn.length; i++) {
(function (i) {
// 【函数形参 i 也是局部变量】
Btn[i].onclick = function () {
console.log(i)
}
})(i)
}
</script>
</body>
应用?
- 函数防抖
function debounce(fn, delay) {
let handle;
return function (e) {
// 取消之前的延时调用
clearTimeout(handle);
handle = setTimeout(() => {
fn(e);
}, delay);
}
}
- 函数节流
function throttle(fn, delay) {
let runFlag = false;
return function (e) {
// 判断之前的调用是否完成
if (runFlag) {
return false;
}
runFlag = true;
setTimeout(() => {
fn(e);
runFlag = false;
}, delay)
}
}
- 变量持久化 (闭包中的变量不会被回收)