【006】深浅拷贝

前言

深浅拷贝也是面试经常问到的问题。能够体现面试者的基本功,代码能力,逻辑能力。

浅拷贝

拷贝就是复制的意思,如果你有一个a数组,拷贝a数组的时候会重新开辟一个空间,存放的东西和a数组里面的东西是一样的,但是他们的地址却不一样。

什么是浅拷贝

什么是浅拷贝,我们看一个栗子。

 var a=[1,2,{a:5}];
 var b=a.slice();
 console.log(b);

上面定义一个a数组,通过slice方法拷贝a数组,用b去接收他。

  • 第一个框框是打印b数组的结果,可以发现完全一模一样。
  • 第二个框框说明对a数组修改后b数组没有改变,说明两个数组的地址不是同一个地址,相互之间没有影响
  • 第三个框的操作是让a[2]的值再变回来
  • 第四个框我们修改a【2】={a:5}里面的值,打印的结果发现b也改变了,这是为什么呢?地址不是一样的啊,那就不指向同一个引用啊?我们接着看第二张图。
    在这里插入图片描述
    其中&001等表示地址。如果你对引用值内存熟悉的话很容易就能看明白这个图。a[2]和b[2]存放的是对象{a:5}的地址,所以改变了会互相影响。这就是浅拷贝
    在这里插入图片描述

浅拷贝的方法

1.手动写一个拷贝函数
 var shollowColon=function(target){
            //1.如果是引用类型要构造一个新的数组或者对象
            var cloneResult=typeof target=="object"&&target&&Array.isArray(target)?[]:{};
            for(props in target){
                if(target.hasOwnProperty(props))
                    cloneResult[props] = target[props];
            }
            //2.引用值返回新的,非引用值返回原来的
            return cloneResult || target;
        }

说明: if(target.hasOwnProperty(props))用来确定某属性是否是对象本身的属性。假如我们在原型上增加一个sex属性,看下面代码

<script>
        var shollowColon=function(target){
            //1.如果是引用类型要构造一个新的数组或者对象
            var cloneResult=typeof target=="object"&&target&&Array.isArray(target)?[]:{};
            for(props in target){
              //  if(target.hasOwnProperty(props))
                    cloneResult[props] = target[props];
            }
            //2.引用值返回新的,非引用值返回原来的
            return cloneResult || target;
        }

        var a = [1,2,{name:'linglong'}];
        Array.prototype.sex="gir";
        var b = shollowColon(a);
        console.log(b);
    </script>

打印a,b的结果是下面这个样子

在这里插入图片描述

2.object.assign方法

3.slice方法

4.扩展运算符…

5.concat方法

手动实现深拷贝

1.利用json下的字符串和对象转换方法

function deepClone(obj){
    let _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
    return objClone
}    
let a=[0,1,[2,3],4],
    b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);

但是该方法存在三个问题

  1. 无法解决循环引用问题
  2. 不能拷贝特殊的对象,如正则,date,set、map等。
  3. 不能拷贝函数
    原因:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON

手动实现

递归浅拷贝

 var deepColon=function(target){
            if(typeof target=="object"&&target){
                //1.如果是引用类型要构造一个新的数组或者对象
            var cloneResult=Array.isArray(target)?[]:{};
            for(props in target){
               if(target.hasOwnProperty(props))
                    // cloneResult[props] = target[props];
                    cloneResult[props] = deepColon(target[props]);

            }
            }
            
            //2.引用值返回新的,非引用值返回原来的
            return cloneResult || target;
        }

递归+浅拷贝实现深拷贝存在的问题

  • 爆栈问题
  • 循环引用
    • 循环检测

遍历树

var a = {
   a1: 1,
   a2: {
       b1: 1,
       b2: {
           c1: 1
       }
   }
}
======================
   a
 /   \
a1   a2        
|    / \         
1   b1 b2     
    |   |        
    1  c1
        |
        1       
        
function cloneLoop(x) {
    const root = {};//拷贝后的结果

    // 栈
    const loopList = [
        {
            parent: root,
            key: undefined,
            data: x,
        }
    ];

    while(loopList.length) {
        // 深度优先
        const node = loopList.pop();//取出尾元素直到为空
        const parent = node.parent;//存放拷贝结果
        const key = node.key;
        const data = node.data;//原拷贝对象

        // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
        let res = parent;
        if (typeof key !== 'undefined') {
            res = parent[key] = {};
        }

        for(let k in data) {
            if (data.hasOwnProperty(k)) {//k是否是data对象上本身的属性
                if (typeof data[k] === 'object') {
                    // 下一次循环
                    loopList.push({
                        parent: res,
                        key: k,
                        data: data[k],
                    });
                } else {
                    res[k] = data[k];
                }
            }
        }
    }

    return root;
}  
  • set和map
const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;

const deepClone = (target, map = new Map()) => { 
  if(map.get(target))  
    return target; 
 
 
  if (isObject(target)) { 
    map.set(target, true); 
    const cloneTarget = Array.isArray(target) ? []: {}; 
    for (let prop in target) { 
      if (target.hasOwnProperty(prop)) { 
          cloneTarget[prop] = deepClone(target[prop],map); 
      } 
    } 
    return cloneTarget; 
  } else { 
    return target; 
  } 
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拷贝和浅拷贝都是常见的复制数据的方式。 浅拷贝只会复制对象的第一层属性,如果对象属性值是一个引用类型,那么复制的是该引用类型的地址,也就是说,新对象和原对象的该引用类型的属性指向同一个内存地址。当原对象的该引用类型的属性值发生变化时,新对象也会受到影响,因为它们指向的地址是相同的。 拷贝则会递归地复制对象及其所有嵌套的属性,直到所有属性都是基本类型或不可变类型时才停止。这样,新对象和原对象的所有属性都指向不同的内存地址,互不影响。 实现拷贝的方式有很多,以下列举几种: 1. 使用JSON.parse和JSON.stringify方法:先将对象转为字符串,然后再将字符串转回对象,这样可以实现拷贝,但是有一些限制,比如不能复制函数、正则表达式等特殊类型的数据。 ```javascript let newObj = JSON.parse(JSON.stringify(oldObj)); ``` 2. 使用递归函数:遍历对象的所有属性,如果属性值是对象,则递归调用该函数进行复制,这样可以实现完整的拷贝。 ```javascript function deepClone(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } let result = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { result[key] = deepClone(obj[key]); } } return result; } ``` 以上列举了常见的拷贝和浅拷贝的方式,具体使用哪种方式取决于实际需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值