应避免深层继承,优先用组合替代;禁止运行时修改原型;高频访问的原型成员需提前缓存;借助devtools或工具函数定位原型链归属。

深层继承本身不是错误,但会让代码越来越难改、难测、难调试。真正的问题不在“能不能继承”,而在于“改一处,崩一片”——比如动了中间某层的 prototype,下游几十个类全受影响。
优先用组合代替多层继承
把通用能力拆成独立对象,通过实例持有,而不是靠原型链一层层往上爬。
- 比如日期格式化、API 请求封装、状态校验逻辑,都做成可复用的工具类或服务对象
- 子类里直接 this.formatter = new DateFormatter(),而不是 extends DateProcessor extends BaseHandler
- 这样修改 formatter 行为时,不会波及任何其他类;单元测试也只需覆盖单个模块
避免运行时动态修改原型
prototype 是共享的,一旦在运行中改了 Parent.prototype.method,所有已创建和将创建的实例都会受影响,IC 缓存也会失效。
- 构造函数定义完后,不再往 prototype 上增删方法
- 需要动态行为时,用策略模式或配置项控制,而不是 patch 原型
- 若必须扩展,用 Object.assign 或 Object.defineProperty 显式操作,留痕可查
高频访问的原型成员提前缓存
如果某个方法或属性在循环或渲染中被反复读取(如 this.render、this.state),每次都要走一遍原型链,开销会放大。
- 在 constructor 或 init 阶段绑定或引用:this._render = this.render.bind(this)
- 或直接赋值:this._api = this.api —— 尤其当 api 来自父类 prototype 时
- 注意:只对真正高频访问的做缓存,别为“可能慢”而过早优化
用工具看清链长和归属
不要靠猜哪一层定义了某个属性。V8 提供了简单办法定位:
- Chrome DevTools 中选中对象 → 右键 “Reveal in Console” → 展开 __proto__ 逐层看
- 用 Object.getPrototypeOf(obj) 一层层向上查,配合 console.log(obj.constructor.name)
- 写个小工具函数打印完整链:getPrototypeChain(obj).map(x => x.constructor?.name || 'null')











