3

如何重写 localStorage 中的方法

 1 year ago
source link: https://www.xiabingbao.com/post/javascript/rewrite-localstorage-rcw995.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
经常会想要重写 localStorage 来实现某个功能,都有哪些方法重写 localStorage 里的方法呢?

经常会有同学想要重写 localStorage 中的方法,来实现 key 的过期时间,或者监听 key 的读写等。那么都有哪些方法重写 localStorage 里的方法呢?

1. 直接在 localStorage 上重写

很多同学喜欢重写的思路,先留存原生的方法,然后直接在 localStorage 上重写该方法,如:

const { setItem } = localStorage;

localStorage.setItem = function (key, value) {
  console.log('localStorage.setItem', key, value);
  setItem.call(this, key, value);
};

不过这种写法,并不是重写setItem()方法,而是在 localStorage 上添加了一个 setItem 的属性,该属性的值就是后面声明的方法,然后把原生的 setItem() 方法给覆盖掉了。

具体我没有太测试,不过在有的浏览器里,会忽略该属性,导致我们的重写失效。

2. 重写 localStorage.__proto__ 上的方法

我们仔细观察的话,setItem、getItem 都是以 __proto__ 的方式继承自 Storage 的。

localStorage的__proto__-蚊子的前端博客

那我们直接重写 localStorage.__proto__ 上面的方法。

const { setItem } = localStorage.__proto__;

localStorage.setItem = function (key, value) {
  console.log('localStorage.__proto__.setItem', key, value);
  setItem.call(this, key, value);
};

这就实现了 setItem()方法真正的重写。

但这里还有个问题,localStorage 和 sessionStorage 都是继承自Storage,重写了 localStorage.__proto__ 上的属性或方法后,也把 sessionStorage 里的方法重写了。

把sessionStorage里的方法也重写了-蚊子的前端博客

3. 外部封装一层

我们不直接对 localStorage 本身的方法进行修改,而是在外面包装一层,底层再使用 localStorage 实现存储功能。

class MyLocalStorage {
  setItem(key, value) {
    console.log('MyLocalStorage.setItem', key, value);
    localStorage.setItem(key, value);
  }
}

const myLocalStorage = new MyLocalStorage();
myLocalStorage.setItem('aa', '123');

这种方式相对来说,自由度会高一些,而且也没有第 1 节中的兼容性问题。只是使用的名称发生了变化,而且还完全把 localStorage 里的属性和方法都屏蔽掉了。

若想没有负担地使用自定义的对象,则需要把所有的属性和方法都实现了。无法像上面那种,单独 mock 某个方法。

4. 覆盖 localStorage

使用Object.definePropertyProxy等方式,完全覆盖掉 localStorage 变量。比第 3 节好的地方在于名称没变。

4.1 直接覆盖,没有效果

若使用下面的方式直接覆盖的话,其实是没有效果的。

window.localStorage = Object.create(null);

console.log(window.localStorage); // 还是原生的

我们通过 Object.getOwnPropertyDescriptor 获取 localStorage 的属性描述符。可以发现并没有writable: true的属性,这说明 localStorage 并不是直接可写的。

localStorage的getOwnPropertyDescriptor-蚊子的前端博客

4.2 使用 Object.defineProperty 重写

既然没有writable属性,那我们就给他加一个。我们可以用Object.defineProperty来重写 localStorage。

但就不能用上面外面包一层的写法了,若直接将上面的 myLocalStorage 给到 localStorage 的话,会产生无限递归(为避免生成误导,这里就不写错误的写法了)。

((win) => {
  const nativeLocalStorage = win.localStorage;
  win.nativeLocalStorage = nativeLocalStorage; // 保留原生的使用

  class MyLocalStorage {
    setItem(key, value) {
      console.log('MyLocalStorage.setItem', key, value);
      nativeLocalStorage.setItem(key, value);
    }

    getItem(key) {
      console.log('MyLocalStorage.getItem', key);
      return nativeLocalStorage.getItem(key);
    }
  }

  const myLocalStorage = new MyLocalStorage();
  // 将新创建的实例赋值给localStorage
  Object.defineProperty(win, 'localStorage', {
    value: myLocalStorage,
    writable: true,
  });
})(window);

我这里对 localStoage 进行了下备份,万一要需要原生的方法时,还可以操作一下。

这篇文章里,我们并没有具体地实现某个功能,如设置过期时间等,而是从另一个角度讲了下如何重写 localStorage 或者里面的方法。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK