方法一:正统做法(扩展性高,推荐)
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);
方法三:标签页通信
异步的,比较耗时,没用过