JavaScript深拷贝默认不处理函数,需手动干预:用typeof判断函数并选择忽略、浅引用、包装或字符串化重建策略,structuredClone不支持函数,可预处理剥离后挂回。

JavaScript 深拷贝默认不处理函数,因为函数是引用类型且不可序列化(JSON.stringify 会丢弃函数)。若需在深拷贝中保留、替换或包装函数属性,必须手动干预序列化/反序列化逻辑——核心在于拦截函数属性并按需定制行为。
识别并标记函数属性
在遍历对象过程中,需准确判断某属性是否为函数(包括普通函数、箭头函数、class 方法等),同时避免误判正则、日期等其他对象。
- 用
typeof value === 'function'判断最稳妥(比value.constructor === Function更兼容) - 注意:getter/setter、bind 后的函数、异步函数仍满足该条件
- 若需区分方法类型(如只处理实例方法而非原型方法),需结合
Object.prototype.hasOwnProperty.call(obj, key)
选择函数处理策略并注入拷贝逻辑
根据业务目标选择一种策略,并在递归拷贝时统一应用:
- 忽略函数:直接跳过,不复制(适合纯数据场景)
- 浅引用保留:拷贝后仍指向原函数(适合无状态工具函数)
- 包装代理:用新函数包裹原函数,便于日志、权限校验或上下文绑定
-
字符串化+动态重建:对简单函数用
func.toString()保存,反序列化时eval或Function构造(仅限可信环境,有安全风险)
在自定义深拷贝函数中嵌入处理逻辑
以递归实现为例,在拷贝每个属性前检查类型:
立即学习“Java免费学习笔记(深入)”;
function deepClone(obj, options = {}) {
if (obj === null || typeof obj !== 'object') return obj;
if (typeof obj === 'function') {
if (options.keepFn) return obj; // 浅引用
if (options.wrapFn) return function(...args) {
console.log('called', obj.name);
return obj.apply(this, args);
};
return undefined; // 默认丢弃
}
const clone = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (Object.hasOwn(obj, key)) {
clone[key] = deepClone(obj[key], options);
}
}
return clone;
}
调用:deepClone({ a: 1, fn: () => 'ok' }, { wrapFn: true }) → { a: 1, fn: [Proxy Function] }
使用结构化克隆(现代方案)的注意事项
structuredClone() 本身不支持函数(抛出 DataCloneError),但可通过 Transferable + MessageChannel 扩展(仅限 worker 环境),或配合 CustomSerializer(Chrome 125+ 实验性 API)实现函数序列化。现阶段更推荐在 structuredClone 前预处理:剥离函数到单独 map 中,拷贝后再挂回。
不复杂但容易忽略——关键不是“能不能拷”,而是“想让它变成什么”。


















