应始终在constructor开头一次性按固定顺序和类型初始化所有实例属性,以确保v8引擎稳定生成隐藏类、优化内联缓存;避免条件赋值、类型不一致及依赖字段初始化器执行复杂逻辑。

始终在构造函数开头一次性声明所有实例属性
不管初始值是否为 null、undefined 或默认值,都应在 constructor 第一行就赋值。避免后续条件分支中动态添加属性。
- ✅ 好写法:
this.name = null; this.age = 0; this.tags = []; - ❌ 避免写法:
if (opts) { this.config = opts; }—— 这会导致隐藏类分裂
保持属性赋值顺序与类型严格一致
同一构造函数创建的所有实例,必须按相同顺序、相同类型设置相同属性名。引擎靠这个建立内联缓存(IC)。
- 顺序要固定:始终先
this.id,再this.name,再this.createdAt - 类型要稳定:比如
this.count一直是 number,不要有时是0,有时是"zero"
不依赖类字段初始化器来替代构造函数逻辑
类字段(如 value = compute();)虽方便,但执行时机在 super() 返回后、constructor 主体前,且可能调用子类重写的方法,带来不可控副作用和重复计算。
- 若需复杂初始化,显式写在 constructor 开头更可控
- 字段初始化器适合简单、无副作用的默认值(如
items = [];),而非逻辑密集型操作
继承场景下注意父子属性挂载顺序
使用 class 时,父类构造函数先执行并挂载其属性;子类 constructor 主体运行前,子类字段初始化器才执行;最后才是子类 constructor 内部语句。
- 这意味着:父类属性一定早于子类属性出现在对象上
- 若子类字段初始化器访问
this.xxx,该属性必须已在父类中定义,否则为undefined











