先记住这个答案
派生类构造器要先调用 super(),才能访问 this。super() 执行父类构造器,返回后才轮到子类字段初始化,然后继续执行剩余构造器语句。显式构造器如果不调用 super(),又试图访问 this 或隐式返回实例,就会抛 ReferenceError。但如果直接返回另一个对象,可以不调用 super();这是少见的例外。
- 派生类访问
this前必须调用super() - 子类字段在
super()返回后初始化 - 直接返回别的对象可以不调用
super()
super() 与 this 绑定的底层顺序
在派生类中,new 执行时先运行构造器中 super() 之前的代码,此时 this 尚未创建,任何对 this 的读取或赋值都会触发 ReferenceError。随后执行 super(),它调用父类构造器并创建实例对象,返回后 this 才可被访问。之后才初始化本类字段,再执行构造函数体剩余部分。
字段初始化器(如 field = 1)会在 super() 返回后按声明顺序执行。在 super() 之前尝试读取任何 this.field,先碰到的就是未初始化的 this 所导致的 ReferenceError,并不会先得到 undefined。这也是判断报错原因时最容易弄错的一步。
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();同一次构造过程中重复调用也会抛异常。
还有一个少见但真实的例外:派生类构造器可以不调用 super(),直接 return {}。返回对象会成为 new 的结果,未初始化的 this 不再需要返回,因此不会抛错。不过这样父类构造器和子类实例字段都没有在通常的实例上完成初始化,返回对象也未必在子类的原型链上。普通继承代码仍应先调用 super()。
容易答错的地方
- 认为基类也要调用 super(),或只有访问 this 才需要 super
- 基类没有父类,调用
super()语法本身就不存在。派生类如果既不读取this,也不返回别的对象,漏掉super()仍会在隐式返回时抛ReferenceError。直接返回对象的例外不能概括成“所有显式构造器都必须调用”。 - 认为 super() 必须放在第一行,或用 super 只能调用父类方法
super()可以在访问this之前的任何位置,比如在局部声明后调用;它用于调用父类构造器,而super.method()才访问父类原型方法。两者书写位置和语法不同,不能混为一谈。
面试官还会怎么问?
在子类构造器中,如果我不引用 this,仅做参数校验,还需要调用 super() 吗?
如果还要返回正常的子类实例,仍然需要。参数校验可以放在 super() 之前,只要这段代码不访问 this。若校验失败直接抛错,也无需继续调用;若直接返回另一个对象,则属于少见的替换实例路径。
super() 调用前能读取类中声明的字段吗?
不能。在 super() 前,this 本身还没有初始化;读取 this.field 会先抛 ReferenceError,不会返回 undefined。super() 返回后,子类字段初始化器才依次执行,随后构造器剩余语句才能读到它们。
super() 之前能调用父类方法吗?
不能通过 this.method() 调用,因为读取 this 就会抛 ReferenceError。super.method() 的属性查找可能成功,但常规实例方法往往还要使用接收者 this;在实例初始化前调用它同样不可靠。先 super(),再调用需要实例状态的方法。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。