CondController实例objList数组显示为空但实际非空,引发属性访问报错的问题求助
嘿,这个问题我之前也碰到过,一开始确实懵圈,其实是浏览器控制台的特性+代码时序问题搞的鬼,咱们一步步拆解:
先解释你看到的“诡异控制台输出”
浏览器控制台打印数组/对象时,用的是引用快照,不是实时值:
- 当你执行
console.log(condListController.objList.length)时,objList确实是空数组,所以输出0; - 但数组是引用类型,后面你的代码(比如
optimizer_params里的逻辑)给它添加了元素,等你在控制台展开这个数组时,浏览器会读取它的最新状态,所以能看到值。 - 控制台显示的
[[prototype]]:Array(0)是当时的快照标识,展开后才会拉取最新数据,这不是bug,是控制台的延迟求值特性。
核心错误原因:代码执行时序不对
你的报错Cannot read properties of undefined (reading 'insert_cond'),本质是在objList被填充数据之前,就开始尝试读取和使用它了,导致condListController.getKey(...)返回undefined,调用insert_cond自然报错。
看你的代码流程:
async function build_conds() { const json = await getJson("load_conditions"); const buy_conds = json.buy_conds; // 这里调用了optimizer_params,但如果它是异步函数,你没加await! optimizer_params(buy_conds, "_BUY", "param_buy"); // 直接进入for循环,这时候optimizer_params可能还没执行完,objList还是空的 for (let i = 0; i < buy_conds.length; i++) { // ... 此时objList为空,getKey找不到对应实例,返回undefined let condManager = condListController.getKey(buy_conds[i].fk_list_id); condManager.insert_cond(...) // 报错 } }
具体修复步骤
先处理异步时序问题
检查optimizer_params函数:- 如果它是异步函数(内部有await、Promise等),必须用
await调用它,确保它把所有CondManager实例都添加到objList后,再进入for循环:// 加await! await optimizer_params(buy_conds, "_BUY", "param_buy"); - 如果
optimizer_params本身不是异步,但内部触发了其他异步操作(比如DOM回调、setTimeout、AJAX),那需要把这些异步逻辑改成同步,或者用Promise包裹,等所有操作完成后再继续。
- 如果它是异步函数(内部有await、Promise等),必须用
修复CondController里的实例属性污染问题
你的CondController方法里有个严重的bug:把函数参数赋值给了实例的this.identifier、this.key等属性,这会导致多调用时数据被覆盖!
比如createCondManager里的this.identifier = identifier;完全没必要,而且如果同时调用两次这个方法,第二次的identifier会覆盖第一次的,导致逻辑混乱。修改这些方法:class CondController { constructor() { this.objList = []; this.obj = {}; this.countArray = []; } count() { return this.countArray.length; } // 去掉多余的this.identifier/this.primary_key赋值,用局部参数就行 createCondManager(identifier, primary_key) { const taskManager = new CondManager(identifier, primary_key); this.objList.push(taskManager); this.obj[identifier] = taskManager; this.countArray.push(0); return taskManager; } addCond(identifier, text, column) { const current_cond_list = this.obj[identifier]; current_cond_list.insert_cond(text, column); } getKey(key) { // 去掉this.key赋值,直接用参数key for (let i = 0; i < this.objList.length; i++) { if (this.objList[i].primary_key === key) { return this.objList[i]; } } // 建议加个兜底,避免找不到时返回undefined console.warn(`No CondManager found for key: ${key}`); return null; } }验证primary_key的匹配性
确保optimizer_params调用createCondManager时传入的primary_key,和buy_conds[i].fk_list_id的值完全一致(类型也要匹配,比如都是数字或都是字符串),否则getKey会找不到对应实例。
最后验证
修改后,先确认optimizer_params执行完后objList里有数据,再进入for循环。你可以在for循环前加个打印:
await optimizer_params(buy_conds, "_BUY", "param_buy"); console.log("After optimizer_params:", condListController.objList.length); // 应该输出大于0的数
这样就能解决这个诡异的问题啦!
备注:内容来源于stack exchange,提问作者Soma Juice

