深浅拷贝
首先,我们需要明白什么是深拷贝,什么是浅拷贝
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。
假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)
//作者:Mike丶 https://www.cnblogs.com/mikeCao/p/8710837.html
深拷贝就意味着,新对象与源对象对比,他们的引用地址发生了变化,举个例子就是出现了两个样貌相同的人,但是他们的家不是同一个地方
而浅拷贝只是复制了源对象的所有,包括引用地址,同样用上面的例子就是2个想通样貌的人,他们的家也是同一个地方。
let a = {
age: 1
};
let b = a;
a.age = 2;
console.log(b.age) // 2
从上述例子中我们可以发现,如果给一个变量赋值一个对象,那么两者的值会是同
一个引用,其中一方改变,另一方也会相应改变。
通常在开发中我们不希望出现这样的问题,我们可以使用深拷贝来解决这个问题;
首先可以通过 Object.assign 来解决这个问题。
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
也就是说,如果对象的属性值为简单类型(如string, number),通过Object.assign({},sourceObj);得到的新对象为深拷贝;
如果属性值为对象或其它引用类型,那对于这个对象而言其实是浅拷贝的。
...
和Object.assign()的行为是一致的。所以同样,如果对象的属性值为简单类型,即只有一层,那么得到的新对象为深拷贝,如果为对象或者其他引用类型,即超过1层,那么得到的新对象为浅拷贝。
let a = {
age: 1
}let b = {...a}
a.age = 2
console.log(b.age) // 1
那么如何进行多层的深拷贝呢?
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = {...a}
a.jobs.first = 'native'
console.log(b.jobs.first) // native
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到刚开始的话题了,两者享有相同的引用。要解决这个问题,我们需要引入深拷贝。
深拷贝
这个问题通常在es5中通过JSON.parse(JSON.stringify(object))来解决。
let a = {
age: 1,
jobs: {
first: 'FE'
}
}let b = JSON.parse(JSON.stringia.jobs.first = 'native'
le.log(b.jobs.first) // FE
但是该方法也是有局限性的:
• 会忽略 undefined
• 不能序列化函数
• 不能解决循环引用的对象
let a = {
age: undefined,
jobs: function() {},
name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}
你会发现在上述情况中,该方法会忽略掉函数和 undefined 。
但是在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问
题,并且该函数是内置函数中处理深拷贝性能最快的。当然如果你的数据中含有以上三
种情况下,可以使用 lodash 的深拷贝函数:cloneDeep
然而lodash的深拷贝对于function和err依然不会成功。
这个时候如果需要继续深拷贝,那么lodash提供了一个自定义深拷贝方法:cloneDeepWith;具体可以在官方文档中研究一下,例如深拷贝dom对象
function customizer(value) {
if (_.isElement(value)) {
return value.cloneNode(true);
}
}
var el = _.cloneDeepWith(document.body, customizer);
console.log(el === document.body);
// => false
console.log(el.nodeName);
// => 'BODY'
console.log(el.childNodes.length);
// => 20