0%

深浅拷贝

浅拷贝

1
object.assign()

不会拷贝对象的继承属性、不会拷贝对象的不可枚举属性、可以拷贝 Symbol 类型的属性

1
2
3
4
5
let target = {};
let source = {a: {b: 1}};

Object.assign(target, source);
console.log(target);

还有 concat、slice、拓展运算符均可以实现浅拷贝。

手工实现浅拷贝:

1
2
3
4
5
6
7
8
9
10
11
12
13
const shallowClone = (target) => {
if (target instanceof object) {
const cloneTarget = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = target[key];
}
}
return cloneTarget;
}

return target;
}

深拷贝

乞丐版:JSON.stringify()

存在的问题:(可以结合之前文章的手写 JSON.stringify() 看)

  • 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,拷贝后整个键值对消失
  • 拷贝后 Date 引用类型变为字符串(调用了 toJSON)
  • 无法拷贝不可枚举的属性
  • 无法拷贝对象的原型链
  • 拷贝 RegExp 会变为空对象
  • 对象中含有 NaN、Infinity,拷贝结果会变为 null
  • 无法拷贝循环引用

手写深拷贝:

首先要了解一个方法:Object. getOwnPropertyDescriptors(),这个方法用于获得属性的特性

1
2
3
4
5
6
const person = {
name: '张三',
age: 18
}
const desc = Object.getOwnPropertyDescriptors(person);
console.log(desc);

还有一个是 Reflect.ownKeys(),它返回一个由目标对象自身的属性键组成的数组。返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

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
48
49
50
51
52
53
54
55
56
57
58
59
60
const isComplexDataType = obj => ((typeof obj === 'object' || typeof obj === 'function') && obj !== null);

// WeakMap 防止内存泄漏
const deepClone = (obj, hash = new WeakMap()) => {
// 特判 Date 和 RegExp,返回一个新对象
if (obj?.constructor === Date) {
return new Date(obj);
}
if (obj?.constructor === RegExp) {
return new RegExp(obj);
}

// 处理循环引用
if (hash.has(obj)) {
return hash.get(obj);
}

// 得到属性值和属性的描述
let desc = Object.getOwnPropertyDescriptors(obj);

// 继承原型链,包括其 descriptors
let cloneObj = Object.create(Object.getPrototypeOf(obj), desc);

// 设置 hash,用于后续检测循环引用
hash.set(obj, cloneObj);

// 遍历所有键值
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function' ? deepClone(obj[key], hash) : obj[key]);
}
return cloneObj;
}

// 验证代码
let obj = {
num: 0,
str: 'string',
boolean: true,
unf: undefined,
nul: null,
obj: { name: 'Jack', id: 1 },
arr: [0, 1, 2],
func: function() { console.log('hello') },
date: new Date(),
reg: new RegExp('/我是正则/ig'),
[Symbol('我是 Symbol')]: 1,
}

Object.defineProperty(obj, 'innumerable', {
enumerable: false,
value: '不可枚举',
})

obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj));
obj.loop = obj; // 循环引用

let cloneObj = deepClone(obj);
cloneObj.arr.push(1234);
console.log(obj);
console.log(cloneObj);

本文标题:深浅拷贝

文章作者:Flower-F

发布时间:2022年02月20日 - 19:36

最后更新:2022年02月21日 - 17:07

-------------本文结束,感谢您的阅读-------------

欢迎关注我的其它发布渠道