json.stringify配合json.parse可实现纯数据对象的深拷贝,但会丢失函数、undefined、symbol、date、regexp等不可序列化类型,适用于无行为、可序列化的dto或表单数据。

函数不能作为对象的键名——这是 JavaScript 的基本规则。当你写 { [someFunc]: 'value' },实际执行时,函数会先被隐式转换为字符串 "function () { ... }"(或空字符串),最终键名变成字符串,而非函数本身。因此,“函数作为键名”在 JS 中根本不会存在,也就谈不上“在深拷贝中丢失”。真正需要警惕的,是函数作为属性值时,在深拷贝中被静默丢弃所引发的引用丢失与逻辑断裂。
为什么函数作为属性值会被深拷贝丢弃
JSON 序列化标准不支持函数类型。只要用 JSON.stringify,所有函数属性都会被跳过:
-
{ name: 'Alice', action: () => console.log('run') }→{"name":"Alice"} - 后续
JSON.parse得到的对象里,action属性彻底消失 - 调用
copy.action()会报TypeError: copy.action is not a function
哪些深拷贝方式会触发这种断裂
以下方法在处理含函数的对象时,均会导致行为逻辑不可用:
-
JSON.parse(JSON.stringify(obj)):最常见,也最危险——函数、undefined、Symbol全部消失 -
structuredClone(obj):现代原生方案,但明确不支持函数和undefined,遇到即静默剔除 - 简易手写递归(未做类型分支):若没判断
typeof value === 'function',可能把函数当普通对象递归,结果报错或返回空对象
如何提前识别这类风险
不是等报错才发觉,而是主动探测:
- 检查原始对象是否有
typeof obj[key] === 'function'的属性 - 对关键对象运行
JSON.stringify(obj, (k, v) => typeof v === 'function' ? '[Function]' : v),看是否出现[Function]占位符——说明它本该存在,但标准序列化会抹掉 - 使用
Object.getOwnPropertyNames(obj).filter(k => typeof obj[k] === 'function')列出所有函数属性,评估是否可被舍弃
修复逻辑断裂的实用策略
函数不能“拷贝”,但可以“重建”或“绕过”:
-
分离数据与行为:把函数抽成独立工具函数,对象只存配置项(如
{ type: 'save', payload: {...} }),运行时按 type 分发调用 -
白名单重建:深拷贝后,手动补回必要函数:
copy.handler = original.handler(适用于已知固定接口) -
改用结构化克隆 + 补丁:先
structuredClone,再遍历原始对象,对每个函数属性做赋值覆盖(注意避免污染原型) -
换库兜底:如需完整保留,用
lodash.cloneDeep—— 它虽不拷贝函数体,但至少保留函数引用(非字符串化),适合跨 iframe 或需保持闭包上下文的场景










