JavaScript处理JSON API数据时数组push后console.log显示异常求助
JavaScript处理JSON API事件数组push操作异常问题
问题背景
需要处理JSON API返回的和指定地址关联的嵌套事件数组,数组内包含多个事件,部分事件带有相同的时间戳。预期逻辑为:遍历数组,创建包含对应事件的新对象;如果检测到重复时间戳,就将重复的事件追加到已创建的对应新对象中。
异常表现
在将事件追加到新对象的已有事件数组环节,push操作看似执行成功,但console.log输出存在异常:预览显示数组内有2个元素,展开数组后仅存在1个元素,且前端代码处理生成的新对象时也只能读取到1个元素。
原问题代码
function processTransactions(address, json){ // 取data.items下的交易数组 var transactions = json.data.items; var txList = new Array(); // 遍历每个条目,迭代log_events数组,仅保留匹配(To或From)的条目 for (let i=0; i<transactions.length; i++){ // tx为当前处理的交易 var tx = transactions[i]; // 遍历当前交易的事件日志数组 // 查找事件日志中匹配我们地址的条目 for( let j=0; j<tx.log_events.length; j++){ // j为事件数组中的一个事件 // 将该事件添加到处理后的事件列表中 var e = tx.log_events[j]; var a = e.raw_log_topics; var expr = new RegExp(address.substring(2)); // 检查地址是否包含在raw_log_events的内容中 if (a.find(value => expr.test(value))){ if (txList.some(ev => ev.block_signed_at === e.block_signed_at)){ // txList中找到匹配对象 console.log ("Match found using timestamp"); var o = txList.find( ev => ev.block_signed_at === e.block_signed_at ); o.events.push(e); console.log(o); console.log("ending match found") } else { // 未找到匹配,创建对象并添加到txList var o = { block_signed_at: e.block_signed_at, // 向对象添加unix时间戳,用于前端展示和排序 block_signed_at_timestamp: new Date(e.block_signed_at).valueOf() / 1000, events: [] } o.events.push(e); txList.push(o); }; } } } return {transactions: txList}; }
问题原因及修复方案
根因分析
- 控制台预览和展开内容不一致是浏览器控制台的惰性求值特性导致:打印对象时的预览是输出时刻的快照,展开对象时浏览器才会读取对象的最新状态,属于调试工具的正常表现,不是核心问题。
- 代码逻辑问题:
- 变量作用域问题:使用
var声明变量o,不存在块级作用域,两个分支内的变量声明会提升到函数作用域,可能出现引用覆盖。 - 冗余遍历逻辑:先调用
some判断是否存在同时间戳条目,再调用find查找对应条目,单次匹配要遍历两次txList,性能损耗的同时极端场景下可能出现两次遍历结果不一致。 - 正则匹配风险:直接截取地址后缀生成正则,未对地址中的正则特殊字符(如
.、$、*等)做转义,可能出现匹配逻辑异常,导致部分符合业务条件的事件未进入处理分支。
- 变量作用域问题:使用
修复后代码
function processTransactions(address, json) { const transactions = json.data.items; const txList = []; // 提前转义正则特殊字符,避免匹配异常 const addrSuffix = address.substring(2).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const matchExpr = new RegExp(addrSuffix); for (let i = 0; i < transactions.length; i++) { const tx = transactions[i]; for (let j = 0; j < tx.log_events.length; j++) { const currentEvent = tx.log_events[j]; const logTopics = currentEvent.raw_log_topics; if (logTopics.find(value => matchExpr.test(value))) { // 单次查找即可判断是否存在同时间戳条目,无需两次遍历 const existRecord = txList.find(ev => ev.block_signed_at === currentEvent.block_signed_at); if (existRecord) { existRecord.events.push(currentEvent); } else { const newRecord = { block_signed_at: currentEvent.block_signed_at, block_signed_at_timestamp: new Date(currentEvent.block_signed_at).valueOf() / 1000, events: [currentEvent] }; txList.push(newRecord); } } } } return { transactions: txList }; }
调试建议
如果需要确认push操作是否执行成功,可以打印数组长度或者序列化后的数组内容,避免浏览器控制台惰性求值的干扰:
// 追加元素后打印确认 console.log('当前事件数组长度:', existRecord.events.length); console.log('当前事件数组快照:', JSON.stringify(existRecord.events));
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

