先记住这个答案
在 ES6 派生类(extends 的类)中,构造器必须先调用 super() 才能使用 this。如果显式写了构造器却不调用 super(),那么访问 this 或返回时都会抛 ReferenceError。基类没有父类,所以构造器不需要调用 super()。super() 会执行父类构造器并返回 this,使当前对象完成初始化,随后再初始化本类字段。
- 派生类必须先调用 super() 再使用 this
- 类字段在 super() 返回后依次初始化
- 基类构造器没有 super() 可调用
super() 与 this 绑定的底层顺序
在派生类中,new 执行时先运行构造器中 super() 之前的代码,此时 this 尚未创建,任何对 this 的读取或赋值都会触发 ReferenceError。随后执行 super(),它调用父类构造器并创建实例对象,返回后 this 才可被访问。之后才初始化本类字段,再执行构造函数体剩余部分。
字段的初始化器(如 field = 1)会在 super() 返回后按声明顺序执行,因此若在 super() 之前尝试读取字段的值,必然得到 undefined(因为字段不存在),甚至私有字段会直接抛 TypeError。这保证了实例的初始化不依赖尚未就绪的子类内容。
class Base {
constructor() {
console.log('Base constructor, this has:', JSON.stringify(this));
}
}
class Derived extends Base {
fieldA = 'a';
constructor() {
super(); // 必须先调用 super
console.log('After super, fieldA =', this.fieldA);
}
}
new Derived();查看输出与解释
Base constructor, this has: {}
After super, fieldA = a在 Base 构造器中 this 还没有任何字段,因为 Derived 的字段要在 super() 返回后初始化,说明顺序是先创建 this,再初始化子类字段。
继承日志类必须遵守的调用顺序
现有 Logger 基类,构造器接收 level 并生成 timestamp;FileLogger 继承它并需要 filename。在 FileLogger 构造器里,若把 this.filename = filename 放在 super(level) 之前,运行时立刻抛 ReferenceError,无法创建实例。正确顺序是先 super(level),再设置子类特有属性。
这种强制顺序的工程价值在于:父类构造器中可能调用类方法或访问公共字段,如果子类字段抢先初始化,会使父类操作看到不完整的数据。super() 先行确保父类基础状态就绪,子类再增强,最终对象状态一致。碰到类似继承时,养成先 super 再操作 this 的习惯,能避免大多数初始化陷阱。
哪些情况不要求显式调用 super()
若派生类完全没有写构造器,语言会自动提供默认构造器,它内部调用 super(...args),因此无需显式书写。但只要显式定义了构造器,就必须在返回前调用一次 super(),且只能调用一次——漏调或重调都会抛异常。
即使构造器返回一个普通对象(return override),也不能绕过这一规则:规范要求派生类构造器必须先完成父类初始化,否则报错。因此该边界只对未创建构造器的情况放宽,对显式构造器始终严格,代价是每个派生类都必须遵守固定顺序,换来一致的实例化语义。
容易答错的地方
- 认为基类也要调用 super(),或只有访问 this 才需要 super
- 基类没有父类,调用
super()语法本身就不存在。即便派生类构造器从未读取this,只要漏掉super(),引擎也会在构造器隐式返回时抛ReferenceError,所以显式构造器必须无条件调用它。 - 认为 super() 必须放在第一行,或用 super 只能调用父类方法
super()可以在访问this之前的任何位置,比如在局部声明后调用;它用于调用父类构造器,而super.method()才访问父类原型方法。两者书写位置和语法不同,不能混为一谈。
面试官还会怎么问?
在子类构造器中,如果我不引用 this,仅做参数校验,还需要调用 super() 吗?
仍然需要。因为无论是否使用 this,派生类构造器都必须以 super() 启动父类初始化。缺少调用时,引擎会在返回或隐式返回时抛错,不能绕过。
super() 调用前能读取类中声明的字段吗?
不能。类字段会在 super() 之后才初始化,因此在 super() 前读取实例字段得到 undefined(与访问原型链不同,这里实例上还不存在该字段)。读取私有字段更会触发 TypeError。
为什么 super() 之前不能使用 this,但方法却可以在构造器中被调用?
this 必须由父类构造器创建,方法则挂载在原型链上,构造器执行前原型链已经存在。但调用方法访问 this 时,仍会因为 this 未初始化而失败,所以通常也不建议在 super() 前调用依赖 this 的方法。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。