JavaScript循环向数组push对象时所有元素重复如何解决
问题根因
你全局定义的callInfo属于引用类型的对象,JS中对象传递的是内存地址引用,你在整个循环过程中每次调用createCallInformationObject时,传入的都是同一个callInfo的引用,函数内部修改的也是同一个内存地址上的对象,push到数组里的也都是这个对象的引用。所以循环结束后数组里的所有元素都指向同一块内存,值自然就是最后一次修改的结果。
循环过程中控制台打印看起来值不同是浏览器控制台的懒加载特性导致的:你打印时对象确实是当时的值,但等你循环结束后再展开控制台的对象,浏览器才会去读取该内存地址当前的最终值,所以看起来所有元素都重复了。
修复方案
方案1:每次循环创建新的模板对象副本
调用函数时不要复用全局的callInfo,每次传入新生成的模板对象即可:
callEvents.forEach(call => { // 每次生成新的模板对象,避免引用复用 const newCallInfo = { whoStartedCall: false, participentOne: false, participentTwo: false, duration: false, timeStarted: false, timeEnded: false, callId: false, callDate: false, callType: false, } let callObject = createCallInformationObject(call, newCallInfo) if (callObject) { callInformationArray.push(callObject) } })
方案2:将模板对象封装到函数内部(更推荐)
直接把模板定义放到createCallInformationObject函数内部,每次调用函数时自动生成新的对象,从根源避免外部引用复用的问题,也不需要再额外传模板参数:
/** * Creates the call information object from the Skype Import data * @param {Object} call * @returns CallInformationObject with filled data */ function createCallInformationObject(call) { // 内部每次新建模板对象 const CallInformationObject = { whoStartedCall: false, participentOne: false, participentTwo: false, duration: false, timeStarted: false, timeEnded: false, callId: false, callDate: false, callType: false, } let parser = new DOMParser(), callInformation = parser.parseFromString(call.content, 'text/xml'), callParticipents = callInformation.getElementsByTagName('name'), callIdentifier = callInformation.getElementsByTagName('partlist')[0], callType = callIdentifier.attributes['type'], callDate = new Date(call.originalarrivaltime), callEventId = callIdentifier.attributes['callId'], callDurationArray = callInformation.getElementsByTagName('duration'), callDuration = null if (!callType || !callEventId) { return } if (callDurationArray.length !== 0) { callDuration = callDurationArray[0].innerHTML } CallInformationObject.callType = callType.value CallInformationObject.callId = callEventId.value CallInformationObject.whoStartedCall = call.displayName CallInformationObject.callDate = callDate CallInformationObject.duration = callDuration callParticipents = getCallParticipents(callParticipents) CallInformationObject.participentOne = callParticipents[0] CallInformationObject.participentTwo = callParticipents[1] if (!CallInformationObject.whoStartedCall) { CallInformationObject.whoStartedCall = CallInformationObject.participentOne } return CallInformationObject }
修改后调用方式调整为:
let callObject = createCallInformationObject(call)
内容的提问来源于stack exchange,提问作者SamWalker
相关产品推荐
相关产品推荐

