前提
在理解深拷贝与浅拷贝之前需要理解数据类型的存储方式。
数据类型
- 基本数据类型
- String:字符串
- Number:数字
- Boolean:布尔值
- Null:空值
- Undefined:未定义,即未赋值
- Symbol:唯一值
- Bigint:任意大的整数
- 引用数据类型
- Object:对象,包含多个键值对,以{}包裹
- Array:数组,存储有序的元素集合,以[]包裹
- Function:函数,是一种可调用的对象
- RegExp:正则表达式,是一种用于匹配特殊字符串模式的特殊对象
- Date:日期对象
区别
- 基本数据类型:存储在栈内存中,占用内容较小,可以直接操作值
- 引用数据类型:存储在堆内存中,变量保存的是一个指针,指针指向堆内存中的实际值
// 例子,基本数据类型,以字符串为例
const baseType = '字符串'
const copyBaseType = baseType
// 由于基本数据类型存储在栈内存之中,所以在内存中会存储变量`baseType`。
// 赋值`baseType`给`copyBaseType`的时候同样会开辟控件存储这个变量。
// 尽管它们保存的值一样,但却是由不同空间保存。
// 例子,引用数据类型,以对象为例
const objectType = {
a:'1',
b:'2'
}
const copyObjectType = objectType
objectType.a = '2'
console.log('objectType',objectType)
console.log('copyObjectType',copyObjectType)
// {a:'2',b:'2'}
// {a:'2',b:'2'}
// 这里可以看到虽然在前面就对`copyObjectType`进行赋值,但是后续对`objectType`中的`a`属性修改
// 依然会影响到`copyObjectType`。
// 这就是堆内存中的内容共享。这两个变量实际存储的就是一个指针,因为指针没有变化,所以如果指针指向的内容发生变化,他们都会变化。
由上面两个例子可以看出基本数据类型和引用数据类型的拷贝之间的区别。 对于引用数据类型的拷贝,可以举一个例子来具象化理解:
小红和小明是好朋友,有一天小红得到了一个画板,她邀请小明和她一起使用这个画板,所以如果有一个人改变了画板的内容,他们都会发现画板的变化。这就是对引用类型的浅拷贝。 而基本数据类型的拷贝就是小明买了和小红一模一样的画板,各用各的。
浅拷贝与深拷贝
在理解上面的内容后相信现在已经知道了浅拷贝的定义。
浅拷贝 只复制对象的第一层属性,引用类型只复制引用地址,新旧对象共享同一内存,修改引用类型的内容会相互影响。
实现浅拷贝的方法:
- 直接赋值
- Object.assign({},obj)
- 使用展开运算符:{...obj}
深拷贝 与之对应的,深拷贝就解决了浅拷贝的问题,能够复制一模一样的对象,并且拥有自己的对象指针,不会被之前的对象操作影响。
实现深拷贝的方法
- JSON.parse(JSON.stringify(obj)) -- 纯数据对象(只有基本类型和嵌套对象/数组)
- 使用第三方库 lodash 中的cloneDeep方法
- 如果时间充裕可以自己手写。
/**
* Deep clones an object or array.
* @param obj The object or array to clone.
* @returns A deep clone of the input object or array.
*/
function deepClone(target, cache = new WeakMap()) {
// 基本类型 & 函数
if (target === null || typeof target !== 'object') {
return target;
}
// 处理循环引用
if (cache.has(target)) {
return cache.get(target);
}
// Date
if (target instanceof Date) {
return new Date(target);
}
// RegExp
if (target instanceof RegExp) {
return new RegExp(target.source, target.flags);
}
// Map
if (target instanceof Map) {
const result = new Map();
cache.set(target, result);
target.forEach((value, key) => {
result.set(deepClone(key, cache), deepClone(value, cache));
});
return result;
}
// Set
if (target instanceof Set) {
const result = new Set();
cache.set(target, result);
target.forEach(value => {
result.add(deepClone(value, cache));
});
return result;
}
// Array / Object(保留原型)
const result = Array.isArray(target)
? []
: Object.create(Object.getPrototypeOf(target));
cache.set(target, result);
// 包含 symbol key
const keys = Reflect.ownKeys(target);
for (const key of keys) {
result[key] = deepClone(target[key], cache);
}
return result;
}
|注:此文章仅代表笔者观点。笔者能力有限,文章中若出现错误还请见谅。