Node.js跨文件修改导出数组变量时原文件与导入文件值不一致的技术咨询
这问题我之前踩过坑,本质是Node.js模块导出时引用类型的赋值逻辑在搞事情,咱们慢慢理清楚:
问题核心原因
首先得搞懂Node.js模块导出和引用的底层逻辑:
- 你在
DataCommon.js里定义的gDataChange是数组,属于引用类型。当你导出module.exports = { gDataChange, ... }时,其实是把这个数组的内存引用地址复制了一份放到导出对象里。 - 当用
splice修改数组时,你是在原数组的内存地址上直接修改内容——模块内和test.js里的gDataChange都指向同一个内存块,所以两边的值肯定同步。 - 但
filter方法会返回一个全新的数组实例(新的内存地址),然后你在setArray里执行gDataChange = [...lArray],这相当于把模块内的gDataChange变量重新赋值,让它指向新数组的引用。但test.js里的gDataChange还是死死盯着原来那个旧数组的内存地址!这就导致两边指向不同的内存块,值自然不一样了。
几种解决方法
根据你的场景,推荐以下几种方案:
方案一:始终修改原数组,避免重新赋值
这种方法不用改太多代码,核心是不让gDataChange指向新的数组,而是清空原数组后再添加新元素:
修改DataCommon.js里的setArray函数:
function setArray(lArray) { gDataChange.length = 0; // 清空原数组的内容,引用地址不变 gDataChange.push(...lArray); // 把新元素推入原数组 }
这样不管怎么操作,两边的gDataChange都指向同一个内存块,值会自动同步。
方案二:用包裹对象存储数组
把数组放到一个对象里,因为对象也是引用类型,修改对象的属性不会改变对象本身的引用:
修改DataCommon.js:
// 用一个对象包裹数组 let dataStore = { gDataChange: [] }; function printArray() { console.log('DataCommon.js call ', dataStore.gDataChange); } function setArray(lArray) { // 直接修改对象的属性,对象引用不变 dataStore.gDataChange = [...lArray]; } module.exports = { dataStore, printArray, setArray }
然后在test.js里通过对象属性访问数组:
var { dataStore, printArray, setArray} = require('../../res/DataCommon.js'); if (!dataStore.gDataChange.length) { dataStore.gDataChange.push(1); dataStore.gDataChange.push(2); dataStore.gDataChange.push(1); dataStore.gDataChange.push(3); } function testGlobalVar() { let newData = [...dataStore.gDataChange]; newData = newData.filter((number)=>{ return number != 1; }); setArray(newData); } testGlobalVar(); console.log('test.js call ', dataStore.gDataChange); printArray(); setTimeout(() => { console.log(dataStore.gDataChange); }, 10000);
这种方法适合需要频繁替换整个数组的场景,比较健壮。
方案三:用getter函数获取最新引用
如果不想改太多结构,可以给模块加一个getter函数,每次需要数组时都调用函数获取最新的引用:
修改DataCommon.js:
let gDataChange = []; function printArray() { console.log('DataCommon.js call ', gDataChange); } function setArray(lArray) { gDataChange = [...lArray]; } // 新增getter函数 function getGDataChange() { return gDataChange; } module.exports = { getGDataChange, printArray, setArray }
然后在test.js里替换所有直接访问gDataChange的地方为调用getGDataChange():
var { getGDataChange, printArray, setArray} = require('../../res/DataCommon.js'); if (!getGDataChange().length) { getGDataChange().push(1); getGDataChange().push(2); getGDataChange().push(1); getGDataChange().push(3); } function testGlobalVar() { let newData = [...getGDataChange()]; newData = newData.filter((number)=>{ return number != 1; }); setArray(newData); } testGlobalVar(); console.log('test.js call ', getGDataChange()); printArray(); setTimeout(() => { console.log(getGDataChange()); }, 10000);
这种方法比较灵活,适合按需获取最新值的场景。
内容的提问来源于stack exchange,提问作者Frederick Steven
相关产品推荐
相关产品推荐

