一、闭包的概念理解
闭包是指 有权访问另一个函数作用域中的变量 的 函数,或着,可以简单理解为定义在一个函数内部的函数,内部函数可以访问到外部函数的局部变量。
例如,函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。
function A() {
let a = 1
window.B = function () {
console.log(a)
}
}
B() // 1
二、闭包存在的意义
在 JS 中,闭包存在的意义简单理解就是让我们可以间接访问函数内部的变量,延长变量的使用寿命以及减少命名空间的污染。
三、闭包的三大特性
- 函数嵌套函数
- 函数内部可以引用外部的参数及变量
- 参数和变量不会被垃圾回收机制回收
四、闭包的优缺点
优势
- 可创建私有变量
通过使用闭包,可以在外部调用闭包函数,从而在函数外部能够间接访问到函数内部的变量,也可以使用这种方法来创建私有变量避免全局变量的污染。
- 让这些变量的值始终保持在内存中
使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被垃圾回收机制回收实现变量数据共享。
缺点
- 滥用闭包会导致大量变量不会被垃圾回收机制回收,内存消耗很大,可能会造成网页卡顿等性能问题
- 函数内部的局部变量没有被释放,使得占用内存时间会变长,容易造成内存泄漏。
- 闭包会在父函数外部,改变父函数内部变量的值。若将函数当作Object使用,把闭包当作它的共用方法,内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。
解决方案
注意编码习惯,在退出函数之前,将不使用的局部变量及时释放。
function fn1(){
var arr = new Array[100000]
function fn2(){
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
案例分析: 函数执行后在创建了10万长度的数组存储在内存中,我们在调用后并没有对函数进行手动释放造成了内存的浪费
解决办法:
var f = fn1()
f()
f = null //让内部函数成为垃圾对象,从而回收闭包
五、闭包的经典应用
问题
循环中使用闭包解决 var 定义函数的问题。
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
问题分析:因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆6。
解决方法
1、使用闭包的方式
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(
function timer() {
console.log(j)
}, j * 1000)
}
)(i);
}
分析:
- 首先使用了立即执行函数将 i 传入函数内部,
- 这个时候值就被固定在了参数 j 上面不会改变,
- 当下次执行 timer 这个闭包的时候,
- 就可以使用外部函数的变量 j,从而达到目的。
2、使用 setTimeout 的第三个参数
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
}, i * 1000, i
)
}
分析:
- 这个参数会被当成 timer 函数的参数传入。
- 通过setTimeout的API解决,每次执行时,都传入参数i,并通过形参形成闭包
3、使用 let 定义 i
这个是最为推荐的方式
for (let i = 1; i <= 5; i++) {
setTimeout(
function timer() {
console.log(i)
}, i * 1000
)
}
- 通过ES6的let关键字,管理i的作用域,每次观察i时都是观察的定时器启动时i的数值
- 通过块级作用域形成闭包
相关文章 👉面试官:说说你对闭包的理解?闭包使用场景