说说前端那些事----递归
程序员文章站
2022-05-04 18:50:01
...
1.递归的定义
什么是递归?
函数在定义中调用函数自身称之为递归
上面那句摘自*对递归的解释,用简单的话说就是自己调用自己,例如:
function add (num) {
if (num === 1) {
return 1
}
return num + add(num - 1)
}
add(5) // 15
add(3) // 6
2.递归的适用范围
在很多技术书籍中都举过阶乘的例子来说明递归,那么除了阶乘和上面的计算求和还有什么情况我们可以写成递归么。
凡事能用while循环实现的都可以用递归实现
3.递归的好处
使逻辑变得清晰,代码变得简介
4.递归的缺点
我们知道递归是自身调用自身,由于闭包导致上层作用域得不到释放,从而当递归循环调用过多时会造成内存泄漏。
function add (num) {
if (num === 1){
return 1
}
return num + add(num - 1)
}
add(100000) // RangeError:Maximum call stack size exceeded
5.尾递归调用
由于适用递归的优势很多,又因为其缺点导致递归在多循环引用时不能使用,为了解决递归导致的内存移除问题,多数语言对递归进行了优化,当我们使用尾递归调用时,堆栈只追踪当前调用时的作用域。可是什么又是尾递归调用呢
function add (num, sum) {
sum = sum || 0
if (num === 1) {
return sum + 1
}
return add(num - 1, sum + num)
}
add(10) // 55
注意这种写法与前面递归的写法,这种写法可以某些语言中可以使堆栈不用跟踪所有调用过的函数作用域,只需要保存当前调用的函数作用域,从而解决递归调用造成的内存泄漏问题。
然而,在javascript中,尾递归调用也解决不了该问题,即使使用尾递归调用依然会造成内存泄漏的问题,至少目前的运行javascript运行环境还没能解决该问题,为此,前端小伙伴在使用递归的时候需要考虑递归循环次数太多从而造成内存泄漏的问题。不过该问题在以后将会得到解决,解决办法也是对尾递归调用进行优化,所有掌握尾递归调用也算是必备的知识了。
6.举个栗子
在项目中,如果我们需要判断某个元素是否为某某元素的子集元素时可以使用递归调用向上查找。同样的,对树状结构的数据遍历为多级选项卡或者多级导航栏我们依然可以使用递归的方式。