为什么在JavaScript的filter方法内修改数组元素未按预期生效?
现象背后的核心逻辑:Array.prototype.filter 的遍历规则
首先明确filter方法的3个关键运行机制:
filter启动遍历前会先锁定本次遍历的数组长度,后续修改原数组长度不会调整遍历次数- 遍历按索引从
0到length-1顺序执行,每一轮遍历进入回调前,就会预先读取当前索引的元素值,用于后续判断是否保留到结果数组,已经处理过的索引不会再次访问 - 回调参数中的
arr是原数组的引用,修改arr就是直接修改原数组本身
第一种情况:代码中写arr[index + 1] *= 2
你的初始数组为[1, 0, 1, 2, 3, 0, 0, 4, 0],长度为9,遍历过程如下:
- 遍历索引
0:进入回调前已经读取索引0的初始值1,回调中修改arr[1](索引1的值)为0*2=0,返回true,把预先读取的1存入结果数组 - 遍历索引
1:进入回调前读取的是上一步修改后的索引1的值0,回调中修改arr[2]为1*2=2,返回true,把0存入结果数组 - 以此类推,每一轮修改的都是下一轮要遍历的索引的值,下一轮遍历读取的就是已经修改后的数值
- 最后一轮遍历索引
8:修改arr[9]属于超出数组边界的无效操作,返回true,把上一步修改后的索引8的值存入结果数组
最终filter返回的结果数组中,只有索引0的值是没被修改过的初始值,其余元素都是被乘2后的值,和你观测到的现象一致。
第二种情况:代码改成arr[index] *= 2
遍历过程如下:
- 遍历索引
0:进入回调前已经读取了索引0的初始值1,之后你在回调中把arr[0]修改为2,但filter本轮返回true时存入结果数组的还是预先读取的1 - 遍历索引
1:进入回调前读取索引1的初始值0,回调中把arr[1]修改为0,存入结果数组的还是预先读取的0 - 所有索引遍历都遵循这个逻辑:你修改当前索引值的操作,发生在
filter读取该索引值之后,修改结果只会作用于原数组,不会进入本次filter的返回结果
你此时打印原数组list会发现原数组的所有元素已经被乘2,只是修改后的结果没有出现在filter返回的新数组中,符合你观测到的“所有元素都不会被修改”的表现。
补充建议
filter的设计定位是筛选符合条件的数组元素,不应该在回调中执行修改原数组的副作用操作。如果要实现数组元素批量映射转换,应该使用专门的Array.prototype.map方法,正确写法如下:
const multiplyByTwo = (ar) => ar.map(item => item * 2)
内容的提问来源于stack exchange,提问作者sk65plus
相关产品推荐
相关产品推荐

