递归深拷贝默认不复制原型链,因原型是隐式链接而非属性;需用Object.getPrototypeOf获取、判断是否为内置原型、再用Object.setPrototypeOf设置,并仅对用户自定义原型递归克隆。

JavaScript 中递归实现深拷贝时,默认不会克隆原型链,因为 Object.getPrototypeOf() 获取的原型本身不是对象的“属性”,而是一种隐式链接。要完整保留原型链,需在创建新对象后显式设置其原型,而不是仅复制自有属性。
为什么要手动处理原型链
普通递归深拷贝(如遍历 Object.keys() 或 Object.getOwnPropertyNames())只操作对象自身的可枚举/不可枚举属性,完全忽略 __proto__ 或 [[Prototype]]。这意味着即使内容全量复制,新对象也会丢失原始构造器、方法继承和 instanceof 行为。
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
递归中保留原型的关键步骤
- 用
Object.getPrototypeOf(obj)获取源对象原型 - 判断原型是否为
Object.prototype或null,避免无限上溯 - 使用
Object.setPrototypeOf(newObj, clonedProto)设置新对象原型 - 对原型本身也做深拷贝(仅当它不是内置原型如
Date.prototype时才递归;否则直接复用) - 注意:不能无条件克隆所有原型——比如
Array.prototype或Function.prototype没有必要也不应被复制,它们属于语言运行时基础结构
支持原型链的简化递归示例
以下代码片段展示核心逻辑(省略循环引用缓存、特殊类型等完整处理,聚焦原型部分):
function deepCloneWithProto(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
// 创建空对象,但保留构造器信息
const Ctor = obj.constructor;
let clone;
if (Ctor === Object || Ctor === Array) {
clone = new Ctor();
} else {
// 尝试用相同构造器创建实例(适用于自定义类)
try {
clone = new Ctor();
} catch {
clone = {};
}
}
cache.set(obj, clone);
// 克隆自有属性(含不可枚举、symbol)
const descriptors = Object.getOwnPropertyDescriptors(obj);
for (const key in descriptors) {
const desc = descriptors[key];
Object.defineProperty(
clone,
key,
{
...desc,
value: deepCloneWithProto(desc.value, cache)
}
);
}
// 克隆原型链(仅对用户自定义原型递归,跳过内置原型)
const proto = Object.getPrototypeOf(obj);
if (proto && proto !== Object.prototype && proto !== Array.prototype) {
Object.setPrototypeOf(clone, deepCloneWithProto(proto, cache));
}
return clone;
}
实际使用中的注意事项
- 函数、
undefined、Symbol等仍需单独判断处理,不能依赖原型链解决 - 内置对象(
Date、RegExp、Map等)通常有自己的克隆逻辑,不应让其原型参与递归,否则会破坏语义 - 箭头函数、绑定函数、生成器函数等没有
prototype属性,也不应被设为新对象原型 - 若源对象原型是
Object.create(null),则其原型为null,需保留该状态

















