深浅拷贝

项目中经常会遇到一些场景比如说从后端获取的数据,前端保存一份,用户需要修改编辑,如果页面需要同时展示两份数据的话,这时候前端就需要同时保留两份独立的数据,就需要用到拷贝,如果涉及到多层级的对象修改,就需要用到深拷贝。首先说一下几个易混的概念。

赋值,浅拷贝,深拷贝

  1. 值类型的数据 赋值是值赋值, 引用类型的数据(数组,对象) 赋值是地址赋 值,修改目标对象,来源对象也会更改
  2. 浅拷贝 如果来源对象的属性都是值类型的数据,修改目标对象,来源对象不会更改。如果属性值是引用类型的数据则需要使用深拷贝(遍历)
  3. JSON.parse( JSON.stringify() ) 序列化和反序列 JSON 在执行字符串化的这个过程时,会先进行一个 JSON 格式化,获得安全的 JSON 值,因此如果是非安全的 JSON 值,就会被丢弃掉。 其中 undefined、function、symbol 这三种类型的值就是非安全的(包括该对象的属性循环赋值该对象),所以格式化后,就被过滤掉了,而 set、map 这种数据格式的对象,也并没有被正确处理,而是处理成了一个空对象
  4. object.assgn() 属于浅拷贝

浅拷贝实现

es6 中的解构赋值可以实现

1
let obj1 = { ...obj}

深拷贝实现

  1. 如果对象中属性值不涉及到,函数、Data、以及正则表达式,直接使用 JSON 转换
1
let obj1 = JSON.parse(JSON.stringify(obj))
  1. 涉及复杂属性值的可以借助循环遍历逐层赋值
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

/**
* @method 深度克隆
* @param {obj} source 来源数据
* @return {obj} target 目标对象
*/

function deepClone(source) {
if (source == null) return null;
if (typeof source !== 'object') return source;
// 如果是正则
if (source instanceof RegExp){
return new RegExp(source);
}
// 如果是日期
if (source instanceof Date){
return new Date(source);
}

// 不直接创建空对象的目的:克隆的结果和之前保持相同的所属类

var target = dataType(source) == "Object" ? new source.constructor : [];
// 遍历source的所有可枚举属性
for (key in source) {

if (
dataType(source[key]) == "Object" ||
dataType(source[key]) == "Array"
) {
target[key] = deepClone(source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
function dataType(data) {
return Object.prototype.toString.call(data).slice(8, -1);
}