1. 首先,闭包是什么?
闭包(closure):内部函数 总是可以访问其所在的 外部函数 中声明的 参数和变量,即使在其 外部函数被返回(寿命终结)了 之后。
2. 闭包的特点是什么
- 让外部访问函数内部边浪成为可能
- 局部变量会常住在内存中
- 可以避免使用全局变量,防止全局变量污染
- 会造成内存泄漏(有一块内存被长期占用,而不被释放)
3. 闭包的创建
闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同,都会重新创建一个新的地址。 但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。
闭包内存泄漏为: key = value,key 被删除了 value 常驻内存中; 局部变量闭包升级版(中间引用的变量) => 自由变量;
4. 例子
那就通俗来点讲,闭包的定义其实也很简单:
函数A内部有一个函数B,函数B可以访问到函数A中的变量,那么函数B就是闭包了
function A() {
let a = 1
window.B = function () {
console.log(a)
}
}
A()
B() // 1
说到底闭包存在的意义就是让我们可以简洁的访问函数内部的变量
经典面试题,循环中使用闭包解决
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 的第三个参数,这个参数就会被当成 timer 函数的参数传入
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},
i * 1000,
i
)
}
3. 第三种就是使用 let 定义 i 来解决问题,这个也是最为推荐的解决办法
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}