直接利用__proto__链实现依赖注入容器本质是绕过class机制、用原型链动态挂载服务实例,虽仅几十行且轻量,但因__proto__已被标准弃用而存在兼容性与维护风险。

直接利用 __proto__ 链与 Object.prototype 的隐式继承关系来实现依赖注入容器,本质上是绕过 class 和 constructor 机制,用原型链动态挂载服务实例——它确实极轻量(几十行),但需谨慎使用,因直接操作 __proto__ 已被标准标记为**弃用且非标准**,仅适合学习原理或极端受限环境(如微前端沙箱、遗留系统 patch)。
核心思路:把容器实例的 __proto__ 指向一个“服务原型对象”
不新建 class,也不用 Map 存实例,而是构造一个纯对象作为“服务原型”,将所有注册的服务方法/属性挂在其上;再让容器实例(如 di)的 __proto__ 指向它。这样调用 di.http.get() 时,JS 引擎会沿原型链查找到 http 属性。
- 服务原型对象本身不保存状态,只定义“能力接口”
- 真实服务实例(如 axios 实例)通过闭包或 WeakMap 绑定到该原型方法中,避免污染原型
- 注册时动态改写原型上的 getter,实现懒加载和单例控制
注册服务:用 Object.defineProperty 控制原型上的访问逻辑
不能直接赋值 serviceProto.http = instance,否则所有容器共享同一实例且无法拦截初始化。应定义 getter:
const serviceProto = {};
Object.defineProperty(serviceProto, 'http', {
get() {
if (!this._instances) this._instances = new WeakMap();
let inst = this._instances.get(this);
if (!inst) {
inst = createHttpClient(); // 实际创建逻辑
this._instances.set(this, inst);
}
return inst;
},
enumerable: true
});
-
this在 getter 中指向容器实例(即di),靠WeakMap实现 per-container 单例 - 避免在原型上存实例引用,防止跨实例污染
- 支持异步初始化(getter 内可返回 Promise,调用方 await 即可)
创建容器:手动设置 __proto__ 并屏蔽默认原型干扰
用 Object.create(null) 创建无原型的对象,再显式设置 __proto__:
function createDI() {
const di = Object.create(null);
di.__proto__ = serviceProto; // 关键:绑定服务原型
return di;
}
const di = createDI();
-
Object.create(null)确保干净起点,避免toString等原生方法干扰 - 不推荐用
new Function('return {}')()等黑科技替代,可读性差且有安全风险 - 若需调试,可临时加
di.constructor = { name: 'DI' },但非必需
扩展与限制:为什么它“超轻量”也“超危险”
体积小(
-
__proto__在严格模式下不可写,ES6+ 环境中需用Object.setPrototypeOf()替代(性能略低) - V8 等引擎对动态修改
__proto__做了优化降级,高频操作影响性能 - TypeScript 无法推导此类动态原型链,类型需手动声明或放弃类型安全
- 现代框架(React/Vue)的响应式系统可能无法追踪原型链上的属性变化
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











