深拷贝需保留函数引用且递归处理Symbol属性:函数直接返回原引用,Symbol键用Object.getOwnPropertySymbols获取并深拷贝其值。

JavaScript 中深拷贝函数和 Symbol 属性,核心在于不丢、不误、不崩:函数不能真正复制逻辑,但必须保留引用;Symbol 作为合法的属性键,必须被识别并递归拷贝其值。
函数在深拷贝中怎么处理
函数是不可序列化的对象,JSON.stringify() 会直接忽略它,structuredClone() 在多数环境(包括当前主流浏览器和 Node.js ≥18.15)也不支持函数。所以“支持函数”的深拷贝,实际含义是:
- 不跳过函数属性,避免变成
undefined - 不尝试反编译或
eval,那会破坏闭包、丢失this绑定、引发安全风险 - 统一按原样返回函数引用(即浅拷贝该函数)
具体操作:
- 用
Object.prototype.toString.call(fn) === '[object Function]'精准识别 - 所有函数类型(普通函数、箭头函数、class 方法、
bind()后函数)都直接返回原值 - 不做任何构造、包装或代理——除非业务明确需要沙箱隔离(此时应换用 Web Worker 或 Proxy 拦截,而非深拷贝)
Symbol 属性怎么正确拷贝
Symbol 是基本类型,但作为对象属性键时不可枚举,for...in 和 Object.keys() 都拿不到。因此手写深拷贝必须显式提取:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
立即学习“Java免费学习笔记(深入)”;
- 使用
Object.getOwnPropertySymbols(obj)获取所有 Symbol 键 - 遍历这些 Symbol 键,对每个
obj[sym]递归调用深拷贝函数 - 将结果赋给新对象的对应 Symbol 键:
cloneObj[sym] = deepClone(obj[sym], hash)
注意:
-
Object.getOwnPropertyDescriptors()可同时捕获 Symbol 属性的描述符(如writable、enumerable),若需完整保留属性特征(比如不可写、不可配置),应结合Object.defineProperties()构建目标对象 - 单纯用
{ ...obj }或Object.assign()无法拷贝 Symbol 属性
实际代码关键片段示意
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
// 判断类型
const typeTag = Object.prototype.toString.call(obj);
let clone;
if (typeTag === '[object Function]') {
return obj; // ✅ 原样保留函数引用
}
if (typeTag === '[object Array]') {
clone = [];
} else if (typeTag === '[object Object]') {
clone = {};
} else if (typeTag === '[object Date]') {
clone = new Date(obj);
} else if (typeTag === '[object RegExp]') {
clone = new RegExp(obj);
} else if (typeTag === '[object Map]') {
clone = new Map();
obj.forEach((v, k) => clone.set(deepClone(k, hash), deepClone(v, hash)));
} else if (typeTag === '[object Set]') {
clone = new Set([...obj].map(v => deepClone(v, hash)));
} else {
return obj; // 其他内置类型(如 ArrayBuffer)按需扩展
}
hash.set(obj, clone);
// 拷贝自有可枚举属性(字符串键)
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clone[key] = deepClone(obj[key], hash);
}
}
// ✅ 拷贝 Symbol 键属性
const symbolKeys = Object.getOwnPropertySymbols(obj);
for (const sym of symbolKeys) {
clone[sym] = deepClone(obj[sym], hash);
}
return clone;
}补充说明:有没有更简单的方案?
- 浏览器端可用
structuredClone(),但它不支持函数和 Symbol(截至 2026 年 10 月) - Lodash 的
_.cloneDeep()支持函数(保留引用)和 Symbol,但不保留属性描述符(如writable: false) - 若项目允许,推荐封装一个轻量版手写实现——控制力强、无依赖、可按需扩展
不复杂但容易忽略。

















