如何在不修改源码的前提下访问JavaScript generator内部变量
JavaScript 中包括异步生成器在内的所有函数,内部局部变量都受作用域规则保护,只要函数本身没有主动对外暴露该变量,常规运行环境下外部无法直接读取栈帧上的私有变量,因此不存在通用的、可用于生产环境的get_value_of_a实现可以直接读取你示例中生成器内部的局部变量a。
以下是不同场景下的可行解决方案:
调试场景方案:利用运行时调试接口
如果是本地开发调试使用,不需要在生产环境运行,可以借助 Node.js 或浏览器的调试协议读取生成器暂停在yield位置时的栈帧变量:
- Node.js 环境可以通过内置的
inspector模块连接调试接口,调用Debugger.evaluateOnCallFrame方法在生成器的调用栈帧中执行表达式读取a的值,示例代码如下:
const inspector = require('inspector'); const session = new inspector.Session(); session.connect(); // 注:使用前需要先通过Debugger.getStackTrace接口获取生成器yield暂停时的callFrameId async function get_value_of_a(callFrameId) { return new Promise(resolve => { session.post('Debugger.evaluateOnCallFrame', { callFrameId, expression: 'a' }, (_, res) => resolve(res.result.value)); }); }
该方案仅可用于调试场景,生产环境默认关闭调试接口,无法使用
生产环境可行方案:猴子补丁拦截变量赋值
如果生成器内部给a赋值的something是你可访问的变量或函数,可以通过猴子补丁替换对应逻辑,在不修改生成器代码的前提下缓存a的值:
// 假设something是全局可访问的函数 const originalSomething = something; let cachedA; // 补丁替换something,赋值时缓存a的值 something = function(...args) { const result = originalSomething.apply(this, args); cachedA = result; return result; } // 直接读取缓存即可 function get_value_of_a() { return cachedA; }
注意:如果
something是生成器内部定义的私有变量,你无法从外部访问的话,该方案不适用,此时没有任何常规手段可以获取到生成器内部的局部变量a。
更通用的包装方案:代理生成器迭代器
你也可以直接包装原生成器的迭代器,在不侵入原生成器代码的前提下,把a的值和yield返回值绑定返回:
function wrapGenerator(originGen) { const cache = {}; // 补丁赋值逻辑缓存a const originalSomething = something; something = (...args) => { const res = originalSomething(...args); cache.a = res; return res; } // 代理异步迭代器 const proxy = { async next(...args) { const rawRes = await originGen.next(...args); return { ...rawRes, value: { data: rawRes.value, a: cache.a } }; }, [Symbol.asyncIterator]() { return this; } }; return proxy; } // 使用包装后的生成器迭代即可 const gen = wrapGenerator(myGen()); for await (const {data, a} of gen) { if (data === special_value) { const my_a = a; // 直接获取a的值 } }
内容的提问来源于stack exchange,提问作者Adam S.
相关产品推荐
相关产品推荐

