js实现实现深浅拷贝
文章目录
- js实现实现深浅拷贝
- 1.概念;
- 2.出现浅拷贝的原因?
- 3.解决方案:对对象的数据进行一个赋值
- 4.深拷贝
- 5.JSON.parse() 的弊端
- 1、如果obj里面有时间对象,则`JSON.stringify`后再`JSON.parse`的结果,时间将只是字符串的形式。而不是时间对象
- 2.如果obj里有`RegExp`、`Error`对象,则序列化的结果将只得到空对象;
- 3、如果obj里有函数,`undefined`,则序列化的结果会把函数或 undefined丢失;
- 4.如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
- 5.`JSON.stringify()`只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用`JSON.parse(JSON.stringify(obj))`深拷贝后,会丢弃对象的`constructor`;
- 6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
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里有RegExp
、Error
对象,则序列化的结果将只得到空对象;
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)