Когда я впервые начал изучать рекурсию, я не совсем понимал, что JavaScript делает в фоновом режиме. Цель этой статьи - сделать рекурсию менее запутанной - это пошаговое руководство того, что происходит, когда вы используете рекурсию для сглаживания вложенного массива.

Массив, который мы хотим сгладить:

const myArray = [['hey'],['ho'],[['lets'], ['go']]]

Как будет выглядеть массив после сглаживания:

['hey', 'ho', 'lets', 'go']

Как мы сгладим наш массив? Есть много разных способов сгладить массив, сегодня мы будем использовать функцию ниже.

Для начала вызовем flattenArray с myArray.

flattenArray(myArray);

Шаг 1: flattenArray([[‘hey’],[‘ho’],[[‘lets’], [‘go’]]])

Так выглядит текущий стек вызовов.

Так выглядит первый вызов функции.

В чем дело?

  • Мы перебираем наш исходный массив const myArray = [[‘hey’],[‘ho’],[[‘lets’], [‘go’]]], используя цикл forEach. Первый элемент, который мы перебираем, оказывается массивом: [‘hey’].
  • Наш массив результатов все еще пуст [].
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, [‘hey’] - массивом, поэтому мы рекурсивно вызываем flattenArray([‘hey’]), как показано в строке 7. Результат этого рекурсивного вызова в конечном итоге будет передан в вызов нашей первой функции. массив результатов.

Шаг 2: flattenArray([‘hey’])

Так выглядит текущий стек вызовов. Теперь мы вышли из нашего первого вызова функции и перешли к нашему второму вызову функции, который является рекурсивным вызовом flattenArray([‘hey’]). Это происходит из строки 7 в приведенном выше фрагменте кода.

Это то, что происходит в нашей рекурсивной функции.

В чем дело?

  • Мне нравится думать об этом как о вызове совершенно новой функции. Мы вызываем flattenArray с [‘hey’]. Мы знаем, что это первый элемент нашего исходного массива, но пока давайте сосредоточимся на вызове этой функции.
  • Наш массив результатов пуст [].
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, ‘hey’ не является массивом, поэтому он помещается в массив результатов, как показано в строке 9.
  • Наконец, мы прошли итерацию по всему нашему массиву (который в данном случае содержал только один элемент) и возвращаем массив результатов, который в итоге равен [‘hey’].

Хорошо, что теперь? Шаг 3: flattenArray([[‘hey’],[‘ho’],[[‘lets’], [‘go’]]])

Мы вернулись к нашему исходному вызову функции.

Так выглядит текущий стек вызовов.

Это то, что происходит в исходном вызове функции.

В чем дело?

  • Мы все еще перебираем наш исходный массив и заканчиваем с первым элементом [‘hey’].
  • Первоначально строка 7 показывала result.push (… flattenArray ([‘hey’])). На предыдущем шаге мы показали, что происходит, когда мы вызывали эту рекурсивную функцию. flattenArray ([«эй»]), он возвращает[‘hey’].
  • Результат рекурсивного вызова [‘hey’] помещается в наш массив результатов в исходном вызове функции.
  • Оператор распространения позволяет нам захватить все элементы в массиве result[‘hey’] и поместить их в массив результатов нашего первого вызова функции. Если бы мы попытались сделать это без оператора распространения, мы бы получили еще один вложенный массив. Попробуйте сами, вставив приведенный ниже код в свою консоль, и вы увидите разницу.
const array1 = [1,2]
const array2 = [3,4]
const array3 = [5,6]
const array4 = [7,8]
array1.push(array2)
array3.push(...array4)
console.log('array 1', array1)
console.log('array 3', array3)

Шаг 4: flattenArray([[‘hey’],[‘ho’],[[‘lets’], [‘go’]]])

Далее будет происходить очень похожий процесс. Мы все еще находимся в первом вызове функции.

Так выглядит текущий стек вызовов.

Это то, что сейчас происходит в исходном вызове функции.

В чем дело?

  • Мы перебираем наш исходный массив, используя forEach. Текущий элемент теперь [‘ho’], который является массивом.
  • Наш результирующий массив содержит элемент ‘hey’, который был добавлен во время первой итерации нашего исходного массива на предыдущем шаге.
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, [‘ho’] - массивом, поэтому мы рекурсивно вызываем flattenArray([‘ho’]), как показано в строке 7. Результат этого рекурсивного вызова в конечном итоге будет передан в вызов нашей первой функции. массив результатов.

