为何forEach外数组未变更?如何保留过滤结果(避指定方案)
数组批量过滤后外部未更新的问题解析
使用场景
将n个对象数组包裹在一个数组中,一次性遍历所有数组并根据属性过滤对象。
需避开的已知方案
[arr1,arr2,arr3] = [arr1,arr2,arr3].map(...)
["arr1","arr2","arr3"].forEach(arrName => window[arrName] = .....)
问题
下述代码中,为什么forEach外部的数组没发生变化?这是不是传引用的问题?该怎么解决?
预期结果:
[{"B":"b","remove":false},{"C":"c"}] [{"A":"a","remove":false},{"C":"c","remove":false}] [{"B":"b","remove":false}]
实际结果:
[{"A":"a","remove":true},{"B":"b","remove":false},{"C":"c"}] [{"A":"a","remove":false},{"B":"b","remove":true},{"C":"c","remove":false}] [{"A":"a","remove":true},{"B":"b","remove":false},{"C":"c","remove":true}]
怎么才能让forEach内部的数组变更在外部生效?
代码示例
let arr1 = [ {"A":"a", "remove":true}, {"B":"b", "remove":false}, {"C":"c"} ]; let arr2 = [ {"A":"a", "remove": false}, {"B":"b", "remove": true}, {"C":"c", "remove": false} ]; let arr3 = [ {"A":"a", "remove": true}, {"B":"b", "remove": false}, {"C":"c", "remove": true} ]; [arr1, arr2, arr3] .forEach(arr => { arr = [...arr.filter(({remove})=> !remove)] console.log(JSON.stringify(arr)); // 内部打印是对的 }); console.log("---------------"); // 检查外部数组是否变更 [arr1, arr2, arr3].forEach(arr => console.log(JSON.stringify(arr)));
原因分析
这确实是传引用的误区。JavaScript里函数参数是按值传递的,但对于数组这种引用类型,传递的是引用的副本。
你在forEach回调里写arr = [...arr.filter(...)]时,只是把回调内部的局部变量arr重新赋值成了新数组,而外部的arr1/arr2/arr3还是指向原来的数组地址,完全没被修改。简单说:回调里的arr是个临时变量,改它不会影响外面的原变量。
解决方法
方法1:直接修改原数组内容
用splice方法清空原数组并插入过滤后的元素,这样直接操作原数组的内存地址,外部变量会同步更新:
[arr1, arr2, arr3].forEach(arr => { const filtered = arr.filter(({ remove }) => !remove); // 从索引0开始删除所有元素,再插入过滤后的新元素 arr.splice(0, arr.length, ...filtered); console.log(JSON.stringify(arr)); });
方法2:通过索引修改原数组引用
把数组存到一个临时变量里,遍历索引直接修改原引用:
const allArrays = [arr1, arr2, arr3]; allArrays.forEach((_, idx) => { allArrays[idx] = allArrays[idx].filter(({ remove }) => !remove); console.log(JSON.stringify(allArrays[idx])); }); // 此时arr1、arr2、arr3已经被更新为过滤后的数组
验证结果
执行上述任一方案后,外部的arr1/arr2/arr3都会变成预期的过滤后数组,再次打印时输出和预期一致。
内容的提问来源于stack exchange,提问作者embarassed2ask
相关产品推荐
相关产品推荐

