由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的使用场景:
1.setTimeout
原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果。
// 会返回一个没有传参的子函数
// 在子函数中可以执行父函数的传参
function f1(a) {
// 子函数:使用父函数的传参,但自身不需要任何传参
function f2() {
console.log(a)
}
return f2
}
// 通过f1获取无参数的f2
setTimeout(f1(1), 1000) //一秒之后打印出1
2、事件处理
定义行为,然后把它关联到某个用户事件上(点击或者按键)。代码通常会作为一个回调(事件触发时调用的函数)绑定到事件。
// 通过传参,返回一个新的不需要参数的函数对象
function changeSize(size) {
return function () {
document.body.style.fontSize = size + 'px'
}
}
// 添加点击事件的处理:使用传参
document.getElementById('size-12').onclick = changeSize(12)
document.getElementById('size-20').onclick = changeSize(20)
document.getElementById('size-30').onclick = changeSize(30)
3、函数防抖
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。实现的关键就在于setTimeOut这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现。
4.封装私有变量
为了防止一个变量可以随便进行访问
function privateSum() {
// 私有变量
var sum = 0
// 存放对私有变量的操作函数
var operations = {
// 每次调用都会使用更新后的sum
inc: function () {
sum++
return sum
},
}
// 返回存储操作函数的对象
return operations
}
// 获取对私有变量的操作函数
let sum = privateSum()
// 对私有变量执行操作
console.log(sum.inc()) //1
console.log(sum.inc()) //2
console.log(sum.inc()) //3
闭包作用域+为什么内存泄漏
闭包的作用域链
1、内部函数innerFn()创建相对于自己的全局变量作用域,保存在[[Scope]]属性中。
2、对于内部函数而言,外部函数中的活动对象(局部变量)也属于自己的作用域链
3、即使外部函数执行完毕,但仍然在内部函数的属性([[Scope]]作用域链)中进行引用
即使外部函数的执行环境/作用域链全部销毁,但是变量还在内存中留着呢
4、根据垃圾回收机制,被引用的变量不会回收,因此会产生内存泄露(memory lack)
举例(闭包(closure)+使用场景)
function f1() {
// 被闭包使用
let n = 100
// 闭包f2
function f2() {
n = n + 100 // 作用域链:f2内部没有n,向上一层在f1中查找到n
// 每次调用f2的时候,n都会使用上次更改后的值
console.log(n)
}
// 返回的是函数f2
return f2
}
var temp = f1() // temp本质 = f2
temp() // 200 // 本质=f2()
temp() // 300 // 本质=再次f2(),这时候再次对n进行修改。这时获取到的n是200了。
temp = null // 取消对闭包(内部函数)的引用