Шаг 5: flattenArray([‘ho’])

Так выглядит текущий стек вызовов.

Так выглядит второй вызов функции.

В чем дело?

  • Опять же, давайте подумаем об этом как о вызове совершенно новой функции. Мы вызываем flattenArray с помощью [‘ho’]. Мы знаем, что это второй элемент нашего исходного массива, но пока давайте сосредоточимся на вызове этой функции.
  • Наш массив результатов пуст [].
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, ‘ho’ - не массивом, поэтому вставьте его в массив результатов, как показано в строке 9.
  • Наконец, мы перебрали весь наш массив (который в данном случае имел только один элемент) и возвращаем массив результатов, который в итоге оказывается [‘ho’].

Хорошо, что теперь? Шаг 6: flattenArray([[‘hey’],[‘ho’],[[‘lets’], [‘go’]]])

Мы вернулись к нашему исходному вызову функции.

Так выглядит текущий стек вызовов.

Это то, что происходит в исходном вызове функции.

  • Мы все еще перебираем наш исходный массив с помощью forEach и заканчиваем со вторым элементом [‘ho’].
  • Теперь мы отправим результат нашего рекурсивного вызова в наш массив результатов в нашем исходном вызове функции.
  • Первоначально строка 7 показывала result.push(…flattenArray([‘ho’])). На предыдущем шаге мы показали, что происходит, когда мы вызывали эту рекурсивную функцию. flattenArray([‘ho’]) возвращает[‘ho’].
  • Оператор распространения позволяет нам захватить все элементы в массиве result[‘ho’] и поместить их в наш массив результатов.

Шаг 7: flattenArray([[‘hey’],[‘ho’],[[‘lets’], [‘go’]]])

Так выглядит текущий стек вызовов.

В настоящее время мы находимся в исходном вызове функции, и это то, как он выглядит.

  • Мы перебираем наш исходный массив, используя forEach. Текущий элемент теперь [[‘lets’],[‘go’]], который является Вложенным массивом !! Вот где действительно важна рекурсия.
  • Наш массив результатов в нашей исходной функции в настоящее время состоит из двух элементов: [‘hey’,'ho'].
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, [[‘lets’],[‘go’]] - массивом, поэтому мы рекурсивно вызываем flattenArray([[‘lets’],[‘go’]]), как показано в строке 7. Результат этого рекурсивного вызова в конечном итоге будет помещен в массив результатов наша исходная функция.

Шаг 8: flattenArray([[‘lets’],[‘go’]])

Так выглядит текущий стек вызовов.

Это то, что происходит в нашей рекурсивной функции.

  • Опять же, давайте подумаем об этом как о вызове совершенно новой функции. Мы вызываем flattenArray с [[‘lets’],[‘go’]]. Мы знаем, что это третий элемент нашего исходного массива.
  • Наш массив результатов пуст [].
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, [‘let’] - массивом, поэтому нам нужно рекурсивно вызвать flattenArray ([‘plays’]).
  • Здесь все по-другому, чем на предыдущих шагах.

Шаг 9: flattenArray([‘lets’])

Так выглядит текущий стек вызовов.

