直接用 babel + jscodeshift 可自动将原型链代码转为 class 语法,需识别三种 prototype 定义模式,提取构造函数、原型方法及继承逻辑,生成兼容的 class 结构并处理动态修改、instanceof 重写等边界情况。

直接用 Babel + jscodeshift 就能完成,不需要重写代码,关键是把原型链逻辑映射到 class 语法,同时保留原有行为(包括 instanceof、this 指向、继承链)。
识别并提取原型链定义模式
历史代码常见三种写法:函数内赋值 prototype、Object.defineProperty、直接修改 constructor.prototype。jscodeshift 脚本需先统一识别这些模式,重点捕获:
• 构造函数名和参数
• 所有挂载在 prototype 上的方法(含 getter/setter)
• 显式调用的 Object.create、__proto__ 或 Object.setPrototypeOf
• 是否存在多层继承(如 Parent.call(this) + Parent.prototype.xxx)
- 用 jscodeshift 的 find(j.MethodDefinition) 和 find(j.ExpressionStatement).filter(...) 分别抓取方法定义和 prototype 赋值语句
- 对每个构造函数,收集所有 prototype.xxx = function() {...} 形式的节点,并记录是否为箭头函数(需转为普通方法)
- 检测 super 调用位置——若原代码中存在 Parent.apply(this, arguments),应转为 super() 或 super(...arguments)
生成兼容的 class 结构
ES6 class 不允许动态添加方法,所以必须一次性把所有 prototype 方法提升为 class body 内部定义。关键处理点:
- 构造函数体直接迁移,但需将 this.xxx = xxx 初始化逻辑保留在 constructor 中(不能挪到字段声明,除非是简单字面量)
- 所有原型方法转为 class 方法,静态方法保持 static 前缀;getter/setter 用对应语法,不展开为普通函数
- 若原构造函数返回对象(如 return {…}),需保留该逻辑——class 构造函数不能 return 非 undefined 对象,此时降级为保留 function 写法或改用工厂函数
- 继承关系用 extends Parent,并确保父类已存在且可被静态解析(避免循环依赖)
处理边界情况与兼容性补丁
不是所有原型链都能无损转 class,以下情况需人工介入或加运行时兼容层:
- 动态 prototype 修改(如运行时执行 A.prototype.m = fn)——脚本无法静态分析,需标记警告并建议抽离为独立模块或用 Proxy 模拟
- instanceof 依赖非标准构造器(如重写了 constructor 或 Symbol.hasInstance)——生成 class 后需同步补全 static [Symbol.hasInstance] 方法
- 混合使用 class 和 function 构造器——若子类是 class、父类是 function,则需确保父类有 prototype 且 constructor 指向正确,必要时插入 Parent.prototype.constructor = Parent
- IE11 等旧环境需 Babel 编译输出 es2015,且开启 loose: true 以避免 class 继承生成 _inherits 辅助函数带来的 this 绑定问题
集成到构建流程并验证
配置 package.json script 实现一键升级:
- 安装依赖:yarn add -D jscodeshift @babel/core @babel/preset-env
- 编写 codemod 脚本(如 transform-prototype-to-class.js),支持 --dry-run 和 --verbose 参数
- 执行:npx jscodeshift -t transform-prototype-to-class.js src/ --extensions=js --parser=babel
- 升级后立即跑原有测试(尤其检查 new A() instanceof A、A.prototype.method.call(obj)、继承链 instanceof 结果)











