原生js的对象拷贝都是浅拷贝,如果需要深拷贝则要手动做一些操作
使用json序列化和反序列化
例子:
let obj = {
name: 'lizitao',
msg: {
age: '2'
}
};
let cp = JSON.parse(JSON.stringify(obj));
console.log(cp);
优点: 写起来非常简单
缺点:局限性太多了。因为只支持json所支持的那些数据类型。像函数、undefined、正则等这些都是不支持的。遇到环状结构则会报错:
var obj = {
a: undefined
};
console.log(JSON.parse(JSON.stringify(obj))) // {}
// 属性a被自动忽略了
JSON.stringify则会检测是否存在环,存在则抛出错误
var obj = {};
obj.child = obj;
JSON.stringify(a) // Uncaught TypeError: Converting circular structure to JSON
所以我们只能复杂一点的方式,用递归来实现深拷贝
第一版(雏形),能复制简单对象
const clone = function(source){
if(source instanceof Object){
let result = {}
for (const key in source) {
result[key] = clone(source[key])
}
return result
} else {
return source
}
}
先判断是否为对象,非对象则直接返回它的值。如果是对象则新建一个空对象 {},并复制属性到该对象上。注意属性的复制是递归调用clone函数的:result[key] = clone(source[key])
,这样才能保证每一层都是深拷贝!。
不过它只判断了是否为对象,并假设对象都是像{name: value}的形式。如果对象是数组、函数、日期等等就不能正常运行了。所以要判断对象到底是哪种类型的对象,并根据对象具体类型来初始化result,用这样的形式:
if(source instanceof Object){
//...
if(source instanceof Array){
//...
} else if(source instanceof Function){
//...
} else if(source instanceof Date){
//...
}
// 省略许多else if, 判断越详细那么支持的对象类型就越多
else {
//...
}
} else {
//...
}
于是我们按照这个思路写出第二版
第二版,支持多种类型的对象
const clone = function(source){
if(source instanceof Object){
let result = null
if(source instanceof Array){
result = []
} else if(source instanceof Function){
result = function(){
return source.apply(this, arguments)
}
} else if (source instanceof Date) {
result = new Date(source);
} else {
result = {}
}
for (const key in source) {
result[key] = this.clone(source[key])
}
return result
} else {
return source
}
}
最后要解决的是对象是环的情况。基本思路是用一个数组来存储所有复制过的对象,每次调用前先判断对象是否复制过了,如果复制过就直接返回。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。