1、什么是闭包
当一个函数在其定义时的作用域之外执行,并且仍然可以访问当时的变量,这个现象就叫做闭包。
javascript
function createCounter() {
let count = 0
return function () {
count++
return count
}
}
const counter = createCounter()
counter() // 1
counter() // 2
-
createCounter()执行完后,按理说作用域应该销毁 -
但返回的函数 仍然引用了 count
-
JS 引擎发现:
👉 这个作用域不能回收
-
于是形成了一个 被"保活"的作用域 ------ 这就是闭包
重点:不是函数产生了闭包,而是函数使用了外层作用域的变量,并且在外层作用域之外执行。
2、vue中的闭包案例
javascript
export function useUser() {
const user = ref(null)
function setUser(data) {
user.value = data
}
return {
user,
setUser
}
}
-
useUser执行完后,本应销毁 -
但
setUser仍然在组件里被调用 -
setUser引用了 user -
所以:
useUser的作用域被保留
👉 组合式 API = 闭包驱动的设计
这不是巧合,是语言能力直接影响框架设计。
3、为什么 for + setTimeout 会"翻车"
javascript
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
//输出3,3,3
-
var i只有一个作用域 -
所有回调 共享同一个 i
-
回调执行时,循环早就结束了
javascript
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
👉let 为每次循环创建了一个新的词法作用域
👉 每个回调闭包住的是 不同的 i
**4、**闭包 ≠ 内存泄漏
闭包会延长变量生命周期,
如果引用链一直存在,才可能导致内存无法释放。
Vue 中真正的问题通常是:
-
全局事件没解绑
-
定时器没清理
-
引用被缓存到单例中
不是"闭包的锅"。
5、用闭包实现"私有变量"
javascript
function createUser() {
let password = '123456'
return {
check(pwd) {
return pwd === password
}
}
}
JS 没有 private,但闭包就是私有化方案
**6、**Vue 业务里的真实闭包模式
javascript
function useRequest() {
let loading = false
async function run(promise) {
loading = true
try {
return await promise
} finally {
loading = false
}
}
return { run }
}
👉loading 被 run 闭包住
👉 状态与行为天然绑定
7、思考
-
为什么 setup 执行一次,但状态可以长期存在?
-
闭包和模块作用域有什么相同与不同?
-
如果一个闭包持有一个很大的对象,什么时候会释放?
-
为什么 hooks / composables 天然适合用闭包实现?
-
如果让你写一个
useDebounce,闭包在其中起什么作用?