Это то, что происходит в нашей рекурсивной функции.

  • Опять же, давайте подумаем об этом как о вызове совершенно новой функции. Мы вызываем flattenArray с помощью ['Let's']. Мы знаем, что это первый элемент нашего [[‘lets’],[‘go’]] массива и часть третьего элемента исходного массива [[‘hey’],[‘ho’],[[‘lets’], [‘go’]]]. Пока мы сосредоточены на вызове этой функции.
  • Наш массив результатов пуст [].
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, ‘lets’ не является массивом, поэтому мы помещаем его в массив результатов, как показано в строке 9.
  • Наконец, мы перебрали весь наш массив (который в данном случае имел только один элемент) и возвращаем массив результатов, который в итоге оказывается [‘lets’].

Шаг 10: flattenArray([[‘lets’],[‘go’]])

Мы вернулись к нашему второму вызову функции.

Так выглядит текущий стек вызовов.

Это то, что сейчас происходит во втором вызове функции.

  • Теперь мы находимся в нашем втором вызове функции, который выполняет итерацию по третьему элементу нашего исходного массива, и мы только заканчиваем с первым элементом [‘lets’].
  • Массив результатов второго вызова функции пуст, как показано в строке 3.
  • Первоначально строка 7 показывала result.push (… flattenArray ([‘Let's’])). На предыдущем шаге мы показали, что происходит, когда мы вызывали эту рекурсивную функцию. flattenArray ([‘Let's’]) возвращает[‘lets’].
  • Теперь мы отправим результат нашего рекурсивного flattenArray([‘lets’]) в наш массив результатов во втором вызове функции flattenArray([[‘lets’],[‘go]]).
  • Мы используем оператор распространения, чтобы брать элементы из массива [‘lets’] и помещать их в наш массив результатов.

Шаг 11: flattenArray([[‘lets’],[‘go’]])

Так выглядит текущий стек вызовов.

Это то, что сейчас происходит во втором вызове функции.

  • Мы находимся в середине вызова нашей второй функции, которая вызывает flattenArray с [[‘lets’],[‘go’]].
  • В нашем массиве результатов есть один element['lets'] из первой итерации нашего вызова forEach.
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, [‘go’] - массивом, поэтому нам нужно рекурсивно вызвать flattenArray ([‘go’]).

Шаг 12: flattenArray([‘go’])

Так выглядит наш стек вызовов

Это то, что происходит в нашей рекурсивной функции.

  • Опять же, давайте подумаем об этом как о вызове совершенно новой функции. Мы вызываем flattenArray с [‘go’]. Мы знаем, что это второй элемент нашего массива [[‘lets’],[‘go’]] и часть третьего элемента исходного массива [[‘hey’],[‘ho’],[[‘lets’], [‘go’]]].
  • Наш массив результатов пуст [].
  • В строке 6 мы проверяем, является ли текущий элемент в нашем цикле forEach массивом, ‘go’ не является массивом, поэтому мы помещаем его в массив результатов, как показано в строке 9.
  • Наконец, мы перебрали весь наш массив (который в данном случае имел только один элемент) и возвращаем массив результатов, который в итоге оказывается [‘go’].

Шаг 13: flattenArray([[‘lets’],[‘go’]])

Мы вернулись ко второму вызову функции.

Так выглядит текущий стек вызовов

Так выглядит второй вызов функции.

  • Теперь мы находимся в нашем втором вызове функции, который выполняет итерацию по третьему элементу нашего исходного массива, и мы только заканчиваем со вторым элементом [‘go’].
  • Массив результатов второго вызова функции имеет один элемент [‘lets’].
  • Первоначально строка 7 показывала result.push (… flattenArray ([‘go’])). На предыдущем шаге мы показали, что происходит, когда мы вызывали эту рекурсивную функцию flattenArray([‘go’]) returns[‘go’].
  • Теперь мы отправим результат нашего рекурсивного flattenArray([‘go’]) в наш массив результатов во втором вызове функции flattenArray([[‘lets’],[‘go]]).
  • Мы используем оператор распространения, чтобы брать элементы из массива [‘go’] и помещать их в наш массив результатов.

Шаг 14: flattenArray([[‘lets’],[‘go’]])

Наш второй вызов функции почти завершен.

Это то, что сейчас происходит во втором вызове функции.

  • Второй вызов функции прошел через каждый элемент массива [[‘lets’],[‘go’]] и теперь готов вернуть свой окончательный массив результатов, который равен [‘lets’, ‘go’].
  • Второй вызов функции рекурсивно сглаживает третий элемент исходного массива: [[‘hey’],[‘ho’],[[‘lets’], [‘go’]]].

Шаг 15: flattenArray([[‘hey’],[‘ho’],[[‘lets’], [‘go’]]])

Так выглядит текущий стек вызовов.

Вот что происходит сейчас при первом вызове функции.

  • Наконец-то мы вернулись к нашему исходному вызову функции. В строке 7 мы изначально запускали рекурсивную функцию flattenArray([[‘lets’],[‘go’]]). Теперь мы подталкиваем результат этого рекурсивного вызова к результату, который, как мы знаем после предыдущих шагов, равен [‘lets’, ‘go’].
  • После использования оператора распространения, чтобы поместить результат рекурсивного вызова в массив результатов первого вызова функции, результат будет равен: [‘hey’, ‘ho’, ‘lets’, ‘go’].

Готово! Пожалуйста, дайте мне знать, есть ли у вас какие-либо вопросы или отзывы. Не стесняйтесь добавить меня в LinkedIn или напишите мне по электронной почте.