JavaScript递归深拷贝含Symbol对象的关键是:用Object.getOwnPropertyNames()和Object.getOwnPropertySymbols()合并获取全部自有键,再统一递归处理;需用WeakMap防循环引用,Symbol值直接赋值。

JavaScript 中递归复制包含 Symbol 属性的对象,关键在于:普通 for...in 或 Object.keys() 无法遍历 Symbol 键,必须显式获取并处理 Object.getOwnPropertySymbols() 返回的 symbol 数组,再与字符串键合并后统一递归深拷贝。
获取所有自有属性键(含 Symbol)
要完整复制对象,需同时拿到字符串键和 Symbol 键:
-
Object.keys(obj)只返回可枚举字符串键 -
Object.getOwnPropertyNames(obj)返回所有自有字符串键(包括不可枚举) -
Object.getOwnPropertySymbols(obj)返回所有自有 Symbol 键 - 三者合并(去重)才能覆盖全部自有属性
递归深拷贝逻辑需区分类型
对每个键值对递归处理时,需判断值类型,避免循环引用和不支持类型(如函数、undefined、Date、RegExp 等可根据需求选择是否保留):
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- null 和 基本类型(string/number/boolean/bigint/symbol)直接返回
-
对象或数组:先检测是否已遍历过(用
WeakMap记录源对象 → 拷贝对象映射),防止循环引用;再创建新对象/数组,递归拷贝其属性 -
Symbol 值本身:直接使用(
Symbol('a')和Symbol('a')不相等,但深拷贝中 Symbol 值无需“复制”,原样赋值即可)
完整可运行示例
以下是一个支持 Symbol 属性的轻量递归深拷贝函数(不含 Date/RegExp 等特殊对象处理,聚焦核心逻辑):
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
let clone;
if (Array.isArray(obj)) {
clone = [];
} else if (obj instanceof Object) {
clone = Object.create(Object.getPrototypeOf(obj));
} else {
return obj; // 如 Date、RegExp 等按需扩展
}
seen.set(obj, clone);
// 获取所有自有键:字符串 + Symbol
const keys = [
...Object.getOwnPropertyNames(obj),
...Object.getOwnPropertySymbols(obj)
];
for (const key of keys) {
const value = obj[key];
clone[key] = deepClone(value, seen);
}
return clone;
}
// 测试
const sym = Symbol('id');
const source = {
a: 1,
[sym]: 'symbol-value',
nested: { b: 2 }
};
const copied = deepClone(source);
console.log(copied[sym]); // 'symbol-value'
console.log(copied.nested === source.nested); // false(已深拷贝)
注意事项
实际项目中建议优先使用成熟库(如 Lodash 的 _.cloneDeep),它已内置 Symbol 支持;若需手写,注意:
- 不可枚举属性(如通过
Object.defineProperty设置的enumerable: false)需靠getOwnPropertyNames+getOwnPropertySymbols覆盖 -
WeakMap/WeakSet无法被深拷贝(无安全遍历方式),通常直接跳过或抛错 - 函数、正则、日期等对象是否拷贝取决于场景,简单递归一般跳过或浅拷贝
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










