ES6 的 Class 继承通过 extends 和 super 关键字,提供了比 ES5 更清晰的继承语法。
extends 基础#
基本继承#
class Animal { constructor(name) { this.name = name }
speak() { console.log(`${this.name} 发出声音`) }}
class Dog extends Animal { constructor(name, breed) { super(name) // 调用父类构造函数 this.breed = breed }
speak() { console.log(`${this.name} 汪汪叫`) }}
const dog = new Dog('旺财', '柴犬')dog.speak() // 旺财 汪汪叫继承的本质#
class Parent {}class Child extends Parent {}
// 原型链关系Child.prototype.__proto__ === Parent.prototype // trueChild.__proto__ === Parent // true
// instanceof 检查const child = new Child()child instanceof Child // truechild instanceof Parent // truesuper 关键字#
作为函数调用#
super() 调用父类构造函数:
class Animal { constructor(name) { this.name = name }}
class Dog extends Animal { constructor(name, age) { // super() 必须在使用 this 之前调用 super(name) this.age = age }}🔶 在派生类的 constructor 中,必须先调用 super(),然后才能使用 this。
作为对象调用#
super 作为对象时指向父类原型:
class Animal { speak() { return '动物的声音' }}
class Dog extends Animal { speak() { // super.speak() 调用父类方法 return super.speak() + ',汪汪汪' }}
new Dog().speak() // '动物的声音,汪汪汪'在静态方法中使用 super#
class Animal { static getType() { return '动物' }}
class Dog extends Animal { static getType() { return super.getType() + ' - 狗' }}
Dog.getType() // '动物 - 狗'方法重写#
完全重写#
class Shape { getArea() { return 0 }}
class Rectangle extends Shape { constructor(width, height) { super() this.width = width this.height = height }
getArea() { return this.width * this.height }}扩展父类方法#
class Logger { log(message) { console.log(`[LOG] ${message}`) }}
class TimestampLogger extends Logger { log(message) { const timestamp = new Date().toISOString() super.log(`${timestamp} - ${message}`) }}
new TimestampLogger().log('Hello')// [LOG] 2024-01-01T00:00:00.000Z - Hello条件调用父类方法#
class Cache { get(key) { return this.data[key] }}
class ExpiringCache extends Cache { get(key) { const item = super.get(key) if (item && item.expiry < Date.now()) { delete this.data[key] return undefined } return item?.value }}继承内置类#
继承 Array#
class MyArray extends Array { // 获取第一个元素 first() { return this[0] }
// 获取最后一个元素 last() { return this[this.length - 1] }
// 求和 sum() { return this.reduce((a, b) => a + b, 0) }}
const arr = new MyArray(1, 2, 3, 4, 5)arr.first() // 1arr.last() // 5arr.sum() // 15
// 继承的方法返回的仍是 MyArray 实例arr.map((x) => x * 2) instanceof MyArray // true继承 Error#
class ValidationError extends Error { constructor(message, field) { super(message) this.name = 'ValidationError' this.field = field
// 修复原型链(某些环境需要) Object.setPrototypeOf(this, ValidationError.prototype) }}
class NetworkError extends Error { constructor(message, status) { super(message) this.name = 'NetworkError' this.status = status Object.setPrototypeOf(this, NetworkError.prototype) }}
try { throw new ValidationError('邮箱格式错误', 'email')} catch (error) { if (error instanceof ValidationError) { console.log(`字段 ${error.field}: ${error.message}`) }}继承 Map#
class ObservableMap extends Map { #listeners = new Set()
set(key, value) { const oldValue = this.get(key) super.set(key, value) this.#notify('set', { key, value, oldValue }) return this }
delete(key) { const value = this.get(key) const result = super.delete(key) if (result) { this.#notify('delete', { key, value }) } return result }
subscribe(listener) { this.#listeners.add(listener) return () => this.#listeners.delete(listener) }
#notify(type, data) { this.#listeners.forEach((listener) => listener(type, data)) }}
const map = new ObservableMap()map.subscribe((type, data) => { console.log(`${type}:`, data)})
map.set('name', '张三') // set: { key: 'name', value: '张三', oldValue: undefined }多态#
基础多态#
class Shape { getArea() { throw new Error('子类必须实现 getArea 方法') }
getInfo() { return `面积: ${this.getArea()}` }}
class Circle extends Shape { constructor(radius) { super() this.radius = radius }
getArea() { return Math.PI * this.radius ** 2 }}
class Rectangle extends Shape { constructor(width, height) { super() this.width = width this.height = height }
getArea() { return this.width * this.height }}
// 多态使用function printArea(shape) { console.log(shape.getInfo())}
printArea(new Circle(5)) // 面积: 78.54...printArea(new Rectangle(4, 6)) // 面积: 24模板方法模式#
class DataProcessor { // 模板方法 process(data) { const validated = this.validate(data) const transformed = this.transform(validated) return this.output(transformed) }
// 子类可重写的钩子方法 validate(data) { return data }
transform(data) { throw new Error('子类必须实现 transform 方法') }
output(data) { return data }}
class JSONProcessor extends DataProcessor { transform(data) { return JSON.parse(data) }
output(data) { return { success: true, data } }}
class CSVProcessor extends DataProcessor { transform(data) { return data.split('\n').map((line) => line.split(',')) }}抽象类模拟#
JavaScript 没有原生抽象类,但可以模拟:
class AbstractAnimal { constructor() { if (new.target === AbstractAnimal) { throw new Error('AbstractAnimal 不能被直接实例化') } }
// 抽象方法 speak() { throw new Error('子类必须实现 speak 方法') }
// 具体方法 sleep() { console.log('睡觉...') }}
class Dog extends AbstractAnimal { speak() { console.log('汪汪汪') }}
// new AbstractAnimal(); // Error: AbstractAnimal 不能被直接实例化new Dog().speak() // 汪汪汪私有成员继承#
私有字段不可继承#
class Parent { #secret = '私密信息'
getSecret() { return this.#secret }}
class Child extends Parent { revealSecret() { // return this.#secret; // SyntaxError: 私有字段在子类中不可访问 return this.getSecret() // 可以通过父类方法访问 }}
new Child().revealSecret() // '私密信息'使用 Symbol 实现受保护成员#
const _protected = Symbol('protected')
class Parent { constructor() { this[_protected] = '受保护的值' }
getProtected() { return this[_protected] }}
class Child extends Parent { useProtected() { // 子类可以访问 Symbol 属性 return this[_protected] }}
new Child().useProtected() // '受保护的值'组合优于继承#
继承的问题#
// 继承导致紧耦合class Bird { fly() { console.log('飞行中') }}
class Penguin extends Bird { // 企鹅不会飞,但继承了 fly 方法 fly() { throw new Error('企鹅不会飞') }}组合方式#
// 使用组合const canFly = { fly() { console.log(`${this.name} 飞行中`) },}
const canSwim = { swim() { console.log(`${this.name} 游泳中`) },}
const canWalk = { walk() { console.log(`${this.name} 行走中`) },}
class Animal { constructor(name) { this.name = name }}
// 鸟类:会飞会走class Bird extends Animal {}Object.assign(Bird.prototype, canFly, canWalk)
// 企鹅:会游泳会走class Penguin extends Animal {}Object.assign(Penguin.prototype, canSwim, canWalk)
// 鸭子:会飞会游泳会走class Duck extends Animal {}Object.assign(Duck.prototype, canFly, canSwim, canWalk)策略模式替代继承#
class PaymentProcessor { constructor(strategy) { this.strategy = strategy }
process(amount) { return this.strategy.process(amount) }}
const creditCardStrategy = { process(amount) { console.log(`信用卡支付 ${amount} 元`) return { success: true, method: 'creditCard' } },}
const wechatStrategy = { process(amount) { console.log(`微信支付 ${amount} 元`) return { success: true, method: 'wechat' } },}
const alipayStrategy = { process(amount) { console.log(`支付宝支付 ${amount} 元`) return { success: true, method: 'alipay' } },}
// 使用const processor = new PaymentProcessor(wechatStrategy)processor.process(100)Object.getPrototypeOf#
class Parent {}class Child extends Parent {}
Object.getPrototypeOf(Child.prototype) === Parent.prototype // trueObject.getPrototypeOf(Child) === Parent // true小结#
| 特性 | 说明 |
|---|---|
| extends | 声明继承关系 |
| super() | 调用父类构造函数 |
| super.method() | 调用父类方法 |
| 方法重写 | 子类重新定义父类方法 |
| 私有字段 | 不可被子类继承 |
| 静态成员 | 可被子类继承 |
Class 继承提供了清晰的语法,但要注意避免过深的继承层次,适时考虑组合模式。