深拷贝和浅拷贝

作者: 羽晞yose | 来源:发表于2020-06-26 01:15 被阅读0次

借用网上基本为复制粘贴的一段话:

深拷贝和浅拷贝最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用,深拷贝在计算机中开辟了一块内存地址用于存放复制的对象,而浅拷贝仅仅是指向被拷贝的内存地址,如果原地址中对象被改变了,那么浅拷贝出来的对象也会相应改变

总结上面的话就是:
深拷贝:新开堆内存,对原数据不会产生影响
浅拷贝:复制空间引用地址,栈内存中操作,会对原数据产生影响

深拷贝(常用数组与对象拷贝方法汇总)

  • slice和concat方法数组拷贝
    缺陷:只会对第一层数据进行深拷贝,仅适用于不包含引用对象的一维数组
let arr = [1, 2, {
    text: '这是源数据'
}];
let arr2 = arr.slice();
let arr3 = arr.concat([]);

arr2[1] = 20;
arr2[2].text = '这是拷贝数据';

arr3[1] = 30;
arr3[2].text = '这是拷贝数据3';
console.log(arr, arr2, arr3);
slice拷贝
concat拷贝
  • ...拓展运算符
    缺陷:只会对第一层数据进行深拷贝,仅适用于属性不包含引用对象的深拷贝
let obj = {
    a: 10,
    b: {
        text: '这是原数据'
    }
}

let obj2 = {...obj};

obj2.a = 20;
obj2.b.text = '这是拷贝数据';

console.log(obj, obj2);
拓展运算符拷贝
上图输出结果可以看出,属性a的值复制的是实例,而属性b复制的是空间地址,所以更改obj2的时候,obj.b也被改变了
  • Object.assgin 对象合并拷贝
    缺陷:只会对第一层数据进行深拷贝,仅适用于属性不包含引用对象的深拷贝
let obj = {
    a: 10,
    b: {
        text: '这是原数据'
    }
}

let obj2 = Object.assign({}, obj); // 注意第一个参数是source,也就是源对象,第二个为要合并进去的对象,顺序如果颠倒会变成直接更改obj

obj2.a = 20;
obj2.b.text = '这是拷贝数据';

console.log(obj, obj2);
image.png
  • Json.parse(Json.stringify())
    缺陷有以下几点(具体总结为数据拷贝不完整):
  1. 有时间对象,时间对象会变为字符串
  2. 有RegExp、Error对象,序列化后变成空对象
  3. 有函数,undefined,会丢失属性
  4. 有NaN、Infinity 和 -Infinity,序列化后变成null
  5. 只能序列化对象的可枚举的自有属性,如果属性是由构造函数生成的,对象变成{}
  6. 循环引用无法正确深拷贝(报错:Converting circular structure to JSON)
// 没完整覆盖上面所有情况,但每一点都写到里面了,除了递归引用,会导致程序没法正确执行,自行放开注释即可
class Test {}
let test = new Test();

// let loopObj = {}
// loopObj.a = loopObj; // 递归引用

let obj = {
    a: 10,
    b: {
        text: '这是原始数据',
        val: 20
    },
    c: function () {},
    d: undefined, // 虽然这个确实扯,给一个属性赋值undefined…
    e: new Date(),
    f: /A/,
    h: NaN,
    i: test, // 丢失constructor
    // j: loopObj
}

let obj2 = JSON.parse(JSON.stringify(obj)); // Converting circular structure to JSON

obj2.a = 100;
obj2.b.text = '这是拷贝数据';

console.log(obj, obj2);

Json.parse(Json.stringify())拷贝

因此想完整的实现深拷贝,需要使用递归来拷贝

function deepClone (obj, hash = new WeakMap()) {
    // 因为null == undefined,所以 null 与 undefined 直接返回
    // 如果是dom元素或基本类型值,直接返回
    const isElement = obj instanceof Element;
    const isEmpty = obj == undefined;
    const isNormalType = typeof obj !== 'object';

    if (isElement || isEmpty || isNormalType) return obj;

    // Object.prototype.toString.call,但window本身就是一个对象,所以直接toString.call()也是一样的
    // 当然这样写有风险,当某天window的toString被改写了,那程序就会出问题了,写文章还是严谨点
    let type = Object.prototype.toString.call(obj);
    
    switch (type) {
        case '[object Date]': return new Date(obj);
        case '[object RegExp]': return new RegExp(obj);
        case '[object Error]': return Error(obj);  // Error 与 new Error是一致的
    }

    if (hash.get(obj)) return obj;

    // 否则证明不是数组就是对象,需要递归拷贝
    // constructor保存着原函数的引用,因此直接不需要判断是数组还是对象来生成
    let clone = new obj.constructor;
    // 存储到weakMap中,下一次进来,先查找是否已有该值,有的话直接返回即可,用于解决循环引用
    hash.set(obj, clone);
    for (let key in obj) {
        clone[key] = deepClone(obj[key], hash);
    }
    
    return clone;
}
自行实现的完整克隆

关于上面的日期、正则等,其实直接返回也是可以的,因为没法更改源数据,要改那肯定是重新实例一个新的对象出来。

相关文章

  • iOS深拷贝(MutableCopy)与浅拷贝(Copy)的区别

    深拷贝和浅拷贝的概念 iOS中有深拷贝和浅拷贝的概念,那么何为深拷贝何为浅拷贝呢?浅拷贝:浅拷贝并不拷贝对象本身,...

  • iOS面试题-第二页

    11.深拷贝和浅拷贝的理解. 深拷贝;拷贝的内容. 浅拷贝:拷贝的指针. 深拷贝如: NSMutableDicti...

  • iOS - copy 与 mutableCopy

    一说到拷贝,就不得不提浅拷贝和深拷贝。 何谓浅拷贝?何谓深拷贝? 往简单的说: 浅拷贝:拷贝地址。 深拷贝:拷贝内...

  • JS中的深拷贝与浅拷贝

    知乎:js中的深拷贝和浅拷贝? 掘金: js 深拷贝 vs 浅拷贝 前言 首先深拷贝与浅拷贝只针对 Object,...

  • iOS--拷贝相关题

    1、什么是深拷贝什么是浅拷贝?浅拷贝和深拷贝的区别 * 浅拷贝(shallow copy):指针拷贝,对于被拷贝对...

  • copy和mutableCopy的区别

    copy和mutableCopy的区别 深拷贝和浅拷贝的区别 在OC中对象的拷贝方式有两种:深拷贝和浅拷贝.浅拷贝...

  • 2018-10-10函数基础

    深拷贝和浅拷贝 深拷贝 copy.deepcopy(对象)浅拷贝 copy.copy(对象)深拷贝: 将对象对应的...

  • Objective-C中的浅拷贝和深拷贝

    Objective-C中的浅拷贝和深拷贝 Objective-C中的浅拷贝和深拷贝

  • JavaScript的深拷贝和浅拷贝

    原文 博客原文 大纲 前言1、对深拷贝和浅拷贝的初步认识2、深拷贝和浅拷贝的区别3、浅拷贝存在的缺陷4、深拷贝的实...

  • java中的深拷贝和浅拷贝

    简单记录一下java中的深拷贝和浅拷贝,深拷贝和浅拷贝只是针对对象而言的. 1 深拷贝代码 2 浅拷贝代码 3 测...

网友评论

    本文标题:深拷贝和浅拷贝

    本文链接:https://www.haomeiwen.com/subject/wyzrfktx.html