javascript递归复制自定义类实例需手动实现克隆策略,优先调用obj.clone()或类静态from()方法,避免原型丢失;对数组、内置类型单独处理,并用weakmap防止循环引用。

JavaScript 中递归复制包含自定义类实例的对象,关键在于区分普通对象、数组、基本类型和自定义类实例,并为后者提供可控的克隆逻辑。默认的深拷贝方法(如 JSON.parse(JSON.stringify()) 或结构化克隆)会丢失类原型和方法,因此需手动实现递归克隆策略。
识别并保留自定义类实例的类型信息
单纯用 typeof 或 Object.prototype.toString 无法准确还原构造函数;应优先使用 obj.constructor 获取原始类,并检查其是否为可安全调用的函数(排除内置类型如 Date、RegExp 等,除非你明确需要支持它们)。
- 用
obj instanceof Object && obj.constructor !== Object初步判断是否为自定义类实例 - 避免对
Array、Date、Map、Set等内置类型直接 new 构造器,应单独处理 - 若类提供静态
from()或实例clone()方法,优先调用——这是最安全、最语义化的克隆方式
为自定义类实现可克隆接口(推荐做法)
与其在通用克隆函数里硬编码每个类的逻辑,不如让类自身声明如何被复制。例如:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
class Person {
constructor(name, pet) {
this.name = name;
this.pet = pet; // 可能是另一个自定义类实例
}
// 显式定义克隆行为
clone() {
return new Person(this.name, this.pet?.clone?.() ?? this.pet);
}
}
这样递归克隆函数只需检测并调用 obj.clone(),大幅降低耦合度和出错风险。
手写递归克隆函数(支持自定义类实例)
一个轻量但健壮的实现示例(不依赖外部库):
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
// 优先尝试对象自带的 clone 方法
if (typeof obj.clone === 'function') {
const cloned = obj.clone();
seen.set(obj, cloned);
return cloned;
}
// 处理数组
if (Array.isArray(obj)) {
const arr = [];
seen.set(obj, arr);
for (let i = 0; i
- 使用
WeakMap防止循环引用导致栈溢出 - 用
Object.create(Constructor.prototype)正确继承原型链 - 用
Object.hasOwn(而非hasOwnProperty)安全遍历自有属性 - 未定义
clone()的自定义类,仅克隆自有属性,不调用构造函数——避免副作用
注意事项与边界情况
有些场景无法“真正”克隆,需提前约定或报错:
-
带闭包或私有字段的类(如
#id):JS 私有字段不可反射,clone()必须显式处理 -
含函数、DOM 节点、
Promise、EventTarget等不可序列化值:默认跳过或抛错,不应静默忽略 -
Symbol 键属性:需额外遍历
Object.getOwnPropertySymbols(obj)并递归克隆 -
不可扩展/冻结对象:克隆后可用
Object.preventExtensions()或Object.freeze()还原状态(如果需要)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










