前言
转载自:
Java 的深拷贝和浅拷贝_浅拷贝
一篇文章彻底说清JS的深拷贝/浅拷贝
1. 深浅拷贝的来由和区分
除了基本数据类型(元类型)之外,还存在 类的实例实例对象 这个引用数据类型。而一般使用 『 = 』号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。
而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。
2. 复合类型深拷贝的最快解
var copyObj = {
name: 'ziwei',
arr : [1,2,3]
}
var targetObj = JSON.parse(JSON.stringify(copyObj))
// 此时 copyObj.arr !== targetObj.arr 已经实现了深拷贝
别着急走,利用window.JSON的方法做深拷贝存在2个缺点:
如果你的对象里有函数,函数无法被拷贝下来
无法拷贝copyObj对象原型链上的属性和方法
当然,你明确知道他们的缺点后,如果他的缺点对你的业务需求没有影响,就可以放心使用了,一行原生代码就能搞定。
目前我在开发业务场景中,大多还真可以忽略上面2个缺点。往往需要深拷贝的对象里没有函数,也不需要拷贝它原型链的属性。
3.深拷贝和浅拷贝的实现方式
其实JQ里已经有$.extend()函数,实现就是深拷贝和浅拷贝的功能。有兴趣的小伙伴也可以看看源码。
浅拷贝
浅拷贝比较简单,就是用for in 循环赋值
function shallowCopy(source, target = {}) {
var key;
for (key in source) {
if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的属性,我不拷贝
target[key] = source[key];
}
}
return target;
}
深拷贝的实现
深拷贝,就是遍历那个被拷贝的对象
判断对象里每一项的数据类型
如果不是对象类型,就直接赋值,如果是对象类型,就再次调用deepCopy,递归的去赋值。
function deepCopy(source, target = {}) {
var key;
for (key in source) {
if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的属性,我不拷贝
if (typeof(source[key]) === "object") { // 如果这一项是object类型,就递归调用deepCopy
target[key] = Array.isArray(source[key]) ? [] : {};
deepCopy(source[key], target[key]);
} else { // 如果不是object类型,就直接赋值拷贝
target[key] = source[key];
}
}
}
return target;
}
以上的无论深、浅拷贝,都用了source.hasOwnProperty(key),意思是判断这一项是否是其自有属性,是的话才拷贝,不是就不拷贝。
也就是说proto上面的属性,我不拷贝。这个其实你可以根据业务需求,来决定加上和这个条件
(JQ的$.extend()是会连proto上的属性也拷贝下来的,但是是直接拷贝到对象上,而不是放到之前的proto上)
4.总结与建议
虽然大家可能经常用框架提供的api来实现深拷贝。
这篇文章分享的目的,更多还是希望用一篇文章整理清楚深浅拷贝的含义、递归实现思路,以及小伙伴们如果使用了JSON.parse()这种黑科技,一定要清楚这样写的优缺点。