JS 递归 拷贝

递归

什么是递归

递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数

注意:递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return

<script>
        var num = 1;
        function fn() {
            console.log('6');

            if(num == 6) {
                return; //必须加return
            }

            num++;
            fn();
        }
        fn();
    </script>

递归例子

1.阶乘

<script>
        //利用递归函数求1~n的阶乘
        function fn(n) {
            if(n == 1) {
                return 1;
            }
            return n * fn(n-1);
        }
        console.log(fn(4));

        //详细思路

        //假如用户输入3,
        //return 3 * fn(3-1)

        //fn(2) = 2 * fn(2-1);

        //return 3 * (2*fn(1))

        //fn(1)  满足n == 1  return 1;

        //return 3 * (2 * 1) = 6
    </script>

2.递归求和

//递归实现n个数字的和
function getSum(x){
    if(x==1){
       return 1;
    }
    return x+getSum(x-1);
}
console.log(getSum(5));

常规求5个数的和

//求5个数字的和
var sum=0
for(var i=0;i<6;i++){
    sum=sum+i;
}
console.log(sum);

求斐波那契数列:1,1,2,3,5,8…

function Fib(x) {
      if(x == 1 || x == 2){
        return 1
      }
      return Fib(x-1) + Fib(x-2);
    }
    console.log(Fib(12));

利用递归遍历数组

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var data = [{
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                gname: '冰箱',
                goods: [{
                    id: 111,
                    gname: '海尔'
                }, {
                    id: 112,
                    gname: '美的'
                },]
            }, {
                id: 12,
                gname: '洗衣机'
            }]
        }, {
            id: 2,
            name: '服饰'
        }];
        //1.利用forEach遍历每一个对象
        function getID(json, id) {
            var o = {}
            json.forEach(function (item) {
                if (item.id == id) {
                    o = item;
                    return item;
                    //2.想要得到里层11 12 可以利用递归
                    //里面应该有goods这个数组 应且长度不为0
                } else if (item.goods && item.goods.length > 0) {
                    o = getID(item.goods, id) //拿到 return item 的值 并赋值给o
                }

            });
            return o;
        }
        console.log(getID(data, 1));
        console.log(getID(data, 2));
        console.log(getID(data, 11));
        console.log(getID(data, 12));
        
    </script>
</body>

</html>

结果:
在这里插入图片描述

拷贝

两种数据类型

1.基本数据类型和引用数据类型

2.基本数据类型指的是,保存在栈中的简单数据类型

3.引用数据类型则是保存在堆内存中的对象,也就是说变量中保存的是指向该对象的指针

4.基本数据类型包括:
number
string
boolean
undefined
null

5.引用数据类型:
array
object
function

浅拷贝

1.深浅拷贝的问题根源是js中不同的数据类型在内存中的存储方式不一样。

基本数据类型是以名值得形式存储在栈内存中的。

let a = 1;

Name	Value
a		1

进行赋值操作时会新开辟一片栈内存存储新的名值。

let a = 1;
let b = a;

Name	Value
a		1
b		1

2.引用数据类型:数组、对象、函数等
以上的类型会在栈内存和堆内存中分别开辟一片空间进行存储。

let a = [1,3,5,7];

在这里插入图片描述

当直接赋值时其实是将a的堆地址赋值给了b,两者最终指向了同一个堆内存

let a = [1,3,5,7];
let b = a;
在这里插入图片描述
这就是浅拷贝,带来的后果就是对b的元素进行操作时,同时改变了a对应的值。

 <script>
        // 浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用(地址).
        // 深拷贝拷贝多层, 每一级别的数据都会拷贝.
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        };
        var o = {};
        // for (var k in obj) {
        //     // k 是属性名   obj[k] 属性值
        //     o[k] = obj[k];
        // }
        // console.log(o);
        // o.msg.age = 20;
        // console.log(obj);

        console.log('--------------');


        Object.assign(o, obj);   //浅拷贝建议用法  ES6中新增方法
        console.log(o);
        o.msg.age = 20;
        console.log(obj);

        //修改 o的msg值  obj的msg也会修改  因为浅拷贝 拷贝的是同一个地址  
    </script>

深拷贝

1.简单粗暴,使用JSON.stringify和JSON.parse进行两次转。

let a = [1,3,5,7];
  let b = JSON.parse(JSON.stringify(a));

  b[0] = 99;
  console.log("a", a); // [1,3,5,7]
  console.log("b", b); // [99,3,5,7]

缺点:转换时会自动忽略undefined,Symbol、function

2.用递归的方法去遍历复制所有的层级

 function deepClone(obj){
    // 判断数据形式
    let clone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj === "object"){
      for(key in obj){
        if(obj.hasOwnProperty(key)){
          // 属性是对象则进行递归
          if(obj[key] && typeof obj[key] === "object"){
            clone[key] = deepClone(obj[key]);
          }else{
            clone[key] = obj[key];
          }
        }
      }
    }
    return clone;
  }
  
  let a = [1,3,5,7],
  b = deepClone(a);
  a[0] = 999;
  console.log(a); // [1,3,5,7]
  console.log(b); // [999,3,5,7]

3.for 循环:

 <script>
        // 深拷贝拷贝多层, 每一级别的数据都会拷贝.
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            },
            color: ['pink', 'red']
        };
        var o = {};
        // 封装函数 
        function deepCopy(newobj, oldobj) {
            for (var k in oldobj) {
                // 判断我们的属性值属于那种数据类型
                // 1. 获取属性值  oldobj[k]
                var item = oldobj[k];
                // 2. 判断这个值是否是数组  先判断数组 在判断对象,因为数组也是对象
                if (item instanceof Array) {
                    newobj[k] = [];
                    deepCopy(newobj[k], item)
                } else if (item instanceof Object) {
                    // 3. 判断这个值是否是对象
                    newobj[k] = {};
                    deepCopy(newobj[k], item)
                } else {
                    // 4. 属于简单数据类型
                    newobj[k] = item;
                }

            }
        }
        deepCopy(o, obj);
        console.log(o);

        var arr = [];
        console.log(arr instanceof Object);
        o.msg.age = 20;
        console.log(obj); //此时不会改变
    </script>

总结

1.在对象中赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值。

2.递归是做复杂深拷贝比较合理的方法。

3.JSON深拷贝只能是对象中没有function时可以使用。

4.数组的深拷贝方法较多,但是大多是只能进行第一层的深拷贝。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值