思路
- 什么是拷贝或者说复制
- 有哪些数据类型,对于不同数据类型来说复制或者说拷贝都是什么操作和意义
- 什么是深拷贝或者说浅拷贝
- 如何去实现一个深拷贝
- 如何实现引用类型里面仍然有引用对象的情况?
1. 什么是拷贝
拷贝,是英文copy的音译词,意为复制。
2. EcmaScript变量
(1)类型
- 基本类型:undefined、null、Boolean、String、Number、symbol、BigInt
- 引用类型:Object-> 细分为Object(对象,{})、Array(数组,[])、Date、Function等
(2)存储和拷贝方式
- 基本类型(简单类型):名(name)和值(value)都存储在栈内存中,所以拷贝时会在栈内存中开辟一个新的内存,两者互相独立。
- 引用类型(复杂类型):名存于栈内存中,真实的值存于堆内存中。其中,栈内存的值存储的是指向堆内存中对应值的引用地址即指针。所以拷贝时复制的是对应值的引用地址,而非堆内存里的真实值,两者的值实际指向的是同一个地址。
// 基本类型
let a = 1;
let b = a;
b = 2;
=> a = ? 2(❌) 1(✅)
// 引用类型
let a = { obj1: 1, obj2: 2 };
let b = a;
b.obj1 = 3;
=> a.obj1 = ? 1(❌) 3(✅)
可知,拷贝是得到一个新的完全一样的name-value对。只是对基本类型而言,值虽然相同但彼此独立存储互不干扰,而对引用类型而言,存储的value虽然在不同的栈内存,但都指向同一地址的数据。 举例:将一个苹果a拷贝得到另一个苹果b,吃了a之后b仍然存在,这是基本类型的拷贝;给a同学取别名b,a和b都指的是这位同学,那么该同学做任何改变都影响a和b,这是引用类型的拷贝。
3. 什么是深拷贝
深拷贝仅针对引用类型。浅拷贝只是增加了一个指针指向已存在的内存地址,仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。深拷贝是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。
4. 如何实现一个深拷贝
(1)扩展运算符...
ES6新增方法,可用于数组或对象。
具体用法为 被赋值 = ...赋值,
const a = [1, 2, 3];
const b = { test: '测试' };
const a1 = [...a];
const b1 = {...b};
a.push(2);
b.test = '是否改变';
console.log(a, a1, b, b1);
// [1, 2, 3, 4], [1, 2, 3], { test: '是否改变' }, { test: '测试' }
(2)Object.assign()
用于将所有可枚举属性的值从一个或多个源对象分配到目标对象,将返回目标对象,仅可用于对象。
具体用法为 Object.assign(target,...sources), target为目标对象,sources为源对象,即Object.assign(目标对象, 源对象1, 源对象2, 源对象3……)。
其中,若属性重复,则会按照目标对象-源对象顺序覆盖。
const a = [1, 2, 3];
const b = { test: '测试' };
const a1 = Object.assign(;
const b1 = {...b};
a.push(2);
b.test = '是否改变';
console.log(a, a1, b, b1);
// [1, 2, 3, 4], [1, 2, 3], { test: '是否改变' }, { test: '测试' }
(3)手写实现:遍历
const deepCopy = (obj) => {
const target = {};
Object.keys(obj).map((prop) => {
target[prop] = obj[prop];
});
return target;
}
4. 如何实现引用类型里面仍然有引用对象的情况?
(1)JSON.stringify & JSON.parse
JSON.stringify 方法将一个JavaScript对象或值转为JSON字符串,对应的JSON.parse用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。
json深拷贝注意事项
惊艳面试官的深拷贝
(2)手写实现
const deepCopy = (source) => {
const target = Array.isArray(source) ? [] : {};
Object.keys(source).map((prop) => {
if(typeof prop === 'object') {
// 这里需要确认一下属性是否可以为null,因为null的typeof结果返回也是object
deepCopy(prop);
} else {
target[prop] = source[prop];
}
});
return target;
}