浅谈深拷贝 内附超好用的深拷贝方法

目录

1. 什么是深拷贝和浅拷贝?

2. Object.assign()

3. 浅拷贝实现

4. 深拷贝实现 

4.1 实用简洁版

4.2 基础版

4.3 优化版


最近遇到一些对象赋值问题,究其根本其实就是深拷贝和浅拷贝的问题,随手记录一下

1. 什么是深拷贝和浅拷贝?

浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

2. Object.assign()

在说浅拷贝和深拷贝之前,先介绍一个ES6的Object.assign()的方法,主要用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象上。用法如下:

let target = { a: 0 };
let source1 = { b: 1 };
let source2 = { c: 2 };
Object.assign(target, source1, source2);
console.log(target); // 输出 {a: 0, b: 1, c: 2}

ps:Object.assign()的用途很广泛,感兴趣大家可以自行百度。这里就可以用来对象拷贝。大部分资料直接说用来作为浅拷贝,直接贴代码简单介绍下:

		// 1.复制对象 Object.assign({}, obj)
		let obj = {
			name: '张三',
			education: {
				primary: '第一小学',
				middle: '第二中学',
				university: '第三大学'
			}
		};
		let copyObj = Object.assign({}, obj);
		console.log('----------初始化----------');
		console.log('obj.name:', obj.name);
		console.log('copyObj.name:', copyObj.name);
		console.log('obj.name === copyObj.name:', obj.name === copyObj.name);

		console.log('----------原值重新赋值----------');
		obj.name = '李四';
		console.log('obj.name:', obj.name);
		console.log('copyObj.name:', copyObj.name);
		console.log('obj.name === copyObj.name:', obj.name === copyObj.name);

		console.log('----------拷贝值重新赋值----------');
		copyObj.name = '王五';
		console.log('obj.name:', obj.name);
		console.log('copyObj.name:', copyObj.name);
		console.log('obj.name === copyObj.name:', obj.name === copyObj.name);

对应输出结果

可以看到,拷贝后的对象属性和原值在修改时,并不改变彼此的值,看起来就是深拷贝,但是name属性是值类型,可以作为深拷贝,我们接下来换引用类型的education属性测试下:

		console.log('----------初始化----------');
		console.log('obj.education:', obj.education);
		console.log('copyObj.education:', copyObj.education);
		console.log('obj.education === copyObj.education:', obj.education === copyObj.education);
		console.log('----------原值重新赋值----------');
		obj.education.primary = '改后小学'
		console.log('obj.education:', obj.education);
		console.log('copyObj.education:', copyObj.education);
		console.log('obj.education === copyObj.education:', obj.education === copyObj.education);
		console.log('----------拷贝值重新赋值----------');
		copyObj.education.middle = '改后中学'
		console.log('obj.education:', obj.education);
		console.log('copyObj.education:', copyObj.education);
		console.log('obj.education === copyObj.education:', obj.education === copyObj.education);

 对应输出结果

其实根据===的恒等一直是true就知道,引用类型的属性值共同指向同一个内存地址,无论改变原始值还是拷贝值的属性,都会相互影响,是浅拷贝的状态。

综合看下来,我们不能直接说Object.assign()是浅拷贝或者深拷贝,

在对象属性仅是值类型的情况下,它可以实现深拷贝

在对象数据是引用类型的请跨下,它只是浅拷贝

不过大部分场景,我们用它来作为浅拷贝的代表,毕竟对象的属性很难确保一定只有值类型

3. 浅拷贝实现

Object.assign(target, ...sources)
let target = {};
let source = { a: { b: 2 } };
Object.assign(target, source);
target.a===source.a   //true

4. 深拷贝实现 

深拷贝方法其实有很多,最常用的最实用的就是这个了

4.1 实用简洁版

let co = JSON.parse(JSON.stringify(o));

直接类型强转,简单粗暴,其实我个人还挺喜欢(❤ ω ❤)

4.2 基础版

function deepCopy(obj) {
  if(typeof obj === "object") {
      if(obj.constructor === Array) {
          var newArr = []
          for(var i = 0; i < obj.length; i++) newArr.push(obj[i])
          return newArr
      } else {
          var newObj = {}
          for(var key in obj) {
              newObj[key] = this.deepCopy(obj[key])
          }
          return newObj
      }
  } else {
      return obj
  }
}

