前言
深拷贝这个知识点可以说是面试必问,而且在实际项目开发中用的也挺多,常见的深拷贝实现方式有:借助 Lodash 工具库、JSON.parse(JSON.stringify(obj))。用别人的很香,但为了知其然更知其所以然,何不自己实现一个呢。
JSON.parse(JSON.stringify(obj)) 的弊端
JSON.parse(JSON.stringify(obj))
实现深拷贝的过程是利用JSON.stringify()
将对象序列化(转换成一个JSON字符串),再使用JSON.parse()
进行反序列化(还原)。
如果 obj 里有时间对象,则JSON.parse(JSON.stringify(obj))
的结果将只是字符串,而不是时间对象:
const date = new Date();
const newDate = JSON.parse(JSON.stringify(date));
console.log(typeof newDate); // string
如果 obj 里有 RegExp、Error 对象,则序列化的结果将只会得到空对象:
const reg = new RegExp();
const err = new Error();
const newReg = JSON.parse(JSON.stringify(reg));
const newErr = JSON.parse(JSON.stringify(err));
console.log(newReg); // {}
console.log(newErr); // {}
如果 obj 里有函数、undefined,则序列化的结果会将函数、undefined 丢失:
const object = {
fn: () => {},
x: undefined,
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject); // {}
如果 obj 里有 NaN、Infinity、-Infinity,则序列化的结果会得到 null:
const object = {
x: NaN,
y: Infinity,
z: -Infinity,
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject); // {x: null, y: null, z: null}
JSON.stringify()
只能序列化对象的可枚举的自有属性,如果 obj 中有对象是构造函数生成的,则使用JSON.parse(JSON.stringify(obj))
深拷贝后,会丢失对象的 constructor:
function Person(name, age) {
this.name = name;
this.age = age;
}
const dlrb = new Person('迪丽热巴', 18);
const object = {
x: dlrb
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject.x.__proto__.constructor === Person); // fasle
如果 obj 中存在循环引用,会陷入死循环:
const object = {
x: 1,
}
object.object = object;
const newObject = JSON.parse(JSON.stringify(object)); // error
排除以上情况,可以使用JSON.parse(JSON.stringify(obj))
来实现深拷贝。
封装 deepClone 函数
/**
* origin 给定的需要深拷贝的对象
* hashMap 利用WeakMap记录是否已经拷贝过,避免循环引用导致死循环
*/
function deepClone(origin, hashMap = new WeakMap()) {
/**
* 简写小技巧
* null == undefined -> true
* null === undefined -> false
*/
if (origin == null || typeof origin !== 'object') {
return origin;
}
if (origin instanceof Date) {
return new Date(origin);
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
const hashKey = hashMap.get(origin);
/** 如果已经拷贝过,直接返回 */
if (hashKey) {
return hashKey;
}
/** 通过构造器来生成新对象,就无需考虑是 [] 还是 {} */
const target = new origin.constructor();
/** 如果没有拷贝过,就记录下来 */
hashMap.set(origin, target);
for (let k in origin) {
/** 区分原型属性和自有属性 */
if (origin.hasOwnProperty(k)) {
target[k] = deepClone(origin[k], hashMap);
}
}
return target;
}
const obj = {
x: 1,
y: {
z: 2,
}
}
const _obj = deepClone(obj);
_obj.y.z = 3;
console.log(obj); // {x: 1, y: {z: 2}}
console.log(_obj); // {x: 1, y: {z: 3}}
最后
如果文中有错误或者不足之处,欢迎大家在评论区指正。
你的点赞是对我莫大的鼓励!感谢阅读~