React函数无法正确构造数组的问题排查
问题描述
我写了一个生成默认数据的函数,代码如下:
const generateDefaultDataForModel = (data: any) => { const numBlocks = data && Object.keys(data).length > 0 ? data[Object.keys(data)[0]].length : 3; console.log("numBlocks", numBlocks); const defaultWeight = 1 / Object.keys(originalWeights).length; console.log("defaultWeight", defaultWeight); const defaultData = Array.from({ length: numBlocks }, () => [defaultWeight, true]); console.log("defaultData", defaultData); const test = Array.from({ length: numBlocks }, () => [defaultWeight, true]); console.log("test", test); return test; };
触发onClick调用该函数时,控制台显示numBlocks=3、defaultWeight=0.9,但defaultData生成的是[[0, true], [0, true], [0, true]],后续完全相同代码生成的test却是[[0.9, true], [0.9, true], [0.9, true]]。试过修改变量名、硬编码数值都没解决,甚至注释掉defaultData这一行后,test也会输出错误结果。
补充调用信息
调用函数的代码如下:
const newCurrentWeights = JSON.parse(JSON.stringify(currentWeights)); generateDefaultDataForModel(originalWeights); newCurrentWeights[model] = generateDefaultDataForModel(originalWeights);
currentWeights的结构为{key: {key: [value, bool]}}。单独调用generateDefaultDataForModel(originalWeights)输出正确,但赋值给newCurrentWeights[model]时就会出现错误。
问题原因分析
originalWeights的键数量在两次调用间发生变化
函数中defaultWeight的计算依赖Object.keys(originalWeights).length,第二次调用函数时,这个长度可能已经被意外修改(比如第一次调用的副作用、JSON.parse(JSON.stringify)的间接影响,或是其他关联代码的修改),导致1/长度的结果被稀释为接近0的数值,只是控制台打印的顺序可能让你误以为defaultWeight还是0.9。闭包引用的变量被后续操作修改
Array.from的回调是闭包,它引用的defaultWeight如果在回调执行前被意外修改(比如异步逻辑干扰、其他代码的副作用),会导致生成的数组元素值异常。不过结合控制台打印的内容,更可能是第二次调用时defaultWeight的计算值本身就变了。originalWeights与currentWeights存在引用关联
如果originalWeights是currentWeights的引用而非深拷贝,那么JSON.parse(JSON.stringify(currentWeights))后的操作可能间接修改了originalWeights的结构,导致第二次调用函数时计算出的defaultWeight异常。
验证与修复建议
- 先在两次调用前打印
originalWeights的键数量,确认是否变化:console.log("首次调用前的originalWeights长度:", Object.keys(originalWeights).length); generateDefaultDataForModel(originalWeights); console.log("二次调用前的originalWeights长度:", Object.keys(originalWeights).length); newCurrentWeights[model] = generateDefaultDataForModel(originalWeights); - 固化
defaultWeight的值,避免闭包引用的潜在问题:const defaultWeight = 1 / Object.keys(originalWeights).length; // 用新变量固化数值,避免后续意外修改影响闭包 const fixedWeight = defaultWeight; const defaultData = Array.from({ length: numBlocks }, () => [fixedWeight, true]); const test = Array.from({ length: numBlocks }, () => [fixedWeight, true]); - 检查所有涉及
originalWeights的代码,确认是否存在未预期的修改操作(比如赋值、添加/删除键)。
内容的提问来源于stack exchange,提问作者Denver Dang