这个也是经常使用的,通过类型判断进行逐层拷贝,也比较中规中矩,但是假如对象属性又指向对象本身造成循环引用的时候,该方法直接堆栈溢出报错了。

4.3 优化版

		const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
		
		const deepClone = function (obj, hash = new WeakMap()) {
		    if (hash.has(obj)) return hash.get(obj)
		    let type = [Date,RegExp,Set,Map,WeakMap,WeakSet]
		    if (type.includes(obj.constructor)) return new obj.constructor(obj);      
		    //如果成环了,参数obj = obj.loop = 最初的obj 会在WeakMap中找到第一次放入的obj提前返回第一次放入WeakMap的cloneObj
		
		    let allDesc = Object.getOwnPropertyDescriptors(obj);  //遍历传入参数所有键的特性
		    let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc); //继承原型
		    hash.set(obj, cloneObj)
		
		    for (let key of Reflect.ownKeys(obj)) {   //Reflect.ownKeys(obj)可以拷贝不可枚举属性和符号类型
		        // 如果值是引用类型(非函数)则递归调用deepClone
		        cloneObj[key] =
		            (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ?
		                deepClone(obj[key], hash) : obj[key];
		    }
		    return cloneObj;
		};
		
		let obj = {
		    bigInt: BigInt(12312),
		    set:new Set([2]),
		    map:new Map([['a',22],['b',33]]),
		    num: 0,
		    str: '',
		    boolean: true,
		    unf: undefined,
		    nul: null,
		    obj: {
		        name: '我是一个对象',
		        id: 1
		    },
		    arr: [0, 1, 2],
		    func: function () {
		        console.log('我是一个函数')
		    },
		    date: new Date(0),
		    reg: new RegExp('/我是一个正则/ig'),
		    [Symbol('1')]: 1,
		};
		
		Object.defineProperty(obj, 'innumerable', {
		    enumerable: false,
		    value: '不可枚举属性'
		});
		
		obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
		
		obj.loop = obj
		
		let cloneObj = deepClone(obj);
		
		console.log('obj', obj);
		console.log('cloneObj', cloneObj);
		
		for (let key of Object.keys(cloneObj)) {
		    if (typeof cloneObj[key] === 'object' || typeof cloneObj[key] === 'function') {
		        console.log(`${key}相同吗? `, cloneObj[key] === obj[key])
		    }
		}

输出结果

 

这个版本忘记在哪里看的了,也不是我写的,比较牛的是,即考虑了循环引用,还有些特殊类型,用该方法近乎无敌,可以直接搬运当成工具类使用了,(如有侵权,请告知删除)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深拷贝和浅拷贝是针对引用数据类型而言的。浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。而深拷贝则是创建一个新对象,这个对象有着原始对象属性值的一份精准拷贝,包括基本类型和引用类型的值。下面是一些实现深拷贝和浅拷贝的方法: 浅拷贝的实现方法: 1. 使用Object.assign方法:可以将一个或多个源对象的属性复制到目标对象中,返回目标对象。这种方法只能实现浅拷贝。 2. 使用for in方法:遍历源对象的属性,将属性复制到目标对象中。这种方法也只能实现浅拷贝。 深拷贝的实现方法: 1. 递归实现:遍历源对象的属性,如果属性是基本类型,则直接复制到目标对象中;如果属性是引用类型,则递归调用深拷贝函数,将引用类型的属性也进行深拷贝。 2. 使用JSON.stringify与JSON.parse:先将源对象转换为JSON字符串,再将JSON字符串转换为新的对象。这种方法可以实现深拷贝,但是有一些限制,比如无法拷贝函数、正则表达式等特殊类型的属性。 3. 使用第三方库lodash:lodash是一个常用的JavaScript函数库,其中提供了深拷贝的函数cloneDeep,可以方便地实现深拷贝。 以上是一些常见的深拷贝和浅拷贝的实现方法。具体使用哪种方法取决于你的需求和场景。 #### 引用[.reference_title] - *1* *2* [深拷贝、浅拷贝及其实现方式](https://blog.csdn.net/m0_53375764/article/details/126711211)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [理解浅拷贝和深拷贝以及实现方法](https://blog.csdn.net/weixin_45811256/article/details/127943678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值