关于深拷贝的思考
2021/1/19 Javascript
深拷贝,平常在用的时候,都会用 JSON 的方法 const deepcloneObj = JSON.parse(JSON.stringify(obj));
这个方法也存在很多的
问题,
- 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
- 拷贝 Date 引用类型会变成字符串;
- 无法拷贝不可枚举的属性;
- 无法拷贝对象的原型链;
- 拷贝 RegExp 引用类型会变成空对象;
- 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
- 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj).
所以,虽然在日常的开发过程中,JSON 方法能够满足我们的日常的开发需求,但是还是会有一些问题。
所以要考虑一个高端大气上档次的,来解决这些问题。 需求:
- 基本类型正常拷贝
- 引用类型正常拷贝
- 不可枚举的类型,也要进去
- 可以考虑获取原型链
- 解决成环的问题
解决方法:
- 基本类型正常递归拷贝
- 引用类型,Date、RegExp 类型,返回新实例,数组函数正常返回
- 使用 Object.create 的方法,用来创建一个新对象,并继承传入原对象的原型链
- 利用 WeakMap 类型作为 Hash 表,作为检测循环引用很有帮助,如果存在循环,则引用直接返回 WeakMap 存储的值
const isComplexDataType = (obj) => (typeof obj === 'object' || typeof obj === 'function') && obj !== null;
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date) return new Date(obj); // 日期对象直接返回一个新的日期对象
if (obj.constructor === RegExp) return new RegExp(obj); //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj);
let allDesc = Object.getOwnPropertyDescriptors(obj);
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
//继承原型链
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;
};