闭包概念
闭包就是对内部函数的叫法。( 下面代码中 inner 函数就称为闭包 )
<script>
function outer() {
function inner() {
}
}
</script>
1. 特性
(1) 闭包函数中可以读取宿主函数的变量
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script>
function outer() {
let i = 100
function inner() {
console.log(i)
}
inner() // 宿主函数中调用闭包函数
}
outer() // 结果:100, 闭包函数成功访问宿主函数的变量 i
</script>
</body>
</html>
(2) 可以延长宿主函数变量的生命周期
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script>
function outer() {
let i = 100
return function inner() {
console.log(++i)
}
}
// 通过闭包函数, 延长了宿主函数中变量的生命周期
let called = outer()
called() // 结果:101
called() // 结果:102
called() // 结果:103
outer()() // 结果:101
called() // 结果:104
</script>
</body>
</html>
通过闭包,延长宿主函数中变量生命周期的常见写法,就是在宿主函数中将闭包函数返回。
然后将返回的闭包函数保存到变量上,让其函数地址始终保持引用状态无法被 GC 回收。
代码解释:
① let called = outer() 中,outer() 执行后, 返回的是闭包函数,返回的函数引用由变量 called 持有, 因为引用被持有,
所以闭包函数的信息不会被 GC 销毁, 而闭包函数又引用了宿主函数中的变量 i,所以宿主函数中的 i 也不会被销毁。
② outer()() 是比较常见的,简易调用闭包函数的写法,当不需要持有闭包函数引用,只想单纯调用一次闭包函数时
可以使用这种写法,outer()() 第一个小括号会执行 outer 函数并返回闭包函数,第二个小括号会执行前面返回的闭包函数。
2. 注意
当闭包函数内引用宿主函数变量,并且闭包函数的引用被持有时, 宿主函数的变量会一直存在于内存中,实际开发中
要避免过多的出现这种情况,或者一旦无法避免,那么请在使用后,尽快清除对闭包函数的引用,让 GC 可以释放其所占空间,
以免内存占用过大,从而影响性能或内存溢出。
应用场景示例
JS 内置函数的参数为回调函数时, 可以更优雅的为回调函数传参
需求:使用归并函数 reduce 对数组求和,并在第一次调用时,输出自定义信息
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script>
let arr = [1, 2, 3, 4, 5]
let callback = (message) => {
return (prev, item, index) => {
if (index === 0) {
console.log(message)
}
return prev + item
}
}
console.log(arr.reduce(callback('开始调用'), 0))
</script>
</body>
</html>