是我参与11月更文挑战的第17天, 活动详情查看:2021最后一次更文挑战
定义
递归是一种解决问题的方法,它解决问题的各个小部分,直到解决最初的大问题。通常涉及函数调用自身。
// 第一种
var recursiveFunction = function(someParam){
recursiveFunction(someParam);
}
// 第二种
var recursiveFunction1 = function(someParam){
recursiveFunction2(someParam);
}
var recursiveFunction2 = function(someParam){
recursiveFunction1(someParam);
}
上面两种都是递归函数。第一种是直接调用自身,第二种是样间接调用自身的方法或者函数。
递归的两大要素:递归方程 和 终止条件 。
js调用栈大小的限制
如果忘记加上用以停止函数递归调用的边界条件即终止条件,会发生什么呢?递归并不会无限地执行下去;浏览器会抛出错误,也就是所谓的栈溢出错误(stack overflow error)。
var i = 0;
function recursiveFn () {
i++;
recursiveFn(); // {1}
}
try{
recursiveFn();
} catch (error) {
console.log('i = ' + i + 'error:' + error);
}
ECMAScript 6有尾调用优化(tail call optimization)。如果函数内最后一个操作是调用函数(第一行),会通过“跳转指令”(jump) 而不是“子程序调用”(subroutine call)来控制。也就是说,在ECMAScript 6中,这里的代码可以一直执行下去。所以,具有停止递归的边界条件非常重要。
斐波那契数列
这是聊递归必说的例子。
斐波那契数据的定义:
- 1和2的斐波那契数是 1;
- n(n>2)的斐波那契数是(n-1)的斐波那契数加上(n-2)的斐波那契数。 代码实现:
function fibonacci(num){
if(num === 1 || num === 2){
return 1;
}
return fibonacci(num-1) + fibonacci(num-2);
}
当n大于2时,Fibonacci(n)等于Fibonacci(n-1)+Fibonacci(n-2)。
我们试着找出6的斐波那契,其会产生如下函数调用:
当然,我们也可以用非递归的方式实现斐波那契函数
function fib(num){
var n1 = 1,
n2 = 1,
n = 1;
for(var i=3;i<=num;i++){
n = n1 + n2;
n1 = n2;
n2 = n;
}
return n;
};
递归更容易理解,代码量更少。但并不比普通函数更快,反而更慢。所以,我们用递归,通常是因为它更容易解决问题。