如何实现深拷贝

简单写一下js实现深拷贝的思路

思路

  • 判断类型,如果是基本数据类型,直接返回;如果是引用数据类型,浅拷贝一个新值代替原来的值;
  • 遍历浅拷贝的对象,检测这个浅拷贝的对象的属性值有没有是引用数据类型,如果是,递归拷贝;
  • 记录被拷贝的值,避免循环引用
  • 需要忽略原型

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 判断是否基本数据类型
function isPrimary(value){
return (typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean')
}

// 判断是否是 js 对象
function isObject(value){
return Object.prototype.toString.call(value) === "[object Object]"
}

function deepClone(value){

// 记录被拷贝的值,避免循环引用的出现
let memo = {};

function baseClone(value){
let res;
// 如果是基本数据类型,则直接返回
if(isPrimary(value)){
return value;
// 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值
}else if(Array.isArray(value)){
res = [...value];
}else if(isObject(value)){
res = {...value};
}

// 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝
Reflect.ownKeys(res).forEach(key=>{
if(typeof res[key] === "object" && res[key]!== null){
//此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题
if(memo[res[key]]){
res[key] = memo[res[key]];
}else{
memo[res[key]] = res[key];
res[key] = baseClone(res[key])
}
}
})
return res;
}

return baseClone(value)
}

其他

js中遍历一个对象的属性的方法

  • Object.keys() 返回自身可枚举属性,不包括继承属性,不包括Symbol属性
  • Object.getOwnerPropertyNames() 返回自身可枚举和不可枚举属性,不包括Symbol属性
  • Object.getOwnerPropertySymbols() 返回自身Symbol属性
  • for..in 遍历对象自身和继承的可枚举属性,不包括Symbol属性
  • Reflect.ownKeys() 返回对象自身所有属性,不管是否可枚举,包括Symbol属性,不包括继承属性

使用 toString() 检测对象类型

  • 可以通过 toString() 获取每个对象的类型
  • Object.prototype.toString.call(value)Object.prototype.toString.apply(value)