Когда я впервые начал изучать рекурсию, я не совсем понимал, что 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 или напишите мне по электронной почте.