1. Что такое рекурсия?
Концепция рекурсии очень проста, "позвони себе" (функции используются в качестве примера ниже).
Прежде чем анализировать рекурсию, вам нужно понять «стек» в JavaScript (call stack
) концепция.
2. Толкай и хлопай
кучачто это такое? Можно понять, что в памяти есть некая область, эта область уподобляется ящику, вы что-то кладете в ящик, это действиетолкать стек. Таким образом, первое, что нужно положить, находится на дне коробки, а последнее, что нужно положить, — наверху коробки. Извлечение вещей из коробки можно понимать какпоп.
Отсюда делается вывод, что у нас есть привычка брать вещи сверху вниз, а вещи, которые кладут первыми, оказываются на дне коробки, и тогда мы можем их доставать последними.
В JavaScript при вызове функции происходит стекирование.return
После того, как предложение ключевого слова или выполнение пройдет стек (поп).
Возьмем пример: каков порядок выполнения этого кода?
function fn1() {
return 'this is fn1'
}
function fn2() {
fn3()
return 'this is fn2'
}
function fn3() {
let arr = ['apple', 'banana', 'orange']
return arr.length
}
function fn() {
fn1()
fn2()
console.log('All fn are done')
}
fn()
Выше приведен ряд действий по укладке и извлечению:
- Во-первых, стек (коробка) сначала пуст
- функция
fn
воплощать в жизнь,fn
Сначала толкните стопку и положите ее на дно стопки (коробки). - Внутри функции fn сначала выполните функцию
fn1
,fn1
складывать наfn
над - функция
fn1
выполнять, сталкиватьсяreturn
ключевое слово, возвратthis is fn1
,fn1
Выскочить из стопки, теперь в коробке остался только одинfn
- назад
fn
внутренний,fn1
После выполнения функцияfn2
Начать выполнение,fn2
толкать стек, но вfn2
внутри, встречаfn3
,Начать выполнениеfn3
,такfn3
толкать стек - В это время порядок снизу вверх в стеке следующий:
fn3
<=fn2
<=fn
,fn
внизу - И так далее, функция
fn3
внутренняя встречаreturn
предложения с ключевыми словами,fn3
После завершения выполнения извлеките стек и вернитесь к функцииfn2
середина ,fn2
также столкнулсяreturn
ключевое слово, продолжайте извлекать стек. - Теперь в стеке только один
fn
Теперь вернемся к функцииfn
в, выполнитьconsole.log('All fn are done'
) утверждение,fn
вытолкнуть стек. - Теперь стек снова пуст.
Вышеуказанные шаги легко запутать людей, ниже приведена блок-схема:
Посмотрите на среду браузера CHROME:
3. Рекурсия
Начнем с простой рекурсии JavaScript
function sumRange(num) {
if (num === 1) return 1;
return num + sumRange(num - 1)
}
sumRange(3) // 6
Последовательность выполнения приведенного выше кода:
- функция
sumRange(3)
воплощать в жизнь,sumRange(3)
стек, встречаreturn
ключевое слово, но стек не может быть извлечен немедленно, потому что вызовsumRange(num - 1)
которыйsumRange(2)
- так,
sumRange(2)
толкать стек и так далее,sumRange(1)
толкающий стек, - наконец,
sumRange(1)
поп возвращает 1,sumRange(2)
вытолкнуть стек, вернуть 2,sumRange(3)
поп - так
3 + 2 + 1
результат 6
см. блок-схему
Таким образом, рекурсия — это также процесс добавления и извлечения стека.
Рекурсия может быть представлена нерекурсией, ниже приведена эквивалентная реализация приведенного выше рекурсивного примера.
// for 循环
function multiple(num) {
let total = 1;
for (let i = num; i > 1; i--) {
total *= i
}
return total
}
multiple(3)
4. Рекурсивные точки внимания
Если приведенный выше пример изменить следующим образом:
function multiple(num) {
if (num === 1) console.log(1)
return num * multiple(num - 1)
}
multiple(3) // Error: Maximum call stack size exceeded
Первая строка приведенного выше кода неreturn
Предложения с ключевыми словами, поскольку рекурсия не имеет условия завершения, она будет продолжать помещаться в стек, вызывая утечку памяти.
Рекурсия подвержена ошибкам
- не задана конечная точка
- Помимо рекурсии, другие части кода
- Когда уместна рекурсия
Что ж, вышеизложенное — это концепция рекурсии в стиле JavaScript.
Ниже приведены практические вопросы.
6. Практические вопросы
- Напишите функцию, которая принимает строку и возвращает строку, которая переворачивает исходную строку.
- Напишите функцию, которая принимает строку строк и сравнивает один символ с начала на конец. Если они равны, верните
true
, если не равно, вернутьfalse
. - Напишите функцию, которая принимает массив и возвращает сглаженный новый массив.
- Напишите функцию, которая берет объект, значения которого четны, складывает их вместе и возвращает итог.
- Напишите функцию, которая принимает объект и возвращает массив, включающий все значения в объекте в виде строк
7. Справочные ответы
Ссылка 1
function reverse(str) {
if(str.length <= 1) return str;
return reverse(str.slice(1)) + str[0];
}
reverse('abc')
Ссылка 2
function isPalindrome(str){
if(str.length === 1) return true;
if(str.length === 2) return str[0] === str[1];
if(str[0] === str.slice(-1)) return isPalindrome(str.slice(1,-1))
return false;
}
var str = 'abba'
isPalindrome(str)
Ссылка 3
function flatten (oldArr) {
var newArr = []
for(var i = 0; i < oldArr.length; i++){
if(Array.isArray(oldArr[i])){
newArr = newArr.concat(flatten(oldArr[i]))
} else {
newArr.push(oldArr[i])
}
}
return newArr;
}
flatten([1,[2,[3,4]],5])
Ссылка 4
function nestedEvenSum(obj, sum=0) {
for (var key in obj) {
if (typeof obj[key] === 'object'){
sum += nestedEvenSum(obj[key]);
} else if (typeof obj[key] === 'number' && obj[key] % 2 === 0){
sum += obj[key];
}
}
return sum;
}
nestedEvenSum({c: 4,d: {a: 2, b:3}})
Ссылка 5
function collectStrings(obj) {
let newArr = []
for (let key in obj) {
if (typeof obj[key] === 'string') {
newArr.push(obj[key])
} else if(typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
newArr = newArr.concat(collectStrings(obj[key]))
}
}
return newArr
}
var obj = {
a: '1',
b: {
c: 2,
d: 'dd'
}
}
collectStrings(obj)
5. Резюме
Суть рекурсии в том, что часто необходимо сначала подумать о том, как выглядит обычная часть.При рассмотрении рекурсивной части ниже приведены методы, обычно используемые в рекурсии JavaScript.
- множество:
slice
,concat
- нить:
slice
,substr
,substring
- Объект:
Object.assign