
本文详解 typescript 中 `super` 的语义:它仅用于访问父类原型上的属性/方法或调用父类构造函数,**不能用于读取父类实例字段(如 `super.firstname`)**,否则返回 `undefined`;实例属性必须通过 `this` 访问。
在 TypeScript(以及底层 JavaScript)中,super 是一个严格语义化的关键字,其行为与 C# 或 Java 中的 base/super 存在本质差异——这正是许多开发者初遇困惑的根源。核心在于:super 只能访问定义在父类原型([[Prototype]])上的成员,而无法访问挂载在实例对象(this)上的属性。
为什么 super.firstName 返回 undefined?
回顾你的代码:
class Person {
public firstName: string;
public lastName: string;
constructor(fn: string, ln: string) {
this.firstName = fn; // ✅ 属性被赋值到 this(实例)上
this.lastName = ln;
}
}
class Customer extends Person {
constructor(fn: string, ln: string) {
super(fn, ln); // ✅ 正确:调用父类构造函数
}
displayDetails() {
console.log(`First Name: ${super.firstName}\tLast Name: ${super.lastName}`); // ❌ 错误!
}
}虽然 firstName 和 lastName 在 Person 类中声明为 public,但它们是实例字段(instance fields),在构造函数中通过 this.firstName = ... 显式挂载到新创建的实例对象上,而非定义在 Person.prototype 上。
而 super.xxx 的查找逻辑等价于:
Object.getPrototypeOf(this).xxx // 即 Person.prototype.xxx
由于 Person.prototype 上并不存在 firstName 属性(它只存在于 this 实例上),因此 super.firstName 查找失败,返回 undefined。
✅ 正确写法始终使用 this 访问继承来的实例属性:
displayDetails() {
console.log(`First Name: ${this.firstName}\tLast Name: ${this.lastName}`); // ✅ 正确
}super 的合法使用场景
| 场景 | 示例 | 说明 |
|---|---|---|
| 调用父类构造函数 | super(arg1, arg2) | 必须是子类构造函数中的第一条语句 |
| 调用父类方法 | super.sayHello() | 要求 sayHello 定义在父类原型上(如 Person.prototype.sayHello) |
| 访问父类 getter/setter | super.fullName | 若 fullName 是父类定义的 get 访问器,则可通过 super 访问 |
例如,补充一个合法使用 super 的示例:
class Person {
constructor(public firstName: string, public lastName: string) {}
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
introduce() {
return `Hi, I'm ${this.fullName}`;
}
}
class Customer extends Person {
introduce() {
// ✅ super 调用父类方法(定义在原型上)
return `Customer: ${super.introduce()}`;
}
get displayName() {
// ✅ super 访问父类 getter
return `★ ${super.fullName} ★`;
}
}注意事项与最佳实践
- ⚠️ 永远不要用 super 读取或写入实例字段(如 super.name, super.age),一律使用 this;
- ⚠️ super() 必须在子类构造函数中 this 被访问前调用,否则会报运行时错误;
- ? 可通过 console.log(Object.getPrototypeOf(new Customer('a','b'))) 验证哪些成员实际存在于原型链上;
- ? 若需强制“只读访问父类实现”,可将逻辑封装为 protected 方法或 getter,再通过 super.method() 调用。
总结
TypeScript 的 super 不是“父类实例的引用”,而是“父类原型的代理”。理解这一设计哲学(源自 ES6 class 的语义),就能避免绝大多数继承相关陷阱。记住口诀:
实例属性 → 用 this;原型方法/访问器 → 才用 super。
修正后的完整可运行代码如下:
class Person {
constructor(public firstName: string, public lastName: string) {}
}
class Customer extends Person {
constructor(fn: string, ln: string) {
super(fn, ln);
}
displayDetails() {
console.log(`First Name: ${this.firstName}\tLast Name: ${this.lastName}`);
}
}
const c = new Customer("Pradeep", "Kumar");
c.displayDetails(); // 输出:First Name: Pradeep Last Name: Kumar










