为何for of循环结合splice无法正确删除连续重复字符?
问题描述
我想实现移除字符串中指定字符的功能,比如移除apple里的所有p。但写的代码只能删掉第一个p,输出aple;输入aplpe时却能得到正确结果ale。测试发现原字符串里连续p数量是偶数次时,部分p删不掉。我猜是for of循环里修改原数组导致的,但不知道具体原因。代码如下:
function removeChar(str, char) { let arr = str.split(''); for (let x of arr) { if (x === char) { arr.splice(arr.indexOf(x), 1); console.log(arr); } } let word = arr.join(''); console.log(word); //prints 'aple' } removeChar('apple', 'p');
问题原因
确实是在for of循环中修改原数组导致的问题。for of依赖数组的迭代器来遍历,迭代器会按初始的索引顺序推进。当你用splice删除元素时,数组长度缩短,后续元素会自动往前移位,但迭代器不会调整自身的遍历位置,直接跳过了被挪过来的目标元素。
拿apple转成的['a','p','p','l','e']举例:
- 第一次循环遍历到索引1的
p,删除后数组变成['a','p','l','e'],此时迭代器的下一个遍历目标是索引2; - 但原索引2的
l已经挪到了索引1,原本在索引1的p现在处于索引1的位置,迭代器直接跳到索引2的l,完全跳过了这个p,所以最后只剩一个p没被删掉。
而aplpe转成['a','p','l','p','e']时,两个p不连续:
- 第一次删除索引1的
p后,数组变成['a','l','p','e'],迭代器下一个目标是索引2; - 此时索引2的元素正好是
p,能被正常检测并删除,所以结果正确。
解决方案
给你几个实用的解决办法:
方法1:倒序遍历数组
倒着遍历的话,删除前面的元素不会影响后面未遍历的元素索引,完全避开移位导致的跳过问题:
function removeChar(str, char) { let arr = str.split(''); for (let i = arr.length - 1; i >= 0; i--) { if (arr[i] === char) { arr.splice(i, 1); } } let word = arr.join(''); console.log(word); } removeChar('apple', 'p'); // 输出 'ale'
方法2:使用filter方法(最简洁)
直接用数组的filter方法过滤掉目标字符,不用手动修改原数组,代码更简洁易读:
function removeChar(str, char) { let word = str.split('').filter(item => item !== char).join(''); console.log(word); } removeChar('apple', 'p'); // 输出 'ale'
方法3:字符串replace方法(最高效)
不用转数组,直接用字符串的replace配合全局正则匹配所有目标字符,性能更优:
function removeChar(str, char) { // 转义正则特殊字符,避免语法冲突 let regex = new RegExp(char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'); let word = str.replace(regex, ''); console.log(word); } removeChar('apple', 'p'); // 输出 'ale'
内容的提问来源于stack exchange,提问作者WevDevNoob
相关产品推荐
相关产品推荐

