方法一:正统做法(扩展性高,推荐)
function test() {
this.a = 1;
this.b = 2;
}
test.prototype.c = 3; // 原型上的属性
const obj = new test();
console.log("原对象", obj);
console.log("克隆后的对象", deepClone(obj));
/** JS 深度克隆
* @param value 需要克隆的值
*/
function deepClone(value) {
// 排除原始类型的情况,函数时也满足此条件
if (typeof value !== "object" || value === null) {
return value;
}
// 克隆结果:1.数组 2.普通对象
const result = Array.isArray(value) ? [] : {};
// 设置克隆结果的原型链为 value 的原型链(即保持原型一致)
Object.setPrototypeOf(result, Object.getPrototypeOf(value));
// 浅层克隆
for (const key in value) {
// 排除原型上的属性
if (value.hasOwnProperty(key)) {
result[key] = deepClone(value[key]); // 针对这个对象的每一个属性值进行克隆,则达到深度克隆效果
}
}
return result;
}
解决环形引用问题
function test() {
this.a = 1;
this.b = 2;
}
test.prototype.c = 3; // 原型上的属性
const obj = new test();
obj.c = obj; // 环形引用 obj.c 等于 obj 本身
console.log("原对象", obj);
console.log("克隆后的对象", deepClone(obj));
/* 建立缓存区,解决环形引用问题 */
let cache = new WeakMap(); // 使用 WeakMap 为了防止内存泄露
/** JS 深度克隆
* @param value 需要克隆的值
*/
function deepClone(value) {
// 排除原始类型的情况,函数时也满足此条件
if (typeof value !== "object" || value === null) return value;
// 解决环形引用问题(即循环引用)
const cached = cache.get(value);
if (cached) return cached;
// 克隆结果:1.数组 2.普通对象
const result = Array.isArray(value) ? [] : {};
// 设置克隆结果的原型链为 value 的原型链(即保持原型一致)
Object.setPrototypeOf(result, Object.getPrototypeOf(value));
// 环形引用时将克隆的值储存到缓存中
cache.set(value, result);
// 浅层克隆
for (const key in value) {
// 排除原型上的属性
if (value.hasOwnProperty(key)) {
result[key] = deepClone(value[key]); // 针对这个对象的每一个属性值进行克隆,则达到深度克隆效果
}
}
return result;
}
方法二:JSON 序列化与反序列化(无扩展性)
const obj = { id: 1, name: "张三", age: 18 };
const newObj = JSON.parse(JSON.stringify(obj));
newObj.name = "艾凯";
newObj.age = 22;
console.log("obj", obj);
console.log("newObj", newObj);
功能上没有问题,但这种做法有一些明显的缺陷。
如果这个对象里面含有 Map 之类的这些玩意,克隆之后这个 mapList 就不再是一个 Map 结构了。
或者是这个对象里面有一些函数之类的,克隆之后这个函数后都没有了。
const obj = { id: 1, name: "张三", age: 18, mapList: new Map(), fun: function () { } };
const newObj = JSON.parse(JSON.stringify(obj));
newObj.name = "艾凯";
newObj.age = 22;
console.log("obj", obj);
console.log("newObj", newObj);
方法三:标签页通信
异步的,比较耗时,没用过