我试图根据另一个数组中不存在的索引从数组中删除一个单词,但当我使用拼接和过滤方法时,我观察到奇怪的行为。
谁能解释一下下面的场景吗? 为什么在这两种情况下都会发生这样的情况,即使同一个对象在迭代中被更改?
文字
['一'、'二'、'三'、'四'、'五'、'六'、'七']
可移除文字
['四', '二', '八']
let words = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'];
let removedWords = ['four', 'two', 'eight'];
words.forEach((word) => {
console.log(word);
if (removedWords.includes(word)) {
words = words.filter((removableWord) => removableWord !== word)
}
});
/* Output */
//one
//two
//three
//four
//five
//six
//seven
let words = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'];
let removedWords = ['four', 'two', 'eight'];
words.forEach((word, index) => {
console.log(word);
if (removedWords.includes(word)) {
words.splice(index, 1);
}
});
/* Output */
//one
//two
//four
//six
//seven
正如本文中提到的Mozila document forEach() 在迭代之前不会复制数组。在过滤并分配回原始对象后,它的行为不应该与拼接相同吗?
注意:补充一下,splice 方法会对原始数组进行更改,而 filter 方法会创建数组的新副本,并且不会更改原始数组,但在给定的情况下例如(过滤后),结果被分配回原始数组。
最佳答案
您的第一个示例也有效,但您的 console.log 可能会让您感到困惑。在过滤之前,您在 for 循环中为每个单词记录一次。
只需在循环后记录结果 words
即可查看其是否有效。
let words = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'];
let removedWords = ['four', 'two', 'eight'];
words.forEach((word, index, iterationArray) => {
console.log(index, word, iterationArray.length, words.length);
if (removedWords.includes(word)) {
words = words.filter((removableWord) => removableWord !== word)
}
});
console.log(words);
对OP评论的回答:
所以我猜你很困惑
"forEach() does not make a copy of the array before iterating"
- 确实,
forEach()
不会创建副本:但是您在循环内创建副本 - 开头的变量
words
是对原始数组的引用['one', 'two', ' Three', 'four', ' Five', 'six' , '七']
- 现在您调用
words.forEach()
这是一个返回此数组上的迭代器的函数(迭代器将始终指向此原始数组,无论您是否更改词
引用点稍后)- 我已将第三个参数
iterationArray
添加到forEach
,这是迭代器使用的数组 - 我还在循环内添加了一个
console.log
:注意,iterationArray
不会改变,但words.length
会改变更改(因为您为其分配了新数组)
- 我已将第三个参数
- 在循环中使用
words.filter
创建一个新数组(例如['one', 'two', ' Three', ' Five', 'six', 'seven ']
)并更改words
变量以指向这个新数组 - 但这不会更改您之前已经创建的迭代器:即forEach
循环仍然“指向”原始数组
对于第二个示例:
let words = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'];
let removedWords = ['four', 'two', 'eight'];
words.forEach((word, index, iterationArray) => {
console.log(word, index, iterationArray.length);
if (removedWords.includes(word)) {
words.splice(index, 1);
}
});
console.log(words);
- 同样,
forEach
不会复制 - 迭代器将指向原始数组
- 但是现在您在循环内部更改了原始数组:即
- 当循环到达索引 1 处的单词“two”时,将原始数组更改为
['one', ' Three', 'four', ' Five', 'six', 'seven']
- 换句话说:您删除了索引 1 处的项目,而其他项目则向左移动
- 当循环到达索引 1 处的单词“two”时,将原始数组更改为
- 现在迭代器将继续,下一个索引是 2
- 由于您更改了原始数组,索引 2 处的值现在为
four
(并且您错过了单词trhee
,它现在位于迭代器的索引 1 处)已经处理完毕
- 由于您更改了原始数组,索引 2 处的值现在为
- 注意,循环内的
console.log
:您可以看到iterationArray
已更改
关于javascript - 为什么在 JavaScript 的 forEach 中删除元素会有奇怪的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73117942/