6

基本数据类型的复制很简单,就是赋值操作,所以深浅拷贝也是针对Object,Array这类引用类型数据。

浅拷贝对于字符串来说,是值的复制,而对于对象来说则是对对象地址的复制;

而深拷贝的话,它不仅将对象的各个属性逐个复制出来,还将各个属性所包含的对象也依次复制出来,相当于在堆区又新开了一块地存放,主要采取递归来实现。

实现方式

原生JS实现:

浅拷贝:

let originObj={ color:['red','green'], num:5 };

function shallowClone(obj){
    let newObj=(obj instanceof Array) ? []:{};
    for(let item in obj){
        if(obj.hasOwnProperty(item)){ // 避免列举出原型上的属性
            newObj[item]=obj[item];
        }
    }
    return newObj;
}
let cloneObj=shallowClone(originObj);

深拷贝:

   function deepClone(obj){
       let newObj=(obj instanceof Array)? [] : {};
       for(let item in obj){
           if(obj.hasOwnProperty){
               const val=obj[item];
               debugger
               newObj[item]=typeof val==='object' ? deepClone(val) : val;
           }
       }
       return newObj;
   }
   let cloneObj2=deepClone(originObj);

其实,这种深拷贝的实现还是有些问题的,比如日期正则对象无法复制,还有一个是循环引用,类似闭包会内存泄露一样,这样的对象深拷贝也会陷入一个闭环,直到栈溢出。
为了解决这个问题,就得先判断这个对象是否等于原对象。

function deepClone(obj){
    let tempArr=[];
    let newObj=(obj instanceof Array) ? [] : {};
    tempArr.push(obj);
    for(let item in obj){
        if(typeof obj[item] === 'object'){
            const index=tempArr.indexOf(obj[item]); 
            // 如果已存在,证明引用了相同对象,那么无论是循环引用还是重复引用,我们返回引用就可以了
            if(index>-1){
                newObj[item]=tempArr[index];
            }else{
                newObj[item]=obj[item];
            }
        }else{
            newObj[item]=obj[item];
        }        
    }
    return newObj;
}

ES6实现

    let newObj1=Object.assgin({},originObj);
    let newObj2={...obj}; // 数组用[...obj]
注:Object.assgin和扩展运算符实现的都是浅拷贝。

JSON实现:

这种方法简单是简单,不过这个方法有以下缺陷:

1.会忽略函数对象以及原型对象,正则会复制成空对象,而日期对象会变成字符串。

2.它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object; 
   
3.如果对象中存在循环引用的情况无法正确处理。

按需取用吧。

  let originObj={fn:function(){console.log(111)}, colors:['red','gree'],reg:/\s/g};
  let newObj=JSON.parse(JSON.stringify(originObj));

第三方库实现:

转自链接:深入剖析 JavaScript 的深复制

Underscore —— _.clone()

顺便安利下underscore,真的挺好用的一个库: Underscore中文文档

这个方法实际上是一种浅复制 (shallow-copy),所有嵌套的对象和数组都是直接复制引用而并没有进行深复制。

        let originObj={ color:['red','green'], num:5 };
        let newObj=_.clone(originObj);
        newObj.colors.push('grey');
        originObj.colors; // ['red','green','grey']; 

源码逻辑很简单:(当然调用的其他方法也是underscore的)

    // Create a (shallow-cloned) duplicate of an object.
    _.clone = function(obj) {
        if (!_.isObject(obj)) return obj;
        return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    }

jQuery —— $.extend()

这个用法很简单:

    let originObj={ color:['red','green'], num:5 };
    let shallowClone=$.extend({},originObj); // 浅拷贝
    let deepClone=$.extend(true,{},originObj); // 深拷贝,第一个参数表示是否深度合并对象

lodash —— _.clone() / _.cloneDeep()

lodash深拷贝——这个算是这几个里面最完善的方法了,日期函数正则对象通通都能复制。

在lodash中关于复制的方法有两个,分别是_.clone()_.cloneDeep()。其中_.clone(obj, true)等价于_.cloneDeep(obj)

没引入的可以点击上面的文档链接去直接改动代码试试看。

另外,查资料过程中还看到这么一个词:结构化克隆算法
还有这一篇资料也有参考,也写得比较详细了:JS的深浅拷贝

夕凪
258 声望11 粉丝