前言
无论是面试中还是实际工作中,遇到深浅拷贝的概率都挺大的。
由于浅拷贝比较简单,本文中就不提了,主要聊一聊深拷贝。
首先列举出深拷贝常见的几种方式:
- 迭代法
- 序列化和反序列化
Object.create()
前方长篇预警...
一、简单对象的深拷贝
对于数据类型简单的对象,如:
let obj = {
a: {
b: 1,
c: 2
},
d: [3, 4]
}
复制代码
方法一(for...in):
我们用简单的for...in...
递归就可以实现此对象的深拷贝
function deepClone(obj) {
// obj 不是对象则不执行
if (typeof obj !== 'object') return;
let res = Array.isArray(obj) ? [] : {};
for (let key in obj) {
res[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
}
}
let obj2 = deepClone(obj);
obj.a.b = 2;
obj.d.push(10);
console.log(obj2.a.b); // 1
console.log(obj2.d); // [3, 4]
复制代码
这样 obj2
就是拷贝出的一个新对象,完全不受 obj
中值发生改变的影响。
方法二(Reflect):
function deepClone2(obj) {
// obj 不是对象则不执行
if (typeof obj !== 'object') return;
let res = Array.isArray(obj) ? [...obj] : { ...obj }
Reflect.ownKeys(res).forEach( key => {
res[key] = typeof obj[key] === 'object' ? deepClone2(obj[key]) : obj[key];
})
}
let obj2 = deepClone2(obj);
obj.a.b = 2;
obj.d.push(10);
console.log(obj2.a.b); // 1
console.log(obj2.d); // [3, 4]
复制代码
这个方法使用了Reflect.ownKeys
方法遍历后递归拷贝,我们发现结果和for...in
的方法得到的结果一样,那两者的区别是什么呢?我们后面再说。
方法三(JSON.parse):
function deepClone3(obj) {
return JSON.parse(JSON.stringify(obj))
}
复制代码
使用这种方法的问题在于以下几点
- 会忽略
undefined
- 会忽略
symbol
- 不能序列化函数
- 不能解决循环引用的对象
- 会抛弃对象的
constructor
也就是这种方式的深拷贝,只能处理那些能用json
表示的对象,而且拷贝后,不管这个对象原来的构造函数是什么,都会变成Object类型。
以上就是对于简单对象深拷贝的实现,上述三种方法基本都可以满足需求
二、复杂对象的深拷贝
上面说到的几种方法,对于对象中只存在数组,Object
的,可以实现深拷贝,但是如果对象属性中有方法或者其他类型的对象,应该怎么处理呢?
- 思考来源于阿里的一道面试题
// 实现如下对象的深拷贝
let obj = {
obj: {
name: '我是一个对象',
id: 1
},
arr: [0, 1, 2],
fn: () => {
console.log('我是一个函数')
},
date: new Date(),
reg: new RegExp('/我是一个正则/ig'),
err: new Error('我是一个错误')
}
复制代码
对于这样的一个对象,如果运用上面的三种方法,我们发现用前两种方法,得到的结果是这样的
arr
和obj
拷贝成功了,其他的都没有拷贝成功。
用序列化的方法拷贝的话得到的结果是这样的:
我们发现对于数组和
Object
以外的其他对象,都会失真,
date
只剩一个字符串了。
解决方案
首先对于function
类型的值,我们可以先把它转换为字符串,再使用eval
方法。
改进后的代码:
function deepClone(obj) {
// obj 不是对象则不执行
if (typeof obj !== 'object') return;
let res = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (typeof obj[key] === 'object') {
res[key] = deepClone(obj[key]);
} else if (typeof obj[key] === 'function') {
res[key] = eval(obj[key].toString());
} else {
res[key] = obj[key];
}
}
}
复制代码
未完待续...