基本数据类型的复制很简单,就是赋值操作,所以深浅拷贝也是针对Object,Array这类引用类型数据。
浅拷贝对于字符串来说,是值的复制,而对于对象来说则是对对象地址的复制;而深拷贝的话,它不仅将对象的各个属性逐个复制出来,还将各个属性所包含的对象也依次复制出来,相当于在堆区又新开了一块地存放,主要采取递归来实现。
实现方式
原生JS实现:
浅拷贝:
let originObj={ color:['red','green'], num:5 };
function shallowClone(obj){
let newObj=(obj instanceof Array) ? []:{};
for(let item in obj){
if(obj.hasOwnProperty(item)){ // 避免列举出原型上的属性
newObj[item]=obj[item];
}
}
return newObj;
}
let cloneObj=shallowClone(originObj);
深拷贝:
function deepClone(obj){
let newObj=(obj instanceof Array)? [] : {};
for(let item in obj){
if(obj.hasOwnProperty){
const val=obj[item];
debugger
newObj[item]=typeof val==='object' ? deepClone(val) : val;
}
}
return newObj;
}
let cloneObj2=deepClone(originObj);
其实,这种深拷贝的实现还是有些问题的,比如日期
,正则对象
无法复制,还有一个是循环引用
,类似闭包会内存泄露一样,这样的对象深拷贝也会陷入一个闭环,直到栈溢出。
为了解决这个问题,就得先判断这个对象是否等于原对象。
function deepClone(obj){
let tempArr=[];
let newObj=(obj instanceof Array) ? [] : {};
tempArr.push(obj);
for(let item in obj){
if(typeof obj[item] === 'object'){
const index=tempArr.indexOf(obj[item]);
// 如果已存在,证明引用了相同对象,那么无论是循环引用还是重复引用,我们返回引用就可以了
if(index>-1){
newObj[item]=tempArr[index];
}else{
newObj[item]=obj[item];
}
}else{
newObj[item]=obj[item];
}
}
return newObj;
}
ES6实现
let newObj1=Object.assgin({},originObj);
let newObj2={...obj}; // 数组用[...obj]
注:Object.assgin和扩展运算符实现的都是浅拷贝。
JSON实现:
这种方法简单是简单,不过这个方法有以下缺陷:
1.会忽略函数对象以及原型对象,正则会复制成空对象,而日期对象会变成字符串。
2.它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;
3.如果对象中存在循环引用的情况无法正确处理。
按需取用吧。
let originObj={fn:function(){console.log(111)}, colors:['red','gree'],reg:/\s/g};
let newObj=JSON.parse(JSON.stringify(originObj));
第三方库实现:
转自链接:深入剖析 JavaScript 的深复制
Underscore —— _.clone()
顺便安利下underscore,真的挺好用的一个库: Underscore中文文档
这个方法实际上是一种浅复制 (shallow-copy),所有嵌套的对象和数组都是直接复制引用而并没有进行深复制。
let originObj={ color:['red','green'], num:5 };
let newObj=_.clone(originObj);
newObj.colors.push('grey');
originObj.colors; // ['red','green','grey'];
源码逻辑很简单:(当然调用的其他方法也是underscore的)
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
}
jQuery —— $.extend()
这个用法很简单:
let originObj={ color:['red','green'], num:5 };
let shallowClone=$.extend({},originObj); // 浅拷贝
let deepClone=$.extend(true,{},originObj); // 深拷贝,第一个参数表示是否深度合并对象
lodash —— _.clone() / _.cloneDeep()
lodash深拷贝——这个算是这几个里面最完善的方法了,日期
,函数
,正则对象
通通都能复制。
在lodash中关于复制的方法有两个,分别是_.clone()
和_.cloneDeep()
。其中_.clone(obj, true)
等价于_.cloneDeep(obj)
。
没引入的可以点击上面的文档链接去直接改动代码试试看。
另外,查资料过程中还看到这么一个词: 结构化克隆算法
还有这一篇资料也有参考,也写得比较详细了: JS的深浅拷贝