本篇学习涉及知识点: 闭包,proxy ,Object.defineProperty。
闭包是什么?有什么应用场景?
-
闭包是指一个函数能够记住并访问它定义时的作用域,即使这个函数是在其外部执行的。闭包常用来:
- 模拟私有变量。
- 函数工厂(返回可以记住特定参数的函数)。
- 延迟执行和事件处理。
Proxy是什么?
- Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
为什么用Proxy而不是Object.defineProperty?
Proxy 提供灵活的拦截和修改功能,而 getter 和 setter 更关注数据访问规则。
1. 灵活的拦截机制
- Proxy 允许你拦截几乎所有的操作,包括属性的访问(
get)、属性的修改(set)、属性的删除(deleteProperty)、方法调用(apply)、构造函数调用(construct)等。你可以对对象的行为进行全方位的控制。 - Object.defineProperty() 只允许你为对象的单个属性定义
getter和setter,而且你必须为每个属性手动设置getter和setter,这在处理动态属性时比较麻烦。
2. 动态属性拦截
- 使用
Proxy时,不需要提前知道对象的结构。可以对任意属性(包括新添加的属性)进行拦截。这使得Proxy更加动态和灵活。你可以动态地添加、删除或修改对象的属性,而不需要事先定义getter和setter。 Object.defineProperty只能定义已经存在的属性的行为,无法直接对新属性进行拦截。
这也是VUE3区别于VUE2做出的最大改变。
利用以上知识点,我们可以手写store。代码如下
class CustomStore {
constructor(initialState) {
this.listeners = []
this.state = this.createProxy(initialState)
}
createProxy(initialState) {
return new Proxy(initialState, {
get(target: any, p: string | symbol, receiver: any): any {
return p in target ? target[p] : undefined
},
set: (target: any, p: string | symbol, newValue: any, receiver: any): boolean => {
if (target[p] === newValue) return true
target[p] = newValue
this.notify(p, newValue)
return true
}
})
}
notify = (key, value) => {
this.listeners.forEach(listener => {
listener(key, value)
})
}
addListener = (listener) => {
if (typeof listener === 'function') {
this.listeners.push(listener)
}
}
getState = () => {
return this.state
}
}
const girlStore = new CustomStore({count: 0, name: 'lisa'})
girlStore.addListener((key, value) => {
console.log(`changed:${key}=${value}`)
})
girlStore.getState().count = 1; // 输出: State changed: count = 1
girlStore.getState().name = 'Bob'; // 输出: State changed: name = Bob
console.log(`changed:`, girlStore.getState())