访问器属性必然丢失,因结构化克隆算法不支持函数类特性;应提取纯数据并显式携带元信息,接收端用本地类或工厂函数重建对象并激活行为。

跨域窗口间传递带有访问器(getter/setter)的对象时,接收端拿到的只是“数据快照”,所有访问器逻辑都会消失——这不是 bug,而是浏览器结构化克隆机制的明确限制。关键不在“怎么传过去”,而在于“怎么在接收端重新激活行为”。
为什么访问器属性一定会丢失
通过 postMessage() 传递对象时,浏览器底层使用结构化克隆算法(Structured Clone Algorithm),它只保留可序列化的原始值:字符串、数字、普通对象、数组、Date(部分环境)、Map/Set(现代支持)等。但以下内容一律被剥离:
- 所有
get/set访问器(它们本质是函数) - 不可枚举属性(
enumerable: false) - Symbol 键、
undefined、function、Promise、Error - 对象的冻结/密封状态(
Object.freeze()无效)
提取纯数据 + 显式携带元信息
发送前不传原始对象,而是构造一个轻量、可预测的数据载荷,把访问器背后的“意图”显性表达出来:
- 例如原对象有
get fullName() { return this.firstName + ' ' + this.lastName; },就直接传{ firstName: 'Alice', lastName: 'Smith' } - 若访问器依赖计算逻辑(如格式化时间),传原始值 + 标识符:
{ createdAt: '2026-05-20T09:30:00Z', timeFormat: 'relative' } - 避免用
JSON.stringify(obj)直接序列化——它会跳过访问器且无法还原;优先用structuredClone(obj)(仅限现代浏览器),但它仍不保留访问器
接收端重建带行为的对象
收到消息后,不要试图“恢复”原对象,而是用本地已有的类或工厂函数,基于传入数据新建实例:
- 发送端:
targetWindow.postMessage({ type: 'USER_UPDATE', data: { id: 101, firstName: 'Bob', lastName: 'Lee' } }, origin) - 接收端监听:
if (msg.data.type === 'USER_UPDATE') { const user = new User(msg.data.data); user.initComputed(); /* 激活 fullName 等访问器 */ } - 确保
User类定义在接收窗口中,且访问器逻辑与发送端语义一致(不共享代码,但共享契约)
补充建议:用事件代替状态同步
如果目标是响应式更新(比如 Vue/React 场景),更健壮的做法是传递“动作”而非“状态”:
- 发:
{ type: 'UPDATE_USER_NAME', userId: 101, value: 'Charlie' } - 收:触发本地 store 的 mutation 或 dispatch action,由本地响应式系统驱动视图更新
- 这样完全绕开对象序列化问题,也更符合跨域通信的松耦合原则

















