深浅拷贝

250 阅读3分钟

1.浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

2.深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

浅拷贝只能拷贝一层对象,如果对象存在嵌套关系,他是不能拷贝的。

浅拷贝:

1.Object.assign()

先来看一个使用Object.assign正常的浅拷贝

var obj1 = {
    a:1,
    b:2,
    c:{
        d:3    
    }
}
var obj2 = {}
Object.assign(obj2,obj1)
obj2.a=11
obj2.c.d=33
console.log(obj1); ----->  {a:1,b:2,c:{d:33}}}
console.log(obj2); ----->  {a:11,b:2,c:{d:33}}

==注意==:

  • 它不能拷贝对象的继承属性
  • 不能拷贝对象的不可枚举属性
  • 可以拷贝Symbol类型的属性
var obj1 = {
  a:{
    b:1  
  }
}
Object.defineProperty(obj1,'c',{
    value: 3,
    enumerable: false
})
var obj2 = {}
Object.assign(obj2,obj1)
console.log(obj1) ---->  {a:{b:1},c:3}
console.log(obj2) ---->  {a:{b:1}}

2.扩展运算符

var obj1 = {a:{b:1}}
var obj2 = {...obj1}
let arr = [1, 2, 3];
let newArr = [...arr];

3. Array.slice()方法

var obj1 = [1,2,{d:3}]
var obj2 = obj1.slice()
obj2[2].d = 4
console.log(obj1);  ----> [ 1, 2, { d: 4 } ]
console.log(obj2);	 ----> [ 1, 2, { d: 4 } ]

4.Array.concat()方法

var obj1 = [1,2,{d:3}]
var obj2 = obj1.concat([])
obj2[2].d = 4
console.log(obj1);  ----> [ 1, 2, { d: 4 } ]
console.log(obj2);	 ----> [ 1, 2, { d: 4 } ]

5.手写一个浅拷贝

function shallowClone(target){
    let newResult = Array.isArray(target) ? [] : {}
    if(typeof target === 'object'){
        for(item in target){
            newResult[item] = target[item]
        }
        return newResult
    }else{
        return target
    }
}
var obj1 = [1,2,{d:3}]
var obj2 = shallowClone(obj1)
obj2[2].d = 4
console.log(obj1);  ----> [ 1, 2, { d: 4 } ]
console.log(obj2);	 ----> [ 1, 2, { d: 4 } ]

深拷贝

1.JSON.parse(JSON.stringty()) 实现

function deepCloneByJson(target){
    return JSON.parse(JSON.stringify(target))
}
var obj1 = [1,2,{d:3}]
var obj2 =deepCloneByJson(obj1)
obj2[2].d = 4
console.log(obj1);
console.log(obj2);

==注意==

  • 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
  • 拷贝Date,会变成字符串
  • 无法拷贝不可枚举属性
  • 无法拷贝对象的原型链
  • 拷贝RegExp,会变成空对象{}
  • 对象中有NaN 、Infinity、JSON序列化的结果会变成null
  • 无法拷贝对象的循环应用

2.手写一个深拷贝

function isComplexTypeData(obj){
    return (typeof obj === 'object') && obj!==null
}
function deepClone(obj, hash = new WeakMap()){
    if(obj.constructor === Date) return new Date(obj)
    if(obj.constructor === RegExp) return new RegExp(obj)
    //如果循环引用了就用 weakMap 来解决
  	 if (hash.has(obj)) return hash.get(obj)
    let desc = Object.getOwnPropertyDescriptors(obj);
    let cloneObj = Object.create(Object.getPrototypeOf(obj), desc);
    hash.set(obj, cloneObj);//继承原型链
    for(let item in Reflet.ownKeys(cloneObj)){
        cloneObj[item] = isComplexTypeData(obj[item]) && typeof !== 'function' ? deepClone(obj,hash) : obj[item]    
    }
    return cloneObj;
}

上面的是手写代码,下面的是测试代码

// 下面是验证代码
let obj = {
    num0,
    str'',
    booleantrue,
    unfundefined,
    nulnull,
    obj: { name'我是一个对象'id1 },
    arr: [012],
    funcfunction () { console.log('我是一个函数') },
    datenew Date(0),
    regnew RegExp('/我是一个正则/ig'),
    [Symbol('1')]: 1,
  };
  Object.defineProperty(obj, 'innumerable', {
    enumerablefalsevalue'不可枚举属性' }
  );
  obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
  obj.loop = obj    // 设置loop成循环引用的属性
  let cloneObj = deepClone(obj)
  //拷贝完了、实验一下
  cloneObj.arr.push(4)
  cloneObj.func()
  console.log('obj', obj)
  console.log('cloneObj', cloneObj)