123.《js实现实现深浅拷贝---代码文字详解》

js实现实现深浅拷贝

1.概念;

浅拷贝:当对对象进行赋值操作时,赋值后的新对象,如果改变其中第一层级的值,那么 赋值后的 新对象 相应的值也 随之改变,这就是对象的浅层次的拷贝,也称为 浅拷贝

深拷贝:当对对象进行赋值操作时,赋值后的新对象,如果改变其中第二层级或二层级后的其他层级的值,那么 赋值后的 新对象 相应的值也 不会 随之改变,这就是对象的深层次的拷贝,也称为 深拷贝

案例如下:

let  obj = {
	a: 100,
	b: 200
}
let newObj = obj
obj.a = 100
console.log(obj)  // { a: 200, b: 200 } 
console.log(newObj) // { a: 200, b: 200 }

由以上案例可发现,赋值后的对象newObj 会随着 旧对象obj 的值的改变而改变

2.出现浅拷贝的原因?

因为 obj 是引用数据类型,引用数据类型存在于 堆内存 中,而堆空间的内存会被 栈内存 中的引用地址进行一个联系,而 对象的拷贝 拷贝的其实是存在于 栈内存 中的引用地址,这就导致了 两个引用地址 指向同一 堆内存 空间,如果修改其中的一个内存引用地址,那么就会对同一堆空间的数据进行修改,这就导致了数据混乱,也就是浅拷贝带来的问题

3.解决方案:对对象的数据进行一个赋值

解决这种的问题 的 方案有很多,我从 es 3 到 es6 的方案都整理下

es3

定义一个空对象,通过 fon in 循环 把旧数据拷贝一份

let obj = { a:100, b:200 }
function es3Copy(obj){
	let newObj = {}
     for (const key in obj) {
          newObj[key] = obj[key]
     } 
     return newObj
}
let es3 = es3Copy(obj)
obj.a = 101
console.log(obj) // { a:101, b:200 }
console.log(es3) // { a:100, b:200 }
es5

通过 Object.defineProperty() 的方式

  • getOwnPropertyNames(对象) 返回指定对象的 键
  • getOwnPropertyDescriptor(obj, v) 返回 指定 键 的 描述对象
    • configurable: true // 可配置
    • enumerable: true // 是否可枚举
    • value: 1 // 该 键 对应的值
    • writable: true // 是否可 读写
let obj = { a:100, b:200 }
function es5Copy(obj){
    let newObj = {}
    Object.getOwnPropertyNames(obj).forEach( v => {
        let des = Object.getOwnPropertyDescriptor(obj, v)
        Object.defineProperty(newObj, v, des)
    })
    return newObj
}
let es5 = es3Copy(obj)
obj.a = 101
console.log(obj) // { a:101, b:200 }
console.log(es5) // { a:100, b:200 }

es6
let obj = { a:100, b:200 }
function es6Copy(obj){
	let newObj = {}
    // console.log(Object.keys(obj));
    // console.log(Object.values(obj));
    // console.log(Object.entries(obj))
    
   for (const [key, value] of Object.entries(obj)) {
   		newObj[key] = value
   } 
   return newObj
}
let es6 = es6Copy(obj)
obj.a = 101
console.log(obj) // { a:101, b:200 }
console.log(es6) // { a:100, b:200 }

这句话待严谨:javaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。

4.深拷贝

方式一:递归

运用递归,一层一层的拷贝下去

let obj = {
    a: 1,
    b: 2,
    c: {
        d: {
            e: 100
        }
    }
}

function deepCopy(obj){
	let newObj = Array.isArray(obj) ? [] : {}
	if( obj && typeof newObj ==='object' ){
		for(const key in obj){
			if( typeof obj[key] === 'object' ){
				newObj[key] = deepCopy(obj[key])
			}else{
				newObj[key] = obj[key]
			}
		}
	}else{
	  return '请传入正确的数据类型'
	}
	return newObj
}
let res = deepCopy(obj)
obj.c.d.e = 200
console.log(obj) 
console.log(res)
方式二:API

JSON.parse(JSON.stringify(obj)) 序列化 和 反序列化

let obj = {
    a: 1,
    b: 2,
    c: {
        d: {
            e: 100
        }
    }
}
let newObj = JSON.parse(JSON.stringify(obj))  
obj.c.d.e = 200
console.log(obj)
console.log(newObj)

5.JSON.parse() 的弊端

我们在使用 JSON.parse(JSON.stringify(xxx))时应该注意一下几点:

1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象
var test = {
     name: 'a',
     date: [new Date(1536627600000), new Date(1540047600000)],
   };

   let b;
   b = JSON.parse(JSON.stringify(test))
2.如果obj里有RegExpError对象,则序列化的结果将只得到空对象;
const test = {
     name: 'a',
     date: new RegExp('\\w+'),
   };
   // debugger
   const copyed = JSON.parse(JSON.stringify(test));
   test.name = 'test'
   console.error('ddd', test, copyed)
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
const test = {
        name: 'a',
        date: function hehe() {
          console.log('fff')
        },
      };
      // debugger
      const copyed = JSON.parse(JSON.stringify(test));
      test.name = 'test'
      console.error('ddd', test, copyed)
4.如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5.JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor
function Person(name) {
	  this.name = name;
	  console.log(name)
	}
	
	const liai = new Person('liai');
	
	const test = {
	  name: 'a',
	  date: liai,
	};
	// debugger
	const copyed = JSON.parse(JSON.stringify(test));
	test.name = 'test'
	console.error('ddd', test, copyed)
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值