使用Array.forEach删除数组元素仅生效一次,求原因及替代方案
问题分析与解决方案
为什么用forEach删除会漏元素?
forEach遍历数组时,是按照初始的索引顺序依次执行的。当你用splice删除当前元素后,数组后面的元素会自动向前移动一位填补空位,导致后续的索引对应关系错乱。举个例子:
假设数组是[A(已完成), B(已完成), C],第一次遍历索引0,删掉A后数组变成[B, C],接下来forEach会直接跳到索引1,但此时数组的索引1是C,原本的B已经跑到了索引0的位置,就这样被跳过了,最终只删掉了第一个已完成任务。
替代方案
方案1:用Array.filter(最推荐)
filter方法会返回一个新数组,包含所有符合筛选条件的元素。我们只需要保留completion为false的任务,直接替换原数组即可,逻辑简洁且不易出错:
if (confirmDelete) { // 过滤出未完成的任务,替换原tasks数组 tasks = tasks.filter(task => !task.completion); // 删除页面上的已完成DOM元素 allCompletedTasksOnPage.forEach(task => task.remove()); }
说明:filter不会修改原数组,而是返回新数组,直接赋值给tasks就完成了删除已完成任务的操作,完全避开了遍历过程中数组长度变化的问题。
方案2:倒序for循环遍历
如果必须修改原数组(比如有其他代码依赖原数组的引用),可以用倒序的for循环。从数组最后一个元素往前遍历,删除元素不会影响前面未遍历的元素索引:
if (confirmDelete) { // 倒序遍历数组 for (let i = tasks.length - 1; i >= 0; i--) { if (tasks[i].completion === true) { tasks.splice(i, 1); } } allCompletedTasksOnPage.forEach(task => task.remove()); }
说明:倒序遍历时,删掉当前元素后,前面的元素索引不会发生变化,因此不会跳过任何需要删除的元素。
方案3:用while循环查找并删除
还可以用while循环不断查找第一个已完成的任务,直到找不到为止:
if (confirmDelete) { let index = tasks.findIndex(task => task.completion === true); while (index !== -1) { tasks.splice(index, 1); index = tasks.findIndex(task => task.completion === true); } allCompletedTasksOnPage.forEach(task => task.remove()); }
说明:每次找到第一个符合条件的元素索引并删除,之后继续查找下一个,直到找不到符合条件的元素(index为-1)时停止循环。
内容的提问来源于stack exchange,提问作者Sufiyan Malik
相关产品推荐
相关产品推荐

