JSON.parse(JSON.stringify())进行深拷贝的缺点

95 阅读1分钟

一、JSON.parse(JSON.stringify())进行深拷贝的缺点

1、函数、undefined 、Symbol 被忽略

const obj = {
   func: () => console.log("Hello"),
   undef: undefined,
   sym: Symbol("foo"),
   name: '张三',
   age: 0,
   sex: null
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy);   // 👉 { name: '张三', age: 0, sex: null }

2、Date 对象被转为字符串

const obj = { 
    date: new Date()
};
console.log(obj.date.getFullYear())    // 👉 2025

const copy = JSON.parse(JSON.stringify(obj));
console.log(obj);                      // 👉 {date: '2025-07-19T01:44:17.445Z'}
console.log(copy.date.getFullYear())   // 👉  Date 对象被转为字符串,方法无法使用,会报错 

3、NaN / Infinity 转为 null

const obj = { 
    num: NaN, inf: Infinity 
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy);  // 👉 { num: null, inf: null }  

4、循环引用不能被拷贝

const obj = { name: "循环引用" };
obj.self = obj; // 自引用
const copy = JSON.parse(JSON.stringify(obj));  // 👉 报错 --  TypeError: Converting circular structure to JSON

5、原型链断裂

function Person (name) { 
    this.name = name; 
}
Person.prototype.greet = function () { console.log(`Hi, ${this.name}!`); };

const alice = new Person("Alice");
alice.greet()   // 👉 "Hi, Alice!"

const copy = JSON.parse(JSON.stringify(alice));
copy.greet(); // 原型方法丢失,会报错: TypeError: copy.greet is not a function 

6、会将RegExp 和 Error 转为空对象

const obj = { 
      regex: /abc/g, 
      err: new Error("test") 
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy);   //  👉 { regex: {}, err: {} } 

7、Map / Set 数据丢失

const obj = { map: new Map([["key", "value"]]) };
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy);   //  👉  { map: {} }

二、解决方法

1、方法一:用递归进行拷贝

function deepClone (source, map = new WeakMap()) {
      // 处理循环引用
      if (map.has(source)) return map.get(source);

      // 处理原始类型和 null/undefined
      if (source === null || typeof source !== "object") {
         return source; // 直接返回函数、Symbol 和原始值
      }

      // 处理特殊类型对象
      if (source instanceof Date) return new Date(source);
      if (source instanceof RegExp) return new RegExp(source);
      if (source instanceof Map) {
         return new Map(Array.from(source.entries()).map(([k,v])=>[deepClone(k),deepClone(v)]))
      }
      
      // 创建新对象并保留原型链
      const clone = Object.create(Object.getPrototypeOf(source))
      map.set(source, clone); // 缓存引用

      // 递归拷贝属性(包括 Symbol 键)
      Reflect.ownKeys(source).forEach(key => {
         clone[key] = deepClone(source[key], map);
      });

      return clone;
}
// 例
const obj = { 
    func: () => console.log("Hello"), 
    undef: undefined, 
    sym: Symbol("foo")
 };
const copy = deepClone(obj); 
copy.a = 3
console.log(copy) // 👉 {undef: undefined, sym: Symbol(foo), a: 3, func: ƒ} console.log(obj) // 👉 {undef: undefined, sym: Symbol(foo), func: ƒ}

2、方法二:structuredClone

① structuredClone 仍无法复制 函数 和 Symbol   ② 原型链不会保留原型链

// 例
const obj = { date: new Date() };

const clonedSink = structuredClone(obj)
console.log(clonedSink.date.getFullYear())   // 👉 2025