使用JavaScript Proxy监听资源加载状态时遇set代理错误求助
问题描述
我想添加一种类似EventListener的机制,在所有应用资源加载完成时触发。我觉得Proxy是监控对象属性变化的合适方式,以下代码在jsfiddle能正常运行,但在实际加载资源的代码里使用时,出现错误:caught (in promise) TypeError: 'set' on proxy: trap returned falsish for property。我刚接触JavaScript Proxy,希望得到指导。
const log = console.log.bind(console); let gameState = { live: false, loading: { images: false, sounds: false, controls: false, }, }; const loadingListener = { set(obj, prop, value) { Reflect.set(...arguments); if (gameState.loading.images && gameState.loading.sounds && gameState.loading.controls) { log(true); } else { log(false); } }, }; // Create the proxy object with the original object and the handler const proxyObject = new Proxy(gameState.loading, loadingListener); // Test the proxy object proxyObject.images = true; proxyObject.sounds = true; proxyObject.controls = true;
解决方案
这个错误的核心原因是Proxy的set陷阱函数没有返回布尔值。根据ES规范,set陷阱必须返回true表示属性设置成功;返回false或未返回值(默认返回undefined,属于falsish值)时,严格模式环境会直接抛出TypeError。你的实际代码大概率运行在严格模式下(比如ES模块、打包后的项目代码),而jsfiddle默认环境是非严格模式,所以没触发这个错误。
修正步骤
在set函数末尾返回操作结果即可,最简单的方式是直接返回Reflect.set的执行结果(它本身会返回布尔值表示操作是否成功):
const log = console.log.bind(console); let gameState = { live: false, loading: { images: false, sounds: false, controls: false, }, }; const loadingListener = { set(obj, prop, value) { // 执行属性设置并获取结果 const setSuccess = Reflect.set(obj, prop, value); // 检查所有资源是否加载完成 if (obj.images && obj.sounds && obj.controls) { log("所有资源加载完成!"); } else { log("资源加载中..."); } // 返回操作结果,避免报错 return setSuccess; }, }; const proxyObject = new Proxy(gameState.loading, loadingListener); // 测试 proxyObject.images = true; proxyObject.sounds = true; proxyObject.controls = true;
额外优化建议
- 避免依赖外部的
gameState,直接通过代理对象obj访问属性,降低代码耦合度:// 原代码依赖外部gameState // if (gameState.loading.images && ...) // 改为直接用obj if (obj.images && obj.sounds && obj.controls) { ... } - 实现真正的EventListener机制:可以在资源加载完成时触发自定义事件,让外部代码监听:
// 定义自定义事件 const resourcesLoadedEvent = new CustomEvent('resourcesLoaded'); const loadingListener = { set(obj, prop, value) { const setSuccess = Reflect.set(obj, prop, value); if (obj.images && obj.sounds && obj.controls) { // 触发事件 window.dispatchEvent(resourcesLoadedEvent); } return setSuccess; }, }; // 外部监听事件 window.addEventListener('resourcesLoaded', () => { console.log("所有资源加载完成,开始初始化游戏!"); // 这里写加载完成后的逻辑 });
内容的提问来源于stack exchange,提问作者TK421
相关产品推荐
相关产品推荐

