为何调用splice删除数组中间元素时,被删除的却是最后一个元素?
问题分析与解决方案
这问题我之前也碰到过好几次,核心原因就是你在forEach循环里直接用splice修改了enemies原数组,彻底打乱了遍历的索引顺序!
为什么会删错元素?
forEach是基于数组初始的长度和索引来遍历的。当你在遍历过程中用splice删除一个元素时,数组长度会立刻减1,后面所有元素的索引都会往前挪一位。举个例子:
假设你的enemies数组是[E0, E1, E2, E3, E4, E5, E6](共7个元素,索引0到6),你本来想删除索引5的E5,但如果之前的遍历已经触发过删除操作,当前的index就会和你预期的元素不对应——比如数组长度变短后,原来的索引5可能现在指向的是原本的最后一个元素E6,这时候执行splice(index, 1)自然就删错了目标。
另外,你的代码嵌套了bullets的循环,如果多个子弹命中同一个敌人,还可能导致多次删除同一位置元素,进一步加剧索引混乱的问题。
两种靠谱的解决方法
方法1:倒序遍历数组
倒着遍历enemies数组(从最后一个元素往前),这样即使删除当前元素,前面的元素索引不会受到影响,完全不会出现错位:
// 用for循环倒序遍历,代替forEach for (let i = enemies.length - 1; i >= 0; i--) { const element = enemies[i]; for (let j = bullets.length - 1; j >= 0; j--) { const bullet = bullets[j]; // 完整的碰撞检测逻辑 if (bullet.X + 5 > element.X && bullet.X < element.X + 30 && bullet.Y + 5 > element.Y && bullet.Y < element.Y + 30) { // 删除对应的敌人和子弹 enemies.splice(i, 1); bullets.splice(j, 1); // 一个子弹只能命中一个敌人,直接跳出内层循环 break; } } }
这里同时把bullets的循环也改成倒序,避免子弹数组修改后同样出现索引问题。
方法2:先收集要删除的索引,再批量删除
先遍历数组,把需要删除的敌人索引存在临时数组里,之后统一删除(注意要倒序删除,避免索引错位):
const indexesToRemove = []; // 先遍历收集需要删除的索引 enemies.forEach((element, index) => { for (let i = 0; i < bullets.length; i++) { const bullet = bullets[i]; if (bullet.X + 5 > element.X && bullet.X < element.X + 30 && bullet.Y + 5 > element.Y && bullet.Y < element.Y + 30) { indexesToRemove.push(index); // 删除已命中的子弹,避免重复检测 bullets.splice(i, 1); i--; // 子弹数组长度变短,回退索引修正循环 } } }); // 倒序删除敌人,防止索引错位 indexesToRemove.sort((a, b) => b - a).forEach(index => { enemies.splice(index, 1); });
这种方法的好处是不会在遍历过程中修改原数组,逻辑更清晰,不容易出错。
额外提醒
你代码里的&...应该是&&吧?别漏了第二个&,不然会变成按位与运算,直接导致碰撞检测逻辑出错。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

