循环中重复声明变量继承旧数组属性问题——工时模式日期截断异常
我有两个数组:organHolidayYears(存储年度的起始与结束日期)和sortedUserWorkingPatterns(存储员工常规工作日及排班模式的启用、停用日期)。我需要把排班模式绑定到对应的年度数组中,并截断排班模式的起止日期以匹配绑定的年度。
举个例子:如果员工某排班模式是2021年7月至2022年6月,那么这个模式要绑定到2021年(起止为2021年7月-12月),同时绑定到2022年(起止为2022年1月-6月)。
我写了嵌套循环遍历年度和排班模式,按需绑定,但截断后的起止日期被覆盖了。比如上面的例子里,2021和2022年度的排班模式起止日期都变成了2022年1月和2021年12月。我每次循环都重新声明了workingPatternsTemp变量,但它还是继承旧循环的属性,请问原因和解决方法?
代码如下:
for (let j = 0; j < organHolidayYears.length; j++) { //cycle through holiday years and attach working patterns var holidayYear = organHolidayYears[j]; for (let i = 0; i < sortedUserWorkingPatterns.length; i++) { // cycle through working patterns and attach to given year if necessary var workingPatternsTemp = sortedUserWorkingPatterns[i]; var toDate = holidayYear.unixEndDate; //holiday year end date var fromDate = holidayYear.unixStartDate; //holiday year start date var workingPatternStartDate = workingPatternsTemp.unixStartDate; var workingPatternEndDate = workingPatternsTemp.unixEndDate; // CASE 1: PATTERN STARTS DURING YEAR if (workingPatternStartDate <= toDate && workingPatternStartDate >= fromDate) { // CASE 1a: PATTERN ENDS DURING YEAR if (workingPatternEndDate <= toDate) { holidayYear.workingPatterns.push(workingPatternsTemp); // add working pattern to holiday year } // CASE 1b: PATTERN ENDS AFTER YEAR else { workingPatternsTemp.unixEndDateInHolidayYear = toDate; // truncate working pattern at end of year holidayYear.workingPatterns.push(workingPatternsTemp); // add working pattern to holiday year } // CASE 2: PATTERN ENDS DURING YEAR (BUT DOES NOT START DURING YEAR) } else if (workingPatternEndDate <= toDate && workingPatternEndDate >= fromDate) { workingPatternsTemp.unixStartDateInHolidayYear = fromDate; // truncate working pattern at start of holiday year holidayYear.workingPatterns.push(workingPatternsTemp); // add working pattern to holiday year } // CASE 3: PATTERN DOES NOT OVERLAP YEAR else {} } }
核心问题是引用类型的赋值特性:workingPatternsTemp = sortedUserWorkingPatterns[i]并没有创建新对象,只是把原数组里的排班模式对象的引用赋值给了workingPatternsTemp。你每次修改workingPatternsTemp的属性(比如unixEndDateInHolidayYear),其实是直接修改了原数组sortedUserWorkingPatterns里的对应对象。当下一次循环处理另一个年度时,这个对象已经被上一次的修改污染,导致后续绑定的年度拿到的是已修改过的对象,最终出现日期覆盖的问题。
每次处理排班模式时,不要直接使用原对象的引用,而是创建原对象的拷贝(浅拷贝或深拷贝,根据对象结构选择),修改拷贝后的对象属性再添加到年度数组中。
具体实现方式:
- 浅拷贝:如果排班模式对象没有嵌套的子对象,用对象展开语法或
Object.assign创建拷贝:
// 替换原有的 var workingPatternsTemp = sortedUserWorkingPatterns[i]; var workingPatternsTemp = {...sortedUserWorkingPatterns[i]}; // 或者用 Object.assign // var workingPatternsTemp = Object.assign({}, sortedUserWorkingPatterns[i]);
- 深拷贝:如果排班模式对象有嵌套的子对象(比如内部还有数组或对象),用深拷贝,比如
JSON.parse(JSON.stringify())(注意:该方法不支持函数、Symbol等特殊类型):
var workingPatternsTemp = JSON.parse(JSON.stringify(sortedUserWorkingPatterns[i]));
修改后的完整代码示例:
for (let j = 0; j < organHolidayYears.length; j++) { var holidayYear = organHolidayYears[j]; for (let i = 0; i < sortedUserWorkingPatterns.length; i++) { // 创建原对象的浅拷贝,避免修改原数据 var workingPatternsTemp = {...sortedUserWorkingPatterns[i]}; var toDate = holidayYear.unixEndDate; var fromDate = holidayYear.unixStartDate; var workingPatternStartDate = workingPatternsTemp.unixStartDate; var workingPatternEndDate = workingPatternsTemp.unixEndDate; if (workingPatternStartDate <= toDate && workingPatternStartDate >= fromDate) { if (workingPatternEndDate <= toDate) { holidayYear.workingPatterns.push(workingPatternsTemp); } else { workingPatternsTemp.unixEndDateInHolidayYear = toDate; holidayYear.workingPatterns.push(workingPatternsTemp); } } else if (workingPatternEndDate <= toDate && workingPatternEndDate >= fromDate) { workingPatternsTemp.unixStartDateInHolidayYear = fromDate; holidayYear.workingPatterns.push(workingPatternsTemp); } } }
这样每次循环处理的都是独立的拷贝对象,修改属性不会影响原数组中的对象,也不会被后续循环覆盖之前的修改结果。
内容的提问来源于stack exchange,提问作者rainybird

