关键是在构造函数中注入依赖并赋值给实例属性,而非修改原型;Vue中用provide/inject实现动态传递,Spring原型Bean则通过构造器注入服务与运行时参数,避免字段注入和原型污染。

在原型继承中注入依赖项,关键不是“往原型上塞东西”,而是让实例在创建时就能拿到所需依赖——既保持继承结构清晰,又避免全局耦合或硬编码。
用构造函数接收依赖,而非从原型读取
原型链本身不负责管理依赖,它只负责方法和属性的共享。真正承载依赖的是实例对象。所以应把依赖作为参数传给构造函数,并保存为实例属性:
- 父类构造函数声明依赖参数(如 api、logger),并赋值给 this
- 子类构造函数调用 Parent.call(this, ...),确保依赖被正确初始化
- 所有业务方法通过 this.api 或 this.logger 使用,不查原型
配合 provide/inject 实现运行时注入(Vue 场景)
在 Vue 组件继承中,父组件无法预知子组件的具体 API,这时可借助 provide/inject 动态传递依赖:
- 子组件在 setup 或 data 中定义具体实现(如 userApi)
- 子组件通过 provide 将其暴露出去
- 父组件用 inject 拿到该依赖,用于分页、加载等通用逻辑
- 这样父子解耦,父类不关心 API 怎么来,只管怎么用
Spring 风格:原型 Bean + 构造器注入(Java 场景)
对于 Spring 管理的原型 Bean,同样适用构造器注入原则:
- 把服务依赖(如 AdapterService)和运行时数据(如 Options)都作为构造参数
- Spring 容器在调用 getBean(Adapter.class, options) 时自动组装
- 单元测试时可直接 new Adapter(mockService, testOptions),完全脱离容器
- 字段注入(@Autowired private xxx)在此场景下不可测、不可控,应避免
不推荐的做法
以下方式看似“注入”,实则破坏可维护性:
- 在子类原型上直接挂依赖对象(Child.prototype.api = ...)→ 所有实例共享,状态污染
- 父类从全局变量或单例里取依赖 → 难以替换、无法测试
- 用 Object.assign 修改已有实例的属性 → 隐藏初始化逻辑,违反封装
- 靠 instanceof 或 isPrototypeOf 反向查找祖先依赖 → 耦合过重,违背依赖倒置

















