Почему не использовать простой численный цикл в JavaScript
Что проверяют
Не заученную мантру «for быстрее» или «map читаемее», а понимание, чем способы обхода различаются по смыслу: что они умеют перебирать, как ведут себя с асинхронным кодом и с пропусками в массиве.
Как отвечать
Классический for (let i = 0; i < arr.length; i++) даёт максимум контроля, но всё делается руками: индекс, условие, шаг. Причины предпочесть другое:
- перебираемое не всегда массив.
for...ofидёт по любому итерируемому объекту:Set,Map,NodeList, генератор, строка. Индексом по строке вы разрезаете суррогатные пары и ломаете эмодзи; - намерение видно из имени:
mapэто преобразование,filterотбор,reduceсвёртка,someиeveryпроверка. Индексный цикл про это молчит, читателю приходится разбирать тело; - нет места для ошибки на единицу и для случайно изменённого счётчика;
- в асинхронном коде
for...ofнормально работает сawait, аforEachнет: колбэк вернёт промис, обход пойдёт дальше и не дождётся ничего.
for (const [index, item] of items.entries()) {
await handle(item, index);
}Обратную сторону тоже назвать: из forEach нельзя выйти по break, map создаёт новый массив, а индексный цикл остаётся правильным выбором, когда идёте с конца, шагом больше единицы или по двум массивам сразу.
Где спотыкаются
Повторяют, что forEach и map быстрее за счёт внутренней оптимизации. Всё наоборот: они вызывают колбэк на каждом элементе, а map ещё и выделяет новый массив. Обычный цикл в замерах как минимум не медленнее, и выбор тут про читаемость, а не про скорость.
Ждут от методов массива параллелизма. Исполнение остаётся однопоточным, параллельность в браузере это Web Workers, и никакой map к ней отношения не имеет.
Забывают про разреженные массивы и про изменение коллекции на ходу. forEach и map пропускают пустые ячейки, индексный цикл увидит на их месте undefined, а добавление элементов во время обхода даёт у разных способов разное поведение.