2022-08-09 14:27:15
实现一个可靠的 JavaScript 深拷贝函数需递归复制对象属性,并处理循环引用、特殊对象类型、不可枚举属性等边界情况。以下是具体实现方法与关键边界情况分析:
核心实现逻辑基础类型与函数处理
null、undefined、字符串、数字、布尔值等直接返回。
函数通常直接返回原引用(无法深度复制函数体和作用域)。
特殊对象类型处理
Date:通过 new Date(obj) 复制时间戳。
RegExp:使用 new RegExp(obj.source, obj.flags) 保留正则表敏唤仔达式和修饰符。
Error:链仿需复制 name、message 和 stack 等属性(示例中未覆盖,需补充)。
Map/Set:递归复制键值对或元素,避免直接引用原对象。
循环引用处理
使用 WeakMap 记录已访问对象,递归时检查是否存在缓存,避免栈溢出。
属性遍历与过滤
结合 Object.getOwnPropertyNames() 和 Object.getOwnPropertySymbols() 获取所有键(包括不可枚举属性和 Symbol 键)。
通过 Object.prototype.hasOwnProperty.call(obj, key) 过滤原型链上的属性。
循环引用
问题:对象桥汪属性间接或直接引用自身,递归会导致无限循环。
解决:使用 WeakMap 缓存已复制对象,递归前检查是否存在缓存。
特殊对象类型
问题:Date、RegExp、Map、Set 等对象无法通过遍历属性复制。
解决:针对每种类型单独处理,例如 Date 用 new Date(obj),Map 递归复制键值对。
不可枚举属性与 Symbol 键
问题:for...in 或 Object.keys() 无法获取不可枚举属性和 Symbol 键。
解决:结合 Object.getOwnPropertyNames() 和 Object.getOwnPropertySymbols()。
原型链属性
问题:深拷贝通常只需复制实例属性,避免污染原型链。
解决:通过 hasOwnProperty 过滤原型属性。
null 和基础类型
问题:typeof null === 'object',需单独判断。
解决:直接返回 null 或基础类型(如字符串、数字)。
函数处理
问题:函数无法深度复制(作用域和闭包无法还原)。
解决:直接返回原函数引用(或根据需求抛出警告)。
性能限制
问题:递归深度过大可能导致栈溢出或性能下降。
解决:使用 WeakMap 优化循环引用,避免重复复制;对超大对象考虑迭代替代递归。
支持循环引用、特殊对象(如 Date、RegExp)、不可枚举属性。
可复制 Map、Set 等集合类型。
递归深度大时性能较差。
无法复制函数、undefined、Symbol、BigInt 等类型。
实现深拷贝的核心是递归复制属性并处理引用关系,关键细节包括:
根据实际场景选择方案:若需兼容性且无特殊类型,可用 JSON 方法;若需高可靠性,推荐自定义 deepClone 函数。