WebWorker传递对象时如何保留全层级嵌套类的原型方法?
你现有方案失效的核心原因是flatted解析时会维护所有引用的映射关系,你在reviver中返回新创建的类实例会破坏原有引用关系,仅首次被处理的引用会被替换为实例,其余嵌套、循环引用仍指向解析生成的原始普通对象,因此出现多层嵌套后原型丢失的问题。
解决方案
第一步:序列化阶段统一打标识
给所有自定义模型类实现toJSON方法,自动追加className标识,避免手动遍历的遗漏:
// 可以先定义一个基类减少重复代码 class BaseModel { toJSON() { return { ...this, className: this.constructor.name } } } // 所有自定义类继承该基类即可 class ProgramModel extends BaseModel { // 原有业务逻辑 } class DataProvider extends BaseModel { // 原有业务逻辑 }
Worker侧发送数据时直接用flatted序列化即可:
// worker 内发送数据逻辑 import { stringify } from 'flatted'; postMessage(stringify(yourProgramModelInstance));
第二步:反序列化阶段直接修改原型
反序列化时不要创建新对象,直接修改解析生成的原始对象的原型,保证引用关系不被破坏:
import { parse } from 'flatted'; // 维护类名到类构造函数的映射 const models = { ProgramModel, DataProvider, // 补充所有需要恢复的自定义类 } as Record<string, new () => any>; worker.onmessage = ({ data }) => { this.programParser = parse(data, (key, val) => { if (val !== null && typeof val === 'object' && val.className && val.className in models) { // 直接修改原对象原型,不创建新实例 Object.setPrototypeOf(val, models[val.className].prototype); // 特殊类的初始化逻辑单独处理 if (val.className === 'DataProvider') { // 若有构造函数专属逻辑可以抽成init方法调用 val.init?.(val.providerData); } } // 始终返回原对象,保证引用链完整 return val; }); }
方案优势
- 完全保留flatted的循环引用处理能力,不会破坏原有引用关系,无论嵌套多少层、有没有循环引用都可以正确恢复原型
- 性能优于新建实例+赋值的方案,不需要重复拷贝对象属性
- 不需要手动处理嵌套层级,所有带className标识的对象都会被自动识别恢复
注意事项
- 类的方法需要定义在原型上(常规class语法定义的方法默认就在原型上),不要使用
this.method = () => {}这类实例方法写法,否则改原型无法继承到方法 - 如果类构造函数有必须执行的逻辑,抽成公共init方法在reviver中调用即可,不要在构造函数中写副作用逻辑
内容的提问来源于stack exchange,提问作者Maxime B
相关产品推荐
相关产品推荐

