如何修改JavaScript的compileScript函数实现变量代理访问外部对象
解决方案
核心思路
直接给用户脚本的执行上下文动态生成对应scriptData属性的getter/setter变量——用户用变量名读写时,本质就是在操作scriptData的原属性,既不用写this.,修改也能同步回原对象,完美避开with和严格模式的限制,同时兼容Web Worker环境。
具体实现代码
function compileScript(userScript, scriptData) { // 提取scriptData的所有可枚举属性名 const propNames = Object.keys(scriptData); // 生成代理变量的代码:每个属性对应一个getter/setter,直接绑定到scriptData const proxyVarsCode = propNames.map(prop => ` let ${prop}; Object.defineProperty(this, '${prop}', { get() { return scriptData['${prop}']; }, set(val) { scriptData['${prop}'] = val; }, enumerable: true }); `).join(''); // 把代理代码和用户脚本合并,用IIFE包裹传入scriptData const fullScript = ` (function(scriptData) { 'use strict'; ${proxyVarsCode} ${userScript} })(scriptData); `; // 返回可执行的函数,直接调用即可触发用户脚本 return function() { return eval(fullScript); }; }
方案优势
- 完全规避
with:严格模式下也能正常运行,不会有语法报错 - 双向同步:用户修改变量时直接修改原
scriptData对象,不存在本地副本不同步的问题 - 兼容Web Worker:用到的
Object.keys、Object.defineProperty、eval都是Worker环境支持的API - 无侵入式:用户脚本可以直接用变量名访问属性,不需要修改原有编写习惯
额外优化建议
- 如果
scriptData会动态新增属性,可以在执行脚本前重新生成代理变量,或者添加一个updateProps方法动态注入新的getter/setter - 可以过滤掉
scriptData中的不可枚举属性,只代理需要暴露给用户的属性
内容的提问来源于stack exchange,提问作者Marino Šimić
相关产品推荐
相关产品推荐

