面试题答案
一键面试1. TypeScript装饰器在NestJS服务层依赖注入方面的应用
- 定义服务类:
这里import { Injectable } from '@nestjs/common'; @Injectable() export class UserService { constructor() {} getUser() { return 'User data'; } }
@Injectable()
是NestJS提供的装饰器,它将一个普通的TypeScript类标记为一个可注入的服务。这个装饰器告诉NestJS这个类可以被注入到其他组件中。 - 在其他组件中注入服务:
在import { Controller, Get } from '@nestjs/common'; import { UserService } from './user.service'; @Controller('users') export class UserController { constructor(private readonly userService: UserService) {} @Get() getUsers() { return this.userService.getUser(); } }
UserController
的构造函数中,通过依赖注入的方式将UserService
注入进来。NestJS会自动实例化UserService
并传递给UserController
的构造函数。这是因为UserService
被标记为@Injectable()
,NestJS的依赖注入系统能够识别并处理它。
2. 相比传统方式的显著优势
- 代码简洁性:
- 传统方式:在传统的JavaScript或TypeScript开发中,实现依赖注入通常需要手动管理对象的创建和传递。例如,在一个模块中需要使用另一个模块的功能时,可能需要在每个使用的地方手动实例化该模块。例如:
const UserService = require('./user.service'); function someFunction() { const userService = new UserService(); return userService.getUser(); }
- 装饰器方式:使用装饰器如
@Injectable()
和构造函数注入,代码变得更加简洁明了。NestJS的依赖注入系统会自动管理服务的实例化和传递,开发人员只需要关注业务逻辑。例如上述UserController
中,直接在构造函数中声明依赖,无需手动实例化。
- 可维护性:
- 传统方式:当服务的依赖关系发生变化时,例如
UserService
依赖于另一个服务DatabaseService
,在传统方式下,需要在所有使用UserService
的地方更新代码来传递新的依赖。 - 装饰器方式:在NestJS中,只需要在
UserService
的构造函数中声明新的依赖,NestJS的依赖注入系统会自动处理传递。例如:
这样,对import { Injectable } from '@nestjs/common'; import { DatabaseService } from './database.service'; @Injectable() export class UserService { constructor(private readonly databaseService: DatabaseService) {} getUser() { return this.databaseService.fetchUser(); } }
UserService
依赖的修改不会影响到使用它的其他组件,使得代码的维护更加容易。 - 传统方式:当服务的依赖关系发生变化时,例如
- 模块化和可测试性:
- 传统方式:在传统开发中,由于对象的创建和依赖管理分散在代码各处,模块之间的边界不够清晰,对单个模块进行单元测试时,很难隔离其依赖。例如,测试
UserService
时,可能需要手动模拟它的所有依赖,而且由于创建依赖的代码分散,模拟过程可能比较复杂。 - 装饰器方式:NestJS的依赖注入基于装饰器,使得模块之间的依赖关系更加明确。在测试时,可以很方便地使用NestJS提供的测试工具来模拟依赖。例如,可以使用
Test.createTestingModule
来创建一个测试模块,只注入模拟的服务,从而方便地测试UserService
的功能,而不受其他真实依赖的影响。
import { Test, TestingModule } from '@nestjs/testing'; import { UserService } from './user.service'; import { DatabaseService } from './database.service'; describe('UserService', () => { let userService: UserService; let databaseService: DatabaseService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UserService, { provide: DatabaseService, useValue: { fetchUser: jest.fn(() => 'Mocked user data') } } ] }).compile(); userService = module.get<UserService>(UserService); databaseService = module.get<DatabaseService>(DatabaseService); }); it('should return user data', () => { expect(userService.getUser()).toBe('Mocked user data'); }); });
- 传统方式:在传统开发中,由于对象的创建和依赖管理分散在代码各处,模块之间的边界不够清晰,对单个模块进行单元测试时,很难隔离其依赖。例如,测试