Skip to content

Class 继承

ES6 的 Class 继承通过 extendssuper 关键字,提供了比 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 // true
Child.__proto__ === Parent // true
// instanceof 检查
const child = new Child()
child instanceof Child // true
child instanceof Parent // true

super 关键字#

作为函数调用#

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() // 1
arr.last() // 5
arr.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 // true
Object.getPrototypeOf(Child) === Parent // true

小结#

特性说明
extends声明继承关系
super()调用父类构造函数
super.method()调用父类方法
方法重写子类重新定义父类方法
私有字段不可被子类继承
静态成员可被子类继承

Class 继承提供了清晰的语法,但要注意避免过深的继承层次,适时考虑组合模式。