.splice
меняет ваш массив, в т.ч. и сдвигает элементы. Поэтому итератор просто проходит мимо некоторых элементов. Выходит такое поведение:
1)
Victor, deleted: false <-- iterator, i = 0, не удаляет
Misha, deleted: true
Alexey, deleted: true
Misha, deleted: false
2)
Victor, deleted: false
Misha, deleted: true <-- iterator, i = 1, удаляет
Alexey, deleted: true
Misha, deleted: false
Misha удален, на его место становится Alexey. А iterator идет дальше
3)
Victor, deleted: false
Alexey, deleted: true - пропущен
Misha, deleted: false <-- iterator, i = 2, не удаляет
Я бы рекомендовал отказаться от .splice
в случаях, когда мы изменяем массив, по которому и проходимся и вместо этого создавать новый. Есть варианты:
// 1, добавлять элементы в новый массив
const result = [];
users.forEach(user => {
if (!user.deleted) {
result.push(user);
}
})
// 2, использовать метод .filter. Он более предпочтительный
const result = users.filter(user => !user.deleted);
Про .filter
подробнее тут