一、概念
闭包就是能够读取其他函数内部变量的函数。这是因为内部函数保持了对外部函数作用域的引用,从而使得这些变量不会被垃圾回收机制回收。
闭包的用途和优缺点
二、闭包的用途包括:
- 访问内部变量:闭包允许在外部访问函数内部的变量,这对于创建私有变量非常有用。
- 保持状态:闭包可以保持函数的执行环境,即使函数已经执行完毕,其内部变量也不会被释放。
优点:
- 封装:闭包可以封装私有变量,防止外部直接访问。
- 持久化:闭包可以保持函数的执行环境,使得某些状态信息得以保留。
缺点:
- 内存占用:由于闭包会保持外部函数的执行环境,可能会导致内存占用增加。
- 内存泄漏:如果不正确管理闭包,可能会导致内存泄漏问题。
三、Vue中闭包的应用
Vue 源码中,闭包主要用于:
- 数据封装(如
reactive、computed) - 缓存结果(如
computed避免重复计算) - 事件管理(如
EventEmitter存储事件) - 依赖管理(如
Dep存储 Watcher)
3.1 响应式数据劫持(Reactivity System)
Vue 2 采用 Object.defineProperty 进行响应式处理,而 Vue 3 采用 Proxy,但无论哪种方式,闭包都用于存储依赖和数据。
Vue 3 的 reactive
function createReactiveObject(target: any) {
const handlers = {
get(target, key, receiver) {
console.log(`访问属性:${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`修改属性:${key} = ${value}`);
return Reflect.set(target, key, value, receiver);
}
};
return new Proxy(target, handlers);
}
const state = createReactiveObject({ count: 0 });
handlers这个对象被Proxy捕获器(拦截器)所引用,形成了闭包。- 这个闭包确保了每次
get/set都会执行特定的逻辑,而不会污染全局。
3.2 缓存计算属性(Computed)
Vue 3 的 computed 依赖 闭包 来存储计算结果,避免重复计算。
function computed(getter) {
let cache;
let dirty = true; // 是否需要重新计算
return {
get value() {
if (dirty) {
cache = getter(); // 只在必要时重新计算
dirty = false;
}
return cache;
}
};
}
const myComputed = computed(() => Math.random());
console.log(myComputed.value); // 第一次计算
console.log(myComputed.value); // 直接返回缓存
cache变量存储计算结果,避免重复计算。dirty变量用于标记是否需要重新计算。
3.3 Vue 事件系统(EventEmitter)
Vue 的事件系统本质上是一个 发布-订阅模式,闭包用于存储事件列表。
function createEventEmitter() {
const events = {}; // 用于存储事件
return {
on(event, fn) {
if (!events[event]) events[event] = [];
events[event].push(fn);
},
emit(event, ...args) {
if (events[event]) {
events[event].forEach(fn => fn(...args));
}
}
};
}
const emitter = createEventEmitter();
emitter.on('click', () => console.log('点击事件触发'));
emitter.emit('click'); // 触发事件
events变量不会暴露到外部,只能通过on和emit操作,保证数据安全性。