Discord.js读取undefined的name属性触发TypeError报错排查
报错根因
触发TypeError: Cannot read properties of undefined (reading 'name')是因为代码中用于定位Scammere数组元素的index变量值越界,访问jsonData.Scammere[index]时返回了undefined,再读取name属性就会抛出该错误,具体逻辑漏洞如下:
- 查找玩家索引的for循环逻辑错误:遍历过程中只要当前元素和目标玩家名不匹配,就会把
index覆盖为jsonData.Scammere.length + 1,哪怕之前已经匹配到正确索引,也会被后续遍历的非匹配项覆盖;而JS数组索引从0开始计数,数组长度+1必然是超出数组范围的无效索引。 - 新增玩家条目后未更新
index值:当判定为新玩家执行push操作后,没有将index设置为新元素对应的索引,后续读取元素时会定位到错误位置。 - 持久化逻辑存在缺陷:用
require()加载JSON文件时Node.js会缓存模块,对jsonData的修改不会自动写入本地test.json,进程重启后数据会丢失;同时node-localstorage的键名拼接多了空格、未处理空值转NaN的问题,会导致计数逻辑异常。
修复步骤
- 移除重复的遍历逻辑,直接用数组原生
findIndex方法一次性获取目标玩家索引,避免手动写循环出现变量覆盖问题,未找到匹配项时findIndex会返回-1,可直接用来判断是否为新玩家。 - 新玩家条目push进数组后,手动将
index设置为新元素的索引(即数组长度-1)。 - 改用
fs模块读写JSON文件,替换原有require加载JSON的方式,保证数据修改能持久化到本地。 - 修正localStorage键名拼接逻辑,增加空值判断,避免出现NaN的计数错误。
- 提前把当前操作的玩家对象存为单独变量,避免重复写
jsonData.Scammere[index]的冗余代码,也减少索引写错的概率。
修复后完整代码
const fs = require('fs'); const path = require('path'); const { LocalStorage } = require('node-localstorage'); const localStorage = new LocalStorage('./scratch'); // 读写Scammer数据的工具函数 function getScammerData() { const file = path.join(__dirname, 'test.json'); return JSON.parse(fs.readFileSync(file, 'utf8')); } function saveScammerData(data) { const file = path.join(__dirname, 'test.json'); fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8'); } module.exports = { name: 'addscammer', description: '添加诈骗者记录指令', execute(message, args, Discord) { if (!args[0]) return message.reply('Du skal skrive personens ingame navn'); const inputName = args[0].toString(); const targetName = inputName.toUpperCase(); // 权限校验 const hasPerm = message.member.roles.cache.has('850790004275019796') || message.member.roles.cache.has('850790306915942400'); if (!hasPerm) return message.reply('你没有使用该指令的权限'); const scammerData = getScammerData(); // 直接查找目标索引 const targetIndex = scammerData.Scammere.findIndex(item => item.name.toUpperCase() === targetName); const isNewPlayer = targetIndex === -1; let operateIndex = targetIndex; if (isNewPlayer) { // 新增条目 scammerData.Scammere.push({ name: inputName, timesScamed: 1, timesTrusted: 0 }); // 新元素索引为数组长度减1 operateIndex = scammerData.Scammere.length - 1; } else { // 更新已有条目计数 const storeKey = `storedScams_${scammerData.Scammere[operateIndex].name}`; const storedVal = localStorage.getItem(storeKey); const baseCount = storedVal ? parseInt(storedVal) : scammerData.Scammere[operateIndex].timesScamed; scammerData.Scammere[operateIndex].timesScamed = baseCount + 1; localStorage.setItem(storeKey, scammerData.Scammere[operateIndex].timesScamed.toString()); } // 持久化到JSON文件 saveScammerData(scammerData); const currentPlayer = scammerData.Scammere[operateIndex]; setTimeout(() => { let hexColor = '#000'; let status = 'scammer'; if (!isNewPlayer) { if (currentPlayer.timesScamed > currentPlayer.timesTrusted) { hexColor = '#ff0000'; status = 'Scammer'; } else { hexColor = '#43eb34'; status = 'Trusted'; } } else { hexColor = '#c4c4c4'; status = 'Måske'; } const embed = new Discord.MessageEmbed() .setColor(hexColor) .setTitle(`Data om: ${currentPlayer.name}`) .setAuthor({ name: currentPlayer.name, iconURL: `https://minotar.net/avatar/${currentPlayer.name}/128.png`, url: 'https://discord.js.org' }) .setThumbnail(`https://mc-heads.net/body/${currentPlayer.name}/128.png`) .addFields( { name: 'Status', value: status }, { name: 'Scammet', value: currentPlayer.timesScamed.toString() }, { name: 'Trusted', value: currentPlayer.timesTrusted.toString() } ); message.channel.send({ embeds: [embed] }); }, 500); } }
注意事项
- 原有JSON文件的字段名是
Scammere,后续读写时注意拼写一致,不要漏写末尾的e。 - 数组访问前建议增加非空判断,比如
if(!currentPlayer) return message.reply('数据读取异常'),可以避免后续迭代时再出现同类undefined报错。
内容的提问来源于stack exchange,提问作者Ramunator
相关产品推荐
相关产品推荐

