
本文详解因异步读取文件导致变量未定义,以及 json 结构含数组层级时误用点/方括号访问引发的 “cannot read properties of undefined” 错误,并提供完整、健壮的解决方案。
你遇到的错误 TypeError: Cannot read properties of undefined (reading 'scrams') 根源有两个关键问题:异步执行时机错误 和 JSON 数据结构理解偏差。
首先,fs.readFile() 是异步操作,其回调函数在文件读取完成之后才执行。而你在回调外部立即使用 datascrams[...]['scrams'],此时 datascrams 仍为初始空对象 {},尚未被赋值,因此 datascrams[interaction.options.getString('cube')] 返回 undefined,后续再访问 .scrams 必然报错。
其次,你的 JSON 结构中 "3x3" 对应的是一个数组(而非对象),其中每个元素才是包含 "scrams" 字段的对象:
{
"3x3": [
{ "scrams": "R U R' U' ..." }
]
}因此,正确路径是:data["3x3"][0]["scrams"],而非 data["3x3"]["scrams"] —— 缺少数组索引 [0] 会导致第二层访问失败。
✅ 正确做法是:
- 将逻辑完全移入 readFile 回调内(或改用 fs.promises.readFile + async/await);
- 解析 JSON 字符串(JSON.parse());
- 安全校验数据存在性(避免硬编码 [0] 导致新错误);
- 添加错误处理,提升鲁棒性。
以下是推荐的现代写法(使用 async/await + try/catch):
if (interaction.commandName === 'comp-scrambles') {
try {
const rawData = await fs.promises.readFile('scrambles.json', 'utf8');
const data = JSON.parse(rawData);
const cubeType = interaction.options.getString('cube');
const cubeData = data[cubeType];
if (!Array.isArray(cubeData) || cubeData.length === 0) {
return interaction.reply({ content: `❌ 未找到有效的 ${cubeType} 魔方打乱数据。`, ephemeral: true });
}
// 安全取第一个打乱(可按需扩展为随机选取)
const scram = cubeData[0].scrams;
if (typeof scram !== 'string') {
return interaction.reply({ content: `❌ 打乱数据格式异常,请检查 scrambles.json。`, ephemeral: true });
}
await interaction.reply(scram);
} catch (err) {
console.error('读取或解析 scrambles.json 失败:', err);
interaction.reply({ content: '⚠️ 服务器内部错误,请稍后重试。', ephemeral: true });
}
}? 注意事项:
- ❌ 不要重复声明 let datascrams = {} 后又在回调里 let datascrams = rawData —— 这会创建新局部变量,无法影响外层作用域;
- ✅ 始终对 JSON.parse() 的结果做类型校验(如 Array.isArray()、?.scrams 可选链或显式 if 判断);
- ✅ 生产环境建议将 JSON 加载逻辑抽离为独立工具函数,并配合缓存(如首次读取后存入内存),避免高频磁盘 I/O。
通过同步控制流与结构化数据验证,即可彻底规避 undefined 访问错误,让机器人稳定可靠地返回打乱序列。










