Angular中DI、构造函数与字段初始化执行顺序问题
原因说明
你观察到的执行顺序感知是准确的——类字段初始化确实早于constructor中显式编写的代码块执行,但代码不报错的核心原因是:TypeScript构造函数参数属性(带访问修饰符的注入参数)的隐式赋值,优先级高于所有类字段初始化。
你写的Angular服务代码是TypeScript语法糖,编译为原生JavaScript后,实际执行逻辑等价于以下代码:
class MyService { constructor(dependentService) { // 1. 编译阶段自动插入:参数属性赋值,最先执行 this.dependentService = dependentService; // 2. 类字段初始化,按声明顺序执行 this.myField$ = this.init(); // 3. 最后才执行你手动写在constructor大括号内的自定义逻辑 // 你的示例中constructor没有自定义代码,这一步为空 } init() { return this.dependentService.getAnObservable(); } }
完整实例化执行顺序
Angular DI容器实例化MyService时的完整流程如下:
- 第一步:DI容器先解析
MyService的所有依赖,拿到已经实例化好的DependentService对象,作为入参传入MyService的构造函数 - 第二步:执行TS编译时自动插入的参数属性赋值逻辑,把传入的
dependentService挂载到当前实例的this上——这一步是语法糖隐式生成的,不会在你写的TS源码里体现 - 第三步:按照类中字段的声明顺序,依次执行所有类字段的初始化逻辑,也就是你写的
public myField$ = this.init()。这一步执行时this.dependentService已经完成赋值,调用其方法自然不会抛出错误 - 第四步:执行你显式写在constructor
{}包裹的代码块中的自定义逻辑,也就是你观测到的「constructor执行」的部分,这部分确实晚于类字段初始化
容易踩的相关误区
如果类字段依赖的是你在constructor自定义代码块中才赋值的属性,就会抛出空值错误,反例如下:
@Injectable({ providedIn: 'root' }) export class ErrorService { // 抛出错误:this.manualSetProp 为 undefined public testField = this.manualSetProp.getStream(); constructor() { // 这行赋值属于第四步执行的自定义逻辑,晚于上面的字段初始化 this.manualSetProp = { getStream: () => of(null) }; } }
内容的提问来源于stack exchange,提问作者philomath
相关产品推荐
相关产品推荐

