关于JavaScript中递归的用法(包你看完后想再不懂闭包都难)
递归:对于递归,最直接的说法,就是函数中又调用函数自己,此时就是递归,但是一定不要忘记,递归一定要有结束条件的。
在你看这篇文章之前,你可以先看看下面这三个有关于递归的问题,并且带着这三个问题来看这篇文章,这样相信你会对递归有更加深刻的理解。(由于自己还不是JavaScript的大佬,所有如有错误,还请大家能够指出小编的不足,小编再次感谢各位)
1)假如楼梯有n个台阶,每次可以走1个或2个台阶,请问走完这n个台阶有几种走法?
2)如何用递归思想实现深拷贝?
3)如何用递归思想实现数组的扁平化?
想必在你看完这三个问题后,你会联想到之前所学到的递归,不管你现在心里是否已有了答案,都不要紧,下面我将从递归的简单用法,到递归案例,再是一些大家在面试中容易遇到的一些面试题做一个简单的分享…
一.若没有结束条件,将形成死循环
function f1() {
console.log("hello,world!");
f1();
}
f1();//浏览器崩溃,因为没有结束条件——死循环
那么我们应该如何改进,让它成为真正的递归?请看下面的代码
var i = 0;
function f1() {
i++;
if (i < 5) {
f1();
}
console.log("递归!");
};
f1();
答案很简单,当我们设置了结束条件后,这样就完成了一个简单的递归了
了解了递归的基本用法后,我们再来看一些递归的经典案例
二.案例:
递归实现:求n个数字的和 n=5 ------->5+4+3+2+1
function getSum(n){
return n==1 ? 1 : n+getSum(n-1)
}
console.log(getSum(5));
递归实现:求一个数字各个位数上的数字的和: 123 --->6 ---1+2+3
function getSum(n){
if( n<=9 ){
return n
}
return (n%10) + getSum(parseInt(n/10))
}
console.log(getSum(123));
递归实现:求斐波那契数列:1、1、2、3、5、8、13、21、34
function getFib(x) {
if(x==1||x==2){
return 1
}
return getFib(x-1)+getFib(x-2);
}
console.log(getFib(1));
递归实现: 有一个细胞 每一个小时分裂一次,一次分裂一个子细胞,第三个小时后会死亡。那么第n个小时候有多少细胞 ?
function cell(hour) {
function livecell(hour) { //活着的
if (hour <= 4) {
return Math.pow(2, hour - 1)
} else {
return livecell(hour - 1) * 2 - diecell(hour)
}
}
function diecell(hour) { //死亡
if (hour <= 4) {
return 0
} else {
return livecell(hour - 3) - diecell(hour - 1) - diecell(hour - 2)
}
}
return livecell(hour)
}
console.log(cell(5))
相信这些案例,在大家最开始学习递归的时候就已经遇到过,也是最基本的几个案例,在这里我们就不再过多的进行讨论。我们最重点的部分来了,就是面试中会遇到的递归题。
1.大家先看一下下面的代码,思考一下会打印出什么结果?
function fact(num) {
if (num <= 1) {
return 1;
} else {
return num * fact(num - 1);
}
}
var antherFact = fact;
fact = null;
console.log(antherFact(4));
首先很容易看的出来,这段代码块运用了递归思想,来想实现的一个阶乘。
想必会有部分小伙伴会认为打印出正确的4的阶乘,其实并不然。下面就是这段代码的执行结果。
原因其实很简单,就是当fact函数想在自己函数内部在调用自己的时候,但是我们在此函数付给了antherFact 后,进行了fact = null 的处理。
下面将是这段代码的解决方案,就是使用 arguments.callee(返回正在被执行的对现象)
function fact(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
var antherFact = fact;
fact = null;
console.log(antherFact(4));
2.数组的扁平化(递归)
var arr = [1,2,3,[4,5],[6,[7,8]]]
var newArr = []
function getArray(arr){
for(var i = 0; i<arr.length ; i++){
if( arr[i].constructor === Array){
getArray(arr[i]) //当我们判断出数组的项为数组类型时,就在将其调用getArray函数进行偏平
}else{
newArr.push(arr[i])
}
}
}
getArray(arr)
console.log(newArr);
3.冒泡排序
相信大家对冒泡排序已经很熟悉,这里我就不再讲解冒泡排序的实现思路。下面我们先看看用常规的方法是怎么实现排序的。
var arr = [5, 3, 2, 12, 11, 8, 99];
function bubbleSort(arr) {
if (arr.length < 2)
return arr;
for (var i = 0; i < arr.length - 1; i++) {
let shouldSort = false;
for (var k = 0; k < arr.length - 1 - i; k++) {
if (arr[k] > arr[k + 1]) {
var temp = arr[k];
arr[k] = arr[k + 1];
arr[k + 1] = temp;
shouldSort = true;
}
}
if (!shouldSort) break;
}
return arr;
}
var result = bubbleSort(arr);
console.log(result);
那么如何运用递归呢?我们知道,冒泡排序是对数组进行了双重for循环遍历比较,那么这时候递归就能在这排上用了,也就是只用进行一次for循环,再加上递归,也能实现我们的冒泡排序。
function Stor(arr,size){
if(size <2 ){ //这里须要对我们的递归设置结束条件
return arr
}else{
for(var i = 0 ; i<size-1 ; i++){
if(arr[i] > arr[i+1]){
var temp = arr[i]
arr[i] = arr[i+1]
arr[i+1] = temp
}
}
return Stor(arr,size-1)
}
}
console.log(Stor(arr,arr.length));
4.与冒泡类似,下面是运用递归实现的快速排序:
//方式1
function QSort(arr) {
//如果数组<=1,则直接返回
if(arr.length <= 1){
return arr;
}
//找基准,并把基准从原数组删除
var pivot = arr.splice(0, 1)[0];
//定义左右数组
var left = [];
var right = [];
//比基准小的放在left,比基准大的放在right
for(var i = 0; i < arr.length; i++){
if(arr[i] <= pivot){
left.push(arr[i]);
}
else{
right.push(arr[i]);
}
}
//递归
return QSort(left).concat([pivot],QSort(right));
}
console.log(QSort([7,2, 3, 4, 5, 10, 7, 8, 2,21]));
//方式2
function QSort2(arr, left, right) {
if(left < right){
//找基数第一趟排序后的位置
let pivot = partion(arr,left,right);
//递归排序左右区间
QSort2(arr, left, pivot - 1);
QSort2(arr, pivot + 1, right);
}
return arr;
}
//快排第一趟
function partion(arr, left, right) {
//第一个元素作为基数
let pivotVal = arr[left];
pivot = left;
while(left<right){
while(right>left && arr[right]>=pivotVal){
right--;
}
[arr[left], arr[right]] = [arr[right], arr[left]];
while(left<right && arr[left]<=pivotVal){
left++;
}
[arr[left], arr[right]] = [arr[right], arr[left]];
}
return left;
}
var arr = [7,2, 3, 4, 5, 10, 7, 8, 2,21];
console.log(QSort2(arr, 0, arr.length-1));
5.深浅拷贝
深浅拷贝大家应该也不陌生吧,也就是对一个对象进行拷贝的过程,区别就在于,如果对象没有引用类型的时候进行的拷贝,称为浅拷贝,反之,当对对象中的引用类型一同拷贝的就,并且新对象和老对象不再公用一个引用地址,就是深拷贝,也可以理解为对浅拷贝的一个递归过程,下面是我编写的浅拷贝和深拷贝的实现过程及说明。
var obj = {
name: 'liuq',
age: 19,
address: {
home: 'SC'
}
};
var newobj = {};
console.log(obj);
// 浅拷贝
function extend(p, c) {
var c = c || {};
for (var i in p) {
c[i] = p[i];
}
}
extend(obj, newobj);
newobj.address.home = 'BJ'
console.log(obj);
//深拷贝 ==> 对浅拷贝的递归
function extendDeeply(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
// 引用类型需要递归实现深拷贝
c[i] = (p[i].constructor === Array) ? [] : {}
extendDeeply(p[i], c[i]);
} else {
// 非引用类型直接复制即可
c[i] = p[i];
}
}
}
extendDeeply(obj, newobj);
newobj.address.home = 'BJ'
console.log(obj);
最后,小编祝你学有所成,感谢你的支持!!