JavaScript类属性初始化顺序严格遵循规范:静态属性按声明顺序初始化(计算属性名需前置依赖),实例字段按书写顺序在super()后、构造函数前初始化,继承中父类字段先于子类初始化。

JavaScript 类属性的初始化顺序由语言规范严格定义,不是靠手动“保证”,而是按固定规则自动执行。只要写法合规,顺序天然成立;出问题往往是因为误用了计算属性名、跨静态/实例混用依赖,或忽略了继承层级中的执行节奏。
类体内声明顺序决定非静态字段初始化顺序
类中所有实例字段(包括带默认值的属性)按代码从上到下的声明顺序初始化,且总在构造函数体执行之前完成。
- 哪怕字段分散在方法之间,引擎仍按书写位置逐个赋值
- 字段初始化发生在
super()返回之后、子类构造函数体开始之前 - 例如:
class C { a = 1; b = this.a + 1; }中,b肯定能读到已初始化的a
静态成员必须前置声明,否则计算属性名会报错
静态属性按类体中出现顺序初始化,但计算属性名([expr])里的表达式会在该行执行时立即求值——此时后面声明的静态成员还不可访问。
- ❌ 错误:
static VERSION = 'v2'; static [`${Api.VERSION}/users`] = '/api/users';(Api.VERSION尚未初始化) - ✅ 正确:把
VERSION提前,或改用static get ENDPOINTS()延迟到访问时计算 - 更稳妥的做法是把组合逻辑提到模块顶层,用常量+对象字面量替代类内动态拼接
继承链中按“父→子”逐级初始化,每级独立走一遍流程
创建子类实例时,并非一次性收集所有字段再初始化,而是沿着继承链向下推进:
立即学习“Java免费学习笔记(深入)”;
- 先执行父类的静态初始化(一次,仅首次加载)
- 再执行子类的静态初始化(也只一次)
- 接着进入实例化:调用
super()→ 父类字段初始化 → 父类构造函数体 → 子类字段初始化 → 子类构造函数体 - 也就是说,
B extends A时,A的字段一定比B的字段先准备好
静态优先于实例,且只初始化一次
整个生命周期里,静态部分(静态属性、静态块)总在任何实例创建前完成,且全局仅执行一次;而非静态部分每次 new 都重新走一遍。
- 静态内容属于类本身,不随实例变化
- 实例字段和初始化块属于每个对象,彼此隔离
- 不要在静态初始化中引用
this或实例字段——它们此时根本不存在


















