手写 useLocalStorage 时发现的问题?

/**
         * 获取浏览器 localStorage 中的值, 也可对该值进行更新操作
         *
         * @param {String} key---localStorage中存储的键值对的key
         * @return {String} value---localStorage中存储的键值对的value
         * @return {Function} setValue---更新方法,可对localStorage中存储的键值对进行更新操作,本方法接受一个参数,该参数为更新后的值
         *
         * @example
         * const { value: nameValue, setValue: setNameValue } = useLocalStorage('name')
         * console.log(nameValue)
         * setNameValue('uni-app')
         * console.log(nameValue)
         */
        function useLocalStorage(key) {
            // 生成一个代理对象
            const ref = {}
            Object.defineProperty(ref, key, {
                get() {
                    const storage = JSON.parse(localStorage.getItem(key) ?? '')
                    return storage
                },
            })

            function setValue(newVal) {
                localStorage.setItem(key, JSON.stringify(newVal))
            }

            return {
                get value() {
                    return ref[key]
                },
                setValue,
            }
        }

        localStorage.setItem('name', JSON.stringify('uni-app'))
        const { value: nameValue, setValue: setNameValue } = useLocalStorage('name')

        console.log(nameValue) // 'uni-app'
        setNameValue('JavaScript')
        console.log(nameValue) // 这里输出的不是 'JavaScript', 仍然是 'uni-app'

问题就是代码段最后一个输出,不知道为什么还是 "uni-app"

阅读 731
avatarAI BotBETA

这段代码的问题在于,你试图返回一个代理对象,但是你的实现方式并没有正确地创建代理。在你的代码中,你创建了一个对象 ref 并使用 Object.defineProperty 来定义其属性。然而,这并没有创建你想要的代理。

具体来说,Object.defineProperty 是一个静态方法,它不能用来创建动态的代理。你需要使用动态的代理解决方案,比如使用 Proxy 对象。

这是一个更正确的使用 Proxy 来创建代理的例子:

function useLocalStorage(key) {
    const initialValue = localStorage.getItem(key) ?? '';
    const value = JSON.parse(initialValue || '{}');

    return new Proxy({
        get(target, prop) {
            if (prop === key) {
                return value;
            }
        },
        set(target, prop, value) {
            if (prop === key) {
                localStorage.setItem(key, JSON.stringify(value));
                this[prop] = value;
            }
            return true;
        }
    });
}

在这个例子中,我们使用 Proxy 对象来创建一个代理。这个代理会拦截所有的 getset 操作,并且如果操作的属性名和我们指定的 key 相同,我们就对其进行相应的操作。对于 get 操作,我们直接返回存储的值。对于 set 操作,我们首先将新的值存储到 localStorage 中,然后更新我们的代理对象。这样,无论何时你访问或设置 key 的值,你都是在操作同一个代理对象,而不是直接操作 localStorage。

3 个回答
✓ 已被采纳
const { value: nameValue, setValue: setNameValue } = useLocalStorage('name')

nameValue是基本类型,而不是引用类型,自然还是'uni-app'

  • 使用 setNameValue('JavaScript') 之后 localStorage 中的指已经变为JavaScript, 但是变量nameValue 还没有改变 需要重新 读取之后 才能变化
  • 末尾再读取一次之后 再输出

      const { value: nameValue, setValue: setNameValue } = useLocalStorage('name')
      console.log(nameValue) // 'JavaScript'

我发现修改一下使用方式可以达到目标效果,但会给使用增加麻烦

function useLocalStorage(key) {
            // 生成一个代理对象
            const ref = {}
            Object.defineProperty(ref, 'value', {
                get() {
                    const storage = JSON.parse(localStorage.getItem(key) ?? '')
                    return storage
                },
            })

            function setValue(newVal) {
                localStorage.setItem(key, JSON.stringify(newVal))
            }

            return {
                value: ref,
                setValue,
            }
        }

        localStorage.setItem('name', JSON.stringify('uni-app'))
        const { value: nameValue, setValue: setNameValue } = useLocalStorage('name')

        console.log(nameValue.value) // 'uni-app'
        setNameValue('JavaScript')
        console.log(nameValue.value) // 此时这里输出的就是 'JavaScript'

而且如果解构的话,好像是无法触发 get 的:

const obj = {
                count: 1,
                get value() {
                    return obj.count++
                },
            }

            const { count, value } = obj

            console.log(value) // 1
            console.log(value) // 1

            console.log(obj.value) // 2
            console.log(obj.value) // 3
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题