Proxy作为apply的thisArgument报Illegal Invocation,求代理Storage更优方案
更优雅的Storage调用拦截实现方案
核心思路是通过实例Proxy绑定原生方法 + 原型方法Proxy统一修正this指向的组合方案实现全场景覆盖,无需自定义隐藏属性或异常兜底。
完整实现代码
// 存储原始Storage原型方法 const originalProtoMethods = { getItem: Storage.prototype.getItem, setItem: Storage.prototype.setItem, removeItem: Storage.prototype.removeItem, clear: Storage.prototype.clear, key: Storage.prototype.key } // 存储原始sessionStorage实例 const rawSessionStorage = window.sessionStorage // 弱引用标记Storage代理实例,不会污染实例属性 const isStorageProxy = new WeakSet() // 1. 代理sessionStorage实例,覆盖直接调用场景 const proxiedSessionStorage = new Proxy(rawSessionStorage, { get(target, prop, receiver) { // 访问原生Storage方法时直接返回绑定了原始实例的函数,规避this指向问题 if (prop in originalProtoMethods) { return originalProtoMethods[prop].bind(rawSessionStorage) } // 此处可添加自定义get拦截逻辑 return Reflect.get(target, prop, receiver) }, set(target, prop, value, receiver) { // 此处可添加自定义set拦截逻辑 return Reflect.set(target, prop, value, receiver) } }) isStorageProxy.add(proxiedSessionStorage) // 替换全局sessionStorage window.sessionStorage = proxiedSessionStorage // 2. 代理Storage原型方法,覆盖call/apply调用场景 Object.keys(originalProtoMethods).forEach(methodName => { Storage.prototype[methodName] = new Proxy(originalProtoMethods[methodName], { apply(target, thisArg, args) { // 若调用时传入的this是代理实例,自动替换为原始实例 const realThis = isStorageProxy.has(thisArg) ? rawSessionStorage : thisArg // 此处可添加自定义方法调用拦截逻辑 return Reflect.apply(target, realThis, args) } }) })
方案验证
两种调用场景均可正常运行,无TypeError: Illegal Invocation报错:
- 直接调用代理方法:
window.sessionStorage.setItem('test', '123') - 原型call/apply调用:
Storage.prototype.setItem.call(window.sessionStorage, 'test', '456')
方案优势
- 无自定义隐藏属性等hack实现,逻辑透明合规
- 不依赖异常捕获做兜底,执行性能更优
- 两种调用场景天然覆盖,无需额外兼容处理
- 自定义拦截逻辑可统一在对应钩子中维护,可扩展性强
- 若要兼容localStorage,按照相同逻辑新增代理即可
内容的提问来源于stack exchange,提问作者Mark J Miller
相关产品推荐
相关产品推荐

