1. extends 的基本语法与核心概念
1.1 语法要点:extends 关键字
在 ES6 引入的面向对象语法中,extends 提供了一种清晰的继承机制。通过该关键字,子类可以从父类获取实例属性和方法,并在构造阶段选择性地执行父类的初始化逻辑。理解其核心概念有助于在类语法下实现可维护的代码结构。
使用 extends 时,子类的构造函数通常需要调用 super,以确保父类的初始化顺序在子类之前完成。这种顺序对于保持 实例状态 的一致性很重要,并且有助于实现多态行为。
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a sound`);}
}
class Dog extends Animal {constructor(name, breed) {super(name); // 调用父类构造函数this.breed = breed;}speak() {console.log(`${this.name} barks`);}
}
通过上述示例可以看到,extends 使得 Dog 具备 Animal 的实例属性和方法,同时 Dog 还能覆盖父类的方法实现。这样的设计有助于代码复用并实现多态,让子类在保留父类行为的同时扩展自己的特性。
1.2 原型链与继承模型
在底层实现层面,extends 将子类原型对象与父类原型对象建立原型链关系,形成 原型链继承。这意味着实例具有从父类原型到子类原型再到 Object.prototype 的整条原型链路。
这种模型确保了实例对父类方法的访问,并在运行时支持实例检查(如 instanceof)与类型推断。理解该机制可以帮助你更高效地设计继承层级,避免出现难以追踪的行为覆盖。
class Parent {say() { console.log('parent'); }
}
class Child extends Parent {say() { console.log('child'); }
}
const c = new Child();
c.say(); // "child"
console.log(c instanceof Parent); // true
2. extends 的实战场景与设计原则
2.1 何时选择继承而非组合
在具有明确父子关系且需要共享大量行为的场景中,继承(extends) 能显著减少重复代码并提升一致性。通过将共用逻辑放置在父类中,子类只需聚焦于特定差异化行为,提高代码的可维护性。

但若父子关系只是“能力的集合”而非天然的层级结构,应该考虑使用组合、委托或混入模式,以避免过深的继承树和耦合问题。
class Vehicle {move() { console.log('moving'); }
}
class Car extends Vehicle {constructor(model) {super();this.model = model;}honk() { console.log('honk'); }
}
2.2 组合优于继承的实战原则
在实际开发中,组合优于继承的原则往往更健壮。通过将行为委托给独立的对象或通过混入实现功能复用,可以在不改变父类结构的前提下扩展能力,提升灵活性。
使用组合时,可以通过将策略对象注入到实例、或者动态给原型添加方法来实现行为复用,这样的设计更易于单元测试和模块化解耦。
const canFly = {fly() { console.log(`${this.name} can fly`); }
};class Bird {constructor(name) { this.name = name; }
}
class Eagle extends Bird {}
Object.assign(Eagle.prototype, canFly);const e = new Eagle('Aquila');
e.fly();
2.3 实战中的 extends 使用注意
在大规模项目中,继承层级的深度应尽量保持简洁,以避免方法覆盖和行为歧义。确保父类职责单一、接口清晰,并遵循
里氏替换原则(LSP),即子类对象应能替换父类对象而不破坏程序行为。此原则对 extends 的稳健性至关重要。
class Animal {speak() { console.log('animal sound'); }
}
class Dog extends Animal {speak() {super.speak(); // 调用父类实现console.log('dog barks');}
}
3. 进阶用法与注意要点
3.1 super 的正确使用与限制
创建子类实例时,super() 必须在访问 this 之前执行,否则会抛出错误。super 既可以调用父类的构造函数,又可以在方法中调用 super.method() 来保留父类实现的基础上扩展。
在重写父类方法时,super 提供了一种安全的扩展路径,使得子类能够在前后保持对父类行为的依赖。
class Vehicle {move() { console.log('vehicle moves'); }
}
class Car extends Vehicle {move() {super.move();console.log('car moves faster');}
}
3.2 静态成员与继承
继承不仅影响实例成员,同时也会影响静态成员。父类的静态方法可以被子类直接继承并调用,形成统一的“工具方法”层级。
需要注意的是,静态成员属于类本身而非实例,通常通过 SubClass.describe() 的形式访问。
class Animal {static describe() { return 'I am an animal'; }
}
class Dog extends Animal {}console.log(Dog.describe()); // I am an animal
3.3 与混入、装饰器等的关系
在复杂的对象系统中,单纯的单继承可能不足以覆盖所有复用需求。此时可以通过混入、委托或装饰器等技术,结合 extends 提高灵活性。
例如,可以把通用行为抽离成独立对象,使用 Object.assign 或自定义混入函数,将行为注入到子类原型,保持继承结构的简洁性。
function mixin(target, source) {Object.getOwnPropertyNames(source).forEach(name => {if (name !== 'constructor') {Object.defineProperty(target, name, Object.getOwnPropertyDescriptor(source, name));}});
}
class Person { constructor(name) { this.name = name; } }
const Talker = {sayHi() { console.log(`Hi, I'm ${this.name}`); }
};
class Student extends Person {}
mixin(Student, Talker);const s = new Student('Tom');
s.sayHi();


