学习记录-手写store.js

74 阅读2分钟

本篇学习涉及知识点: 闭包,proxy ,Object.defineProperty。

闭包是什么?有什么应用场景?

  • 闭包是指一个函数能够记住并访问它定义时的作用域,即使这个函数是在其外部执行的。闭包常用来:

    • 模拟私有变量。
    • 函数工厂(返回可以记住特定参数的函数)。
    • 延迟执行和事件处理。

Proxy是什么?

  • Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

为什么用Proxy而不是Object.defineProperty?

Proxy 提供灵活的拦截和修改功能,而 getter 和 setter 更关注数据访问规则。

1. 灵活的拦截机制

  • Proxy 允许你拦截几乎所有的操作,包括属性的访问(get)、属性的修改(set)、属性的删除(deleteProperty)、方法调用(apply)、构造函数调用(construct)等。你可以对对象的行为进行全方位的控制。
  • Object.defineProperty() 只允许你为对象的单个属性定义 gettersetter,而且你必须为每个属性手动设置 gettersetter,这在处理动态属性时比较麻烦。

2. 动态属性拦截

  • 使用 Proxy 时,不需要提前知道对象的结构。可以对任意属性(包括新添加的属性)进行拦截。这使得 Proxy 更加动态和灵活。你可以动态地添加、删除或修改对象的属性,而不需要事先定义 gettersetter
  • 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())