Я чувствую, что все больше и больше for циклы становятся презираемыми, и в то время как некоторые чешут затылки и недоумевают, почему, другие смотрят на простое использование цикла for как на признак зеленых и неопытных разработчиков, которые не знают, как использовать Методы массива. Я попадал в эту ловушку во многих случаях, часто чувствуя, что делаю что-то не так, когда подключал `for`, а не карту или сокращение, но хотя есть причины, по которым методы Array обычно лучше, они не всегда лучше.

Прежде чем приводить доводы в пользу циклов for, я хочу упомянуть, что есть много случаев, когда методы Array намного лучше:

  • + Если у вас есть исходные данные, вы можете отображать и фильтровать данные без ручного отслеживания индексов.
  • + Вместо того, чтобы захватывать переменную с использованием индекса цикла for, она предоставляется в качестве параметра вашей анонимной функции.
  • + Вам не нужно изменять данные, как в цикле for, вы просто возвращаете их.
  • + Использование методов массива обеспечивает более инкапсулированный подход к кодированию, поскольку обычно все оборачивается в меньшие партии работы и меньшие функции.
  • + По нескольким причинам, указанным выше, легче узнать, что происходит внутри каждого метода Array, тогда как циклы For обычно поощряют использование большого громоздкого кода, изменяющего функциональный блок, внутри которого находится цикл for.

Но! мы здесь не для того, чтобы аргументировать использование методов массива, мы пытаемся найти те исключения, в которых они не совсем работают.

Случаи против использования методов массива:

  • — В случае отсутствия исходных данных вы должны сгенерировать массив, по которому перебираете. В более редких случаях это может быть очень запутанным и даже невозможным.
  • — нельзя вырваться из карты, уменьшить или фильтровать; это может вызвать проблемы с производительностью для больших итераций, хотя в некоторых случаях Array.find может заменить это.
  • — Контекст this теряется при использовании стрелочных функций, если вы также не используете bind(this) или более многословную «функцию».

Этот контекст является второстепенным по сравнению с первыми двумя, поэтому мы проигнорируем его и сосредоточимся на отсутствии исходных данных и производительности.

Итак, давайте начнем с обычного случая из учебника, который, я думаю, показывает, где методы Array лучше. Если у вас есть исходные данные (одноклассники), а производительность/управление не имеет значения (получение любого одноклассника в массиве), вам следует использовать методы массива в цикле for.

let matches = classmates
  .filter(classmate=>classmate.name === ‘John’)
// vs
let matches = [];
for(let i = 0; i < classmates.length; i++) 
{
  if(classmates[i].name === ‘John’) 
    matches.push(classmates[i])
}

Но это может стать запутанным, если массив огромен, он отсортирован и мы хотим выйти из соображений производительности. Здесь мы должны злоупотреблять Array.find и мутировать вне нашего метода Array — теперь циклы for выглядят не так уж плохо:

let arr = []
let matches = classmates.find(classmate => {
  if(classmate.name > 'John') return arr
  else if(classmate.name === 'John') arr.push(classmate)
})
// vs
let matches = [];
for(let i = 0; i < classmates.length; i++) 
{
  if(classmates[i].name === 'John') matches.push(classmates[i])
  else if(classmates[i].name > 'John') break
}

Но все действительно начинает ломаться, если у нас нет массива/набора данных для работы. Конечно, для простых массивов, таких как перебор чисел от 0 до 9, мы можем сделать это достаточно легко:

let arr = [...Array(10).keys()]

Но что, если бы массив полагался на содержимое самого цикла? Что, если бы мы повторяли цикл for над вычислением числа Пи, которое становилось бы все более точным? Что, если бы мы захотели остановиться, когда расчет был с точностью до 1%? Мы не знаем, сколько попыток или итераций это займет, должно ли это занять n, где n равно 1–1000, или n, где n равно 1–100 000? или n, где n равно 1–1 000 000? Мы должны продолжать цикл до тех пор, пока условие не будет выполнено. Единственный способ выйти за пределы цикла for (и за пределы функций while/recursive) — сгенерировать абсурдно большой массив на основе догадки и выйти раньше с помощью логики поиска, изменяя внешний массив. Посмотрите на это чудовище:

// guessing this will take a million iterations?
// this is going to be a huge array!
let rangeArr = [...Array(1000000).keys()]
let pi = 0
let matches = rangeArr.find(n=> {
  if(isAccurate(pi)) return pi
  else pi += nextPi(n+1) // + 1 since our array starts at 0
})

// vs
let pi = 0;
for(let n= 1; !isAccurate(pi); n++) pi += nextPi(n)

А топовый образец даже не удовлетворяет нашей задаче! Что, если потребуется 1 000 001 расчет? Конечно, мы могли бы добавить к этому немного рекурсии, но теперь нам нужно беспокоиться о большем количестве кода и возможных проблемах с переполнением стека. Мы также могли бы использовать функцию генератора, но для этого потребовался бы цикл или рекурсия (см. выше).

Как общее правило для себя, я создал эту схему. Это в значительной степени основано на мнении, поскольку во многих отношениях код так же связан с искусством и теорией, как и с формой и логикой.

  • Зеленый => Методы массива, вероятно, лучше подходят для этого
  • Белый => Используйте здравый смысл
  • Синий => For циклы могут быть лучше для этого

Ключ:

  • Существующий набор данных = › У нас есть массив
  • Расчетный набор данных => У нас есть то, что нам нужно, чтобы легко сформировать массив/диапазон
  • Зависимый набор данных => Мы не можем правильно сформировать массив/диапазон, пока не узнаем результат каждой итерации: это зависит от итерации.
  • (Столбец управления показывает, насколько необходим контроль над циклом, например прерывание, обычно по соображениям производительности)

Вывод

Я думаю, что больше всего меня беспокоит абсолютный менталитет. «Никогда не используйте х» или «х всегда лучше, чем у». Возможно, х часто лучше, чем у, но такая категоричность сужает вашу точку зрения и (на мой взгляд) душит хороший код.

Если бы я провел аналогию со всей дискуссией «Массив против для», я бы сравнил плоскую головку с отверткой Phillips в мире, где большинство винтов и отверток сейчас движутся к плоской головке. Если бы у меня был только один инструмент в этом мире, я думаю, я был бы готов попытаться обойтись плоской головкой (Методы массива), но это все равно может затруднить (или почти невозможно) справиться с некоторыми необычными винтами Phillip, с которыми я сталкиваюсь. с, так что я не должен смущаться время от времени смахивать пыль со своей старой головки Phillips. Кроме того, выбрасывать старую отвертку только потому, что вы обнаружили, что плоские головки можно вставить в крестообразную головку, может быть немного…

подождите… хреново!