JavaScript 中的依赖注入:完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么你的代码需要依赖注入



每个 JavaScript 开发者迟早都会面临“紧耦合”代码的问题。想象一个 UserService 类,它在内部创建了 Database 和 Logger 的实例。当你想要在不使用真实数据库的情况下测试 UserService 时会发生什么?或者想要用不同的实现替换 Logger 呢?最好的情况是——你重写整个类,最坏的情况是——你破坏了整个应用程序。



这个问题的解决方案是 依赖注入 (DI)。这是支撑 控制反转 (IoC) 概念的关键设计原则之一。在本文中,我们将详细解释什么是 DI,如何在不使用框架的情况下在纯 JavaScript (Node.js) 中实现它,以及为什么它对构建可扩展和可测试的应用程序至关重要。



用简单的话说,什么是依赖注入?



想象你正在建造一所房子。你没有把插座永久嵌入墙壁,而是安装了标准插座。今天你插上一盏旧台灯,明天插上一盏智能灯。你没有改变墙壁——你改变了依赖。



在编程中,依赖注入 是一种模式,对象从外部接收其依赖项(其他对象、服务、配置),而不是自己创建它们。我们不再编写:



// ❌ 不好:紧耦合class UserService {    constructor() {        this.db = new Database('mongodb://...');        this.logger = new ConsoleLogger();    }}


我们编写:



// ✅ 好:依赖注入class UserService {    constructor(database, logger) {        this.db = database;        this.logger = logger;    }}


现在 UserService 不知道 Database 和 Logger 是如何创建的。它的唯一任务就是使用它们。这就是 控制反转:对创建依赖项的控制从类转移到了调用代码。



依赖注入的类型



有三种主要方式来注入依赖项:

  • 构造函数注入 —— 最常见且首选的方法。依赖项在创建对象时传递。
  • 属性注入 —— 依赖项在对象创建后设置。适用于可选依赖项。
  • 方法注入 —— 当依赖项仅在某一个操作中需要时,将其传递给特定的方法。


在 Node.js 中编写一个简单的 DI 容器



在大型项目中,手动管理依赖项变得困难。这时 DI 容器 就派上用场了——它是一个知道如何创建和存储应用程序所有依赖项的对象。让我们用纯 JavaScript 编写一个简单但功能完整的容器。



我们的容器将支持两种模式:单例(整个应用程序共享一个实例)和 瞬态(每次请求创建一个新实例)。



// di-container.jsclass DIContainer {    constructor() {        this.registry = new Map();        this.instances = new Map();    }

// 使用工厂函数注册一个依赖项 register(name, factory, { singleton = true } = {}) { this.registry.set(name, { factory, singleton }); if (singleton) { this.instances.delete(name); // 重新注册时重置缓存 } }

// 获取依赖项的实例 resolve(name) { const item = this.registry.get(name); if (!item) { throw new Error(`Dependency "${name}" not found`); }

博客

书籍推荐