概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包(closure) = 内层函数 + 外层函数的变量
在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
一般就是一个函数A,return其内部的函数B ,被return出去的B 函数能够在外部访问A函数内部的变量,这时候就形成了一个B函数的变量背包。
闭包的最典型的应用是实现回调函数(callback)
入门简单写法:(实例)
<script>
// 里层函数f()加外层函数outer()的变量 捆绑在一起形成闭包
function outer() {
const a = 2
function f() {
console.log(a)
}
f()
}
outer()
</script>
常见的闭包的形式,外部可以访问使用 函数内部的变量,写法:
<script>
function outer() {
const a = 2
function inner() {
console.log(a)
}
return inner //返回函数
}
// outer() === inner === function inner() {}
// const fn = function inner() {}
const fn = outer()
fn() //调用函数 外层函数使用内部函数的变量
</script>
闭包的应用 : 实现数据私有
实例:做一个统计函数调用次数,函数调用一次,就++
// count 为全局变量,能轻易被修改
let count = 1
function fn() {
count++
console.log(`函数被调用${count}次`)
}
fn() //2
fn() //3
//实现数据私有,无法直接修改count
function fn() {
//在外层申明一个函数 使i变成局部变量
let count = 1
function fun() {
count++
console.log(`函数被调用了${i}次`)
}
return fun
}
const result = fn()
result() //2
result() //3
闭包的作用:
1、封闭数据,实现数据私有,外部也可以访问函数内部的变量
2、闭包很有用,它允许将函数与其所操作的某些数据(环境)关联起来
3、使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收
注意:
内存泄漏:函数一直被使用,变量不会被回收(没有销毁)
内存浪费:不仅仅因为它常驻内存,更重要的是,对闭包的使用不当会造成无效内存的产生。
案例:循环中使用闭包解决var定义函数的问题
for (var i = 1; i <= 5; i++) {
// 定时器 延迟函数
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
首先,setTimeout 是一个异步函数,所以会先把循环全部执行完毕,这时候i 就是6,所以会输出一堆6。
解决方法:
第一种:使用闭包的方法
for (var i = 1; i <= 5; i++) {
; (function (j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
上述代码中,首先使用了立即执行函数将i 传入函数内部,这时值就被固定在参数j 上面不会改变,当下次执行timer 这个闭包时,就可以使用外部函数的变量j ,从而达到目的。
第二种:使用setTimeout 的第三个参数,这个参数会被当成timer 函数的参数传入
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},
i * 1000,
i
)
}
第三种:使用let 定义i 来解决问题,这个也是最为推荐的方式
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}