ES6 类继承:从原型链到现代语法糖的进化
1. ES6 类继承基本语法
1.1 类声明与继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 必须首先调用!
this.breed = breed;
}
bark() {
console.log(`${this.name} (${this.breed}) barks: Woof!`);
}
// 方法重写
speak() {
super.speak(); // 调用父类方法
console.log('And also wags tail');
}
}
1.2 关键特性
extends
建立继承关系super
在构造函数和方法中的不同表现- 方法自动绑定
[[HomeObject]]
(底层实现)
2. 继承链的构建原理
2.1 原型链结构
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 静态属性继承
Object.setPrototypeOf(Dog, Animal);
2.2 实例化过程
- 创建新对象,关联子类原型
- 执行子类构造函数
- 通过
super()
激活父类构造函数 - 完成实例属性初始化
3. super
的运行时机制
3.1 构造函数中的 super
- 必须在使用
this
前调用 - 相当于
Animal.prototype.constructor.call(this, ...args)
3.2 方法中的 super
// 以下两种写法等价
super.method();
Object.getPrototypeOf(Dog.prototype).method.call(this);
4. 静态成员的继承
class Base {
static version = '1.0';
static logVersion() {
console.log(this.version);
}
}
class Sub extends Base {
static version = '2.0';
}
Sub.logVersion(); // 输出 "2.0"
特性:
- 静态方法通过
super.constructor
访问父类 - 遵循与实例方法不同的继承链
5. 与 ES5 实现的对比
5.1 传统实现方式
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + ' makes a noise.');
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
5.2 ES6 的优势对比
特性 | ES5 | ES6 |
---|---|---|
代码可读性 | 低(显式操作原型链) | 高(类语法) |
静态继承 | 需手动实现 | 自动继承 |
方法重写 | 容易破坏原型链 | super 安全访问 |
内置类扩展 | 不可靠 | 规范支持 |
6. 开发实践与注意事项
6.1 强制规范
- 子类构造函数必须优先调用
super()
- 禁止在
super()
前访问this
- 方法中的
super
基于静态绑定
6.2 高级模式
扩展内置类型
class CustomError extends Error {
constructor(message, code) {
super(message);
this.code = code;
this.name = 'CustomError';
}
}
抽象类模式
class AbstractClass {
constructor() {
if (new.target === AbstractClass) {
throw new Error('Cannot instantiate abstract class');
}
}
}
6.3 常见错误示例
class Buggy extends Animal {
constructor() {
this.prop = 123; // 报错:Must call super constructor first
super();
}
}
7. 示例
以下是一个完整的 ES6 类继承示例,包含方法重写、super
使用和静态方法继承:
// 基类:电子设备
class ElectronicDevice {
static powerType = 'electricity'; // 静态属性
constructor(brand) {
this.brand = brand;
this.isOn = false;
}
// 实例方法
togglePower() {
this.isOn = !this.isOn;
console.log(`${this.brand} device is now ${this.isOn ? 'ON' : 'OFF'}`);
}
// 静态方法
static describePower() {
console.log(`All devices use ${this.powerType}`);
}
}
// 子类:智能手机
class Smartphone extends ElectronicDevice {
static powerType = 'battery'; // 重写静态属性
constructor(brand, os) {
super(brand); // 必须首先调用父类构造函数!
this.operatingSystem = os;
this.battery = 100;
}
// 方法重写 + 扩展
togglePower() {
if (this.battery <= 0) {
console.log('Battery dead!');
return;
}
super.togglePower(); // 调用父类方法
}
// 新增方法
checkBattery() {
console.log(`${this.brand} ${this.operatingSystem} phone: ${this.battery}%`);
}
// 重写静态方法
static describePower() {
super.describePower(); // 调用父类静态方法
console.log(`(Specifically smartphones use ${this.powerType})`);
}
}
// 使用示例
const iphone = new Smartphone('Apple', 'iOS');
iphone.togglePower();
// 输出: Apple device is now ON
// 执行了父类和子类的组合逻辑
iphone.checkBattery();
// 输出: Apple iOS phone: 100%
Smartphone.describePower();
// 输出:
// All devices use battery
// (Specifically smartphones use battery)
// 静态属性继承演示
console.log(ElectronicDevice.powerType); // electricity
console.log(Smartphone.powerType); // battery
关键知识点解释:
- 继承链构建
Smartphone.prototype
继承自ElectronicDevice.prototype
Smartphone
类本身继承自ElectronicDevice
类(静态成员继承)
- 方法重写机制
- 子类中定义同名方法会覆盖父类方法
- 通过
super.methodName()
可以调用父类原始方法
- 静态成员继承
- 静态属性和方法也会被继承
- 子类可以重写父类的静态成员
- 通过
super.staticMethod()
调用父类静态方法
- 构造函数要求
- 子类构造函数必须先调用
super()
- 之后才能使用
this
关键字
- 原型链验证
console.log(iphone instanceof Smartphone); // true
console.log(iphone instanceof ElectronicDevice); // true
结语:面向未来的类设计
ES6 类继承通过规范化语法:
- 降低原型链的理解门槛
- 提升代码可维护性
- 支持更严谨的 OOP 模式
- 为静态类型系统(TypeScript)奠定基础
在实际工程中,建议结合 private
字段(ES2022)和装饰器提案,构建健壮的类层次结构。对于复杂场景,可考虑组合模式(composition)与类继承的配合使用。