Discord.js挖矿Bot开发:数组push元素无效返回undefined问题
Discord机器人挖矿掉落功能异常排查
问题现象
开发支持资源挖矿玩法的Discord机器人时遇到逻辑异常:控制台可正常打印掉落的物品对象,但物品无法被推入droppedItems数组,最终无法将数组映射为字符串输出。
- 预期结果:返回由掉落物品对象组成的数组,每个对象包含
name、amount、category三个属性 - 实际结果:函数最终返回
undefined - 已尝试无效方案:给push操作加await、将数组声明为常量等微调操作
- 初始疑问:是否是数组定义在循环外部导致异常
问题代码
module.exports.addItem = async (client, user, item, amount, interaction) => { if(!client instanceof Client) throw new Error("Client is not an instance of Discord.js Client"); if(!user instanceof User) throw new Error("User is not an instance of Discord.js User"); if(!item) throw new Error("Item is not defined"); if(!amount) throw new Error("Amount is not defined"); if(typeof amount !== "number") throw new Error("Amount is not a number"); if(!interaction) throw new Error("Interaction is not defined"); if(!interaction instanceof Interaction) throw new Error("Interaction is not an instance of Discord.js Interaction"); const userFound = await client.database.user.findOne({ userID: user.id }); if(!userFound) return interaction.followUp({ content: `You haven't started your journey yet!` }); const itemFound = userFound.inventory.find(i => i.name === item.name); if(!itemFound) { await client.database.user.findOneAndUpdate( { userID: user.id, }, { userID: user.id, $push: { inventory: { name: item.name, amount, category: item.category } } } , { upsert: true, }); } else { await client.database.user.findOneAndUpdate( { userID: user.id, }, { userID: user.id, $pull: { inventory: { name: item.name } } } , { upsert: true, }); await client.database.user.findOneAndUpdate( { userID: user.id, }, { userID: user.id, $push: { inventory: { name: item.name, amount: item.amount + amount, category: item.category } } } , { upsert: true, }); } return { name: item.name, amount, category: item.category }; } module.exports.dropItem = async (client, user, category, interaction) => { if(!client instanceof Client) throw new Error("Client is not an instance of Discord.js Client"); if(!user instanceof User) throw new Error("User is not an instance of Discord.js User"); if(!category) throw new Error("Category is not defined"); if(typeof category !== "string") throw new Error("Category is not a string"); let droppedItems = []; for (const item of client.items) { for(let i = 0; i < client.items.length; i++) { if(!client.items[i].category === category) return; } const dropAmount = Math.floor((Math.random() * 3) + 1); const dropChance = Math.floor(Math.random() * 100); if(dropChance > item.dropChance) return; const itemDropped = await this.addItem(client, user, item, dropAmount, interaction) console.log(itemDropped); // This logs the item correctly droppedItems.push(itemDropped); } return droppedItems; // This always returns "undefined" }
问题根因
和数组定义位置无关,是代码里的逻辑错误导致函数提前终止、调用失效,具体问题如下:
- 提前return终止整个函数:两处错误的
return是返回undefined的核心原因- 内层多余的for循环中,只要遍历到任意一个物品分类和目标分类不符,就直接执行
return终止整个dropItem函数,根本走不到后续返回数组的逻辑 - 掉落概率判断处,只要当前物品概率判定未掉落,就直接
return终止函数,应该用continue跳过当前物品的循环,而不是退出整个函数
- 内层多余的for循环中,只要遍历到任意一个物品分类和目标分类不符,就直接执行
- 类型校验写法失效:所有
!xxx instanceof Class的写法存在运算符优先级错误,!优先级高于instanceof,实际执行逻辑是(!xxx) instanceof Class,永远返回false,所有参数校验完全不生效 - 方法调用指向错误:CommonJS模块下直接用
this.addItem调用会出现指向问题,this不指向当前导出对象,会导致addItem调用失败 - 物品数量计算错误:更新已有物品库存时,错误使用了基础配置物品的
item.amount(基础物品配置不存在该字段,值为undefined),应该用查询到的用户库存中对应物品的itemFound.amount + 新增数量计算 - 冗余循环逻辑:外层已经在遍历所有物品,内层再写一层for循环遍历所有物品检查分类完全多余,直接判断当前遍历到的
item.category是否匹配目标分类即可
修复后核心逻辑参考
// 先单独定义方法,避免this指向问题 const addItem = async (client, user, item, amount, interaction) => { // 修复instanceof校验写法,加括号改变优先级 if(!(client instanceof Client)) throw new Error("Client is not an instance of Discord.js Client"); if(!(user instanceof User)) throw new Error("User is not an instance of Discord.js User"); if(!item) throw new Error("Item is not defined"); if(!amount) throw new Error("Amount is not defined"); if(typeof amount !== "number") throw new Error("Amount is not a number"); if(!interaction) throw new Error("Interaction is not defined"); if(!(interaction instanceof Interaction)) throw new Error("Interaction is not an instance of Discord.js Interaction"); const userFound = await client.database.user.findOne({ userID: user.id }); if(!userFound) return interaction.followUp({ content: `You haven't started your journey yet!` }); const itemFound = userFound.inventory.find(i => i.name === item.name); if(!itemFound) { await client.database.user.findOneAndUpdate( { userID: user.id }, { $push: { inventory: { name: item.name, amount, category: item.category } } }, { upsert: true } ); } else { await client.database.user.findOneAndUpdate( { userID: user.id }, { $pull: { inventory: { name: item.name } } }, { upsert: true } ); await client.database.user.findOneAndUpdate( { userID: user.id }, { $push: { inventory: { name: item.name, amount: itemFound.amount + amount, // 修复数量取值 category: item.category } } }, { upsert: true } ); } return { name: item.name, amount, category: item.category }; } const dropItem = async (client, user, category, interaction) => { // 修复instanceof校验 if(!(client instanceof Client)) throw new Error("Client is not an instance of Discord.js Client"); if(!(user instanceof User)) throw new Error("User is not an instance of Discord.js User"); if(!category) throw new Error("Category is not defined"); if(typeof category !== "string") throw new Error("Category is not a string"); const droppedItems = []; for (const item of client.items) { // 删除冗余内层循环,直接判断当前物品分类,不匹配就跳过当前循环 if(item.category !== category) continue; const dropAmount = Math.floor((Math.random() * 3) + 1); const dropChance = Math.floor(Math.random() * 100); // 未掉落就跳过当前物品,不要return退出整个函数 if(dropChance > item.dropChance) continue; // 直接调用定义好的addItem,不用this const itemDropped = await addItem(client, user, item, dropAmount, interaction) console.log(itemDropped); droppedItems.push(itemDropped); } return droppedItems; } // 最后统一导出 module.exports = { addItem, dropItem }
内容的提问来源于stack exchange,提问作者Magma
相关产品推荐
相关产品推荐

