1. 浅拷贝、深拷贝
- 浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份拷贝。
- 属性是基本类型 => 拷贝的就是基本类型的值
- 属性是引用类型 => 拷贝的就是内存地址
- 指向同一个对象
a = {value: 1}
b = {'foo': a}
c = {'foo': a}
// a的修改会同时影响b、c
a.value = 2;
console.log(b.foo.value); // 2
console.log(c.foo.value); // 2
- 深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象
- 属性是基本类型 => 拷贝的就是基本类型的值
- 属性是引用类型 => 创建一个新对象,进行完整拷贝
- 指向不同对象,修改互不影响
2. 浅拷贝方法
2.1 Object.assign()
将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。
s1 = {
age: 10,
info: {
class: 9001
}
}
s2 = Object.assign({}, s1) //{age: 10, info: {…}}
s1.info === s2.info // true
2.2 展开运算符 ...
s1 = {
age: 10,
info: {
class: 9001
}
}
s2 = {...s1} //{age: 10, info: {…}}
s1.info === s2.info // true
2.3 Array.prototype.concat()
a1 = [1, '2', {class: 9001}]
a2 = a1.concat()
a2[2] === a1[2] // true
2.4 Array.prototype.slice()
a1 = [1, '2', {class: 9001}]
a2 = a1.slice()
a2[2] === a1[2] // true
3. 深拷贝方法
3.1 JSON.parse(JSON.stringify())
- JSON.stringify将对象转成JSON字符串
- JSON.parse把字符串解析成对象
s1 = {
age: 10,
info: {
class: 9001
}
}
s2 = JSON.parse(JSON.stringify(s1))
s1.info === s2.info // false
可以看到处理后 s2 的 info 为一个全新的对象
缺点:
- 函数丢失
- undefined丢失
- Date对象变成字符串
- 无法解决循环引用
s3 = {
age: undefined,
getAge(){
return age;
},
reg: /test/,
date: new Date()
}
s4 = JSON.parse(JSON.stringify(s3))
通过上图的对比,可以发现 age \ getAge 丢失,reg变成空对象,date变成字符串。
3.2 手写深拷贝
要点:
- 引用类型递归处理
- 使用map处理循环引用
- 函数
const deepClone = (a, map = new Map()) => {
// 类型判断,引用类型
if (a instanceof Object) {
// 已缓存,则直接返回
if (map.get(a)) {
return map.get(a);
}
let result = {};
if(a instanceof Array) {
// 数组处理
result = [];
} else if (a instanceof Date) {
// Date处理
result = new Date(a);
} else if (a instanceof RegExp) {
// 正则处理,分为source、flags
result = new RegExp(a.source, a.flags);
}
// 缓存
map.set(a, result);
// 遍历键
for (let key in a) {
// 只处理实例本身的属性
if (a.hasOwnProperty(key)) {
// 属性值需要递归处理
result[key] = deepClone(a[key], map);
}
}
} else {
// 非引用类型直接返回
return a;
}
};