浅拷贝:
let obj = {a: 123, b: 456, c: {a:123,b: 456}};
let copy = {}
copy.a = obj.a;
copy.b = obj.b;
copy.c = obj.c;
copy.c.a = 222;
console.log(obj)
从上面结果可以看出,浅拷贝只拷贝一层。对于obj属性仍然为引用的内容并没有进行拷贝,而是共用了一个,所以对于copy.c.a的修改同时也影响到了obj。
实现一个浅拷贝copy函数:
let obj = {a: 123, b: 456, c: {a:123,b: 456}};
function copy (value) {
if (typeof value === 'object') {
let isArray = value instanceof Array
let rs = isArray? []:{}
for (let i in value) {
rs[i] = value[i]
}
return rs
} else {
return value
}
}
let newObj = copy(obj);
console.log(newObj)
当然,如果您知道您拷贝的就是一个数组,那么可以通过arr.slice(0)
来实现,或者通过 [].concat()
来实现,或者通过es6的…扩展属性来实现let rs = isArray? [...value]: {...value}
,如果不知道那么就可以通过上述函数方式了。
深拷贝:
实现一个深拷贝:
let obj = {a: 123, b: 456, c: {a:123,b: 456}};
function copy (value) {
if (typeof value === 'object') {
let isArray = value instanceof Array
let rs = isArray? []:{}
for (let i in value) {
rs[i] = copy(value[i])
}
return rs
} else {
return value
}
}
let newObj = copy(obj);
newObj.c.a = 1111;
console.log(obj)
通过上面结果可知:深拷贝是完全复制了两份内容,对一份的修改不能影响到另一份的变化。
实现深拷贝的另一种方式:
JSON.parse(JSON.stringify(obj))
然而:
上面那种深拷贝是最简单的情况,针对的是数组和对象的情况,并没有考虑其他数据类型。
复杂情况1:
比如:
let obj = {a: 123, b: 456, c: {a:123,b: 456}};
obj.d = obj;
function copy (value) {
if (typeof value === 'object') {
let isArray = value instanceof Array
let rs = isArray? []:{}
for (let i in value) {
rs[i] = copy(value[i])
}
return rs
} else {
return value
}
}
let newObj = copy(obj);
newObj.c.a = 1111;
console.log(obj)
运行后会报如下错误:
错误原因分析:for循环陷入了死循环。
解决办法:
let obj = {a: 123, b: 456, c: {a:123,b: 456}};
obj.d = obj;
function copy (value, map = new Map()) {
if (typeof value === 'object') {
if(map.get(value)) {
return map.get(value);
}
let isArray = value instanceof Array
let rs = isArray? []:{}
map.set(value, rs)
for (let i in value) {
rs[i] = copy(value[i], map)
}
return rs
} else {
return value
}
}
let newObj = copy(obj);
console.log(newObj.d)
复杂情况2:
如何数据类型有symbol,而for循环是对其不支持的。
通过Reflect.ownKeys()解决
let obj = {a: 123, b: 456, c: {a:123,b: 456}};
obj.d = obj;
obj[Symbol('e')]='dadfadf'
function copy (value, map = new Map()) {
if (typeof value === 'object') {
if(map.get(value)) {
return map.get(value);
}
let isArray = value instanceof Array
let rs = isArray? []:{}
map.set(value, rs)
// for (let i in value) {
// rs[i] = copy(value[i], map)
// }
Reflect.ownKeys(isArray? [...value]:{...value}).forEach(key=>{
rs[key] = copy(value[key], map)
})
return rs
} else {
return value
}
}
let newObj = copy(obj);
console.log(newObj.d)
复杂情况3:
如果数据类型有Date这些,那又该怎么办?
通过结构化克隆法
let obj = {a: 123, b: 456, c: {a:123,b: 456}, f: new Date};
obj.d = obj;
obj[Symbol('e')]='dadfadf'
function copy (value, map = new Map()) {
if (typeof value === 'object') {
if(map.get(value)) {
return map.get(value);
}
let isArray = value instanceof Array
let rs = isArray? []:{}
map.set(value, rs)
switch(value.constructor){
case Date:
rs = new value.constructor(value)
break
default:
Reflect.ownKeys(isArray? [...value]:{...value}).forEach(key=>{
rs[key] = copy(value[key], map)
})
}
// for (let i in value) {
// rs[i] = copy(value[i], map)
// }
return rs
} else {
return value
}
}
let newObj = copy(obj);
console.log(newObj.d)
除此之外,还有很多情况需要考虑。。所有JavaScript里深拷贝并不是一个简单的东西,可以参考一下Lodash这个库里如何实现的深拷贝。
整理自:https://www.bilibili.com/video/av49559690?from=search&seid=10412422805049926042