结构化克隆算法决定跨域传递结果,而非深拷贝;它仅支持字符串、数字、数组、普通对象等安全类型,排除函数、symbol、date(部分转字符串)、regexp(转空对象)等,且丢失属性描述符与对象状态。

深拷贝对象在跨域窗口间传递时,并不会真正“保留深拷贝结果”,而是被结构化克隆算法(Structured Clone Algorithm)再次处理——而该算法本身就有严格的数据类型限制。所谓“深拷贝后传过去”,实际上传的仍是结构化克隆后的副本,原始深拷贝中可能包含的 Date、RegExp、Map、Set、函数、Symbol、getter/setter 等,都会在 postMessage() 过程中被过滤或降级。
理解结构化克隆才是关键
浏览器跨域通信不走序列化字符串路径(除非你手动 JSON 化),而是依赖内置的结构化克隆机制。它和深拷贝有交集但目标不同:
- 结构化克隆只保留可跨域安全传输的值:字符串、数字、布尔、null、数组、普通对象、ArrayBuffer、TypedArray、DataView、Blob、File、ImageBitmap、ImageData、DOM 事件对象(部分)、以及它们的嵌套组合
- 它明确排除:
function、undefined、Symbol、Date(某些环境转为字符串)、RegExp(转为空对象)、Error、Promise、Map/Set(部分浏览器支持,但非全平台)、WeakMap/WeakSet、DOM 节点、window 对象等 - 所有属性描述符(
writable、enumerable、configurable、get/set)一律丢失,冻结/密封状态也不保留
不要依赖深拷贝绕过限制
用 structuredClone() 或手写深拷贝函数对对象预处理,再 postMessage(),并不能突破结构化克隆的边界。因为 postMessage() 会把传入的对象再次按结构化规则克隆一次。
-
structuredClone(obj)本身也受限于同套规则,不能复制函数或 Symbol 键 - 手写深拷贝若生成了
Date实例或RegExp,在postMessage()时仍会被转义或丢弃 - 即使你用
JSON.stringify() + parse做“伪深拷贝”,也会主动剔除undefined、函数、Symbol、Date(变成字符串)、循环引用等——这不是绕开限制,而是主动降级
正确做法:契约先行,数据归一
跨域通信应视为“接口调用”,而非“对象共享”。发送端与接收端需约定清晰的数据契约,只传递纯数据字段,行为逻辑留在本地。
- 发送前提取必要字段,构造扁平、JSON-safe 的 payload,例如:
{ id: 't-101', name: 'upload', progress: 65, startedAt: new Date().toISOString() } - 避免传
new Date()或/d+/g,改传 ISO 字符串或正则源字符串 + 标志位(如{ pattern: '\d+', flags: 'g' }) - 接收端收到后,用本地类或工厂函数重建实例:
const task = Task.fromPayload(msg.data.payload),其中fromPayload方法负责还原 Date、RegExp 等特殊类型 - 对响应式对象(如 Vue 2 data)、冻结对象等,不传原对象,只传变更标识(如
{ op: 'update', path: ['user', 'profile'], value: 'John' })
补充:何时用 JSON,何时用 structuredClone
两者不是替代关系,而是适用场景不同:
- 用
JSON.stringify() + parse:需兼容老浏览器、确认输入绝对无函数/Date/RegExp/undefined/Symbol、且能接受时间变字符串、正则消失等损失 - 用
structuredClone():现代环境(Chrome 98+、Firefox 94+、Safari 15.4+),支持Map、Set、ArrayBuffer、Blob等,但仍不支持函数和 Symbol —— 它比 JSON 更强,但仍未突破跨域通信的底层限制 - 无论选哪种,最终进
postMessage()的那一刻,都还要过一遍结构化克隆关卡











