面试题答案
一键面试装饰器实现依赖注入机制
- 装饰器基础:在TypeScript中,装饰器是一种特殊的声明,它可以附加到类声明、方法、访问器、属性或参数上。在Angular中,装饰器用于为类添加元数据。例如,
@Component
、@Injectable
等装饰器。 - @Injectable装饰器:
- 当一个类被
@Injectable
装饰时,它表示这个类可以被依赖注入系统创建和管理。这主要是为了让Angular知道该类可能有依赖项需要注入。 - 例如:
import { Injectable } from '@angular/core'; @Injectable() class Logger { log(message: string) { console.log(message); } }
- 当一个类被
- @Component装饰器与依赖注入:
- 一个
@Component
装饰的组件类,通过构造函数来声明它所依赖的服务。Angular的依赖注入系统会在创建组件实例时,查找并注入这些依赖。 - 例如:
import { Component } from '@angular/core'; import { Logger } from './logger.service'; @Component({ selector: 'app - my - component', templateUrl: './my - component.html' }) class MyComponent { constructor(private logger: Logger) {} ngOnInit() { this.logger.log('Component initialized'); } }
- 这里
MyComponent
依赖Logger
服务,依赖注入系统会查找Logger
的提供者,并创建一个实例注入到MyComponent
的构造函数中。
- 一个
- 提供者(Providers):
- 在Angular中,依赖的注册是通过提供者(providers)来完成的。可以在模块(
@NgModule
的providers
数组)、组件(@Component
的providers
数组)等地方定义提供者。 - 例如,在模块中注册
Logger
服务:
import { NgModule } from '@angular/core'; import { Logger } from './logger.service'; @NgModule({ providers: [Logger] }) class AppModule {}
- 当依赖注入系统查找
Logger
服务时,会根据这些提供者的配置来创建或获取实例。
- 在Angular中,依赖的注册是通过提供者(providers)来完成的。可以在模块(
循环依赖问题及解决
- 循环依赖问题描述:
- 当两个或多个类相互依赖,形成一个循环时,就会出现循环依赖问题。例如,类A依赖类B,而类B又依赖类A。
- 例如:
import { Injectable } from '@angular/core'; @Injectable() class A { constructor(private b: B) {} } @Injectable() class B { constructor(private a: A) {} }
- 在这种情况下,Angular的依赖注入系统在创建
A
的实例时,需要先创建B
的实例,而创建B
的实例又需要先创建A
的实例,导致无限循环。
- 通过装饰器相关知识解决:
- 延迟注入(Lazy Injection):
- 使用
@Inject
装饰器手动注入依赖,而不是在构造函数中直接声明。@Inject
装饰器可以用于方法参数等。例如:
import { Injectable, Inject } from '@angular/core'; @Injectable() class A { constructor(@Inject('B') private bFactory: () => B) {} useB() { const b = this.bFactory(); b.doSomething(); } } @Injectable() class B { constructor(private a: A) {} doSomething() { console.log('B is doing something'); } }
- 这里通过将
B
的注入包装成一个工厂函数(() => B
),使得在实际使用B
时才创建B
的实例,避免了循环依赖。在模块中注册时,需要使用useFactory
来提供B
:
import { NgModule } from '@angular/core'; import { A, B } from './services'; @NgModule({ providers: [ A, { provide: 'B', useFactory: () => new B(new A(() => new B())) } ] }) class AppModule {}
- 使用
- 使用
@Optional
装饰器:- 如果某个依赖不是必须的,可以使用
@Optional
装饰器。例如:
import { Injectable, Optional } from '@angular/core'; @Injectable() class A { constructor(@Optional() private b: B) {} } @Injectable() class B { constructor(private a: A) {} }
- 这样在创建
A
实例时,如果B
因为循环依赖无法创建,A
的b
属性会被设置为null
(或undefined
),从而避免了循环依赖导致的错误。但这种方式适用于依赖可以为null
的场景。
- 如果某个依赖不是必须的,可以使用
- 延迟注入(Lazy Injection):