JavaScript 类继承
关键要点
- JavaScript 类继承通过
extends
关键字实现,子类继承父类的属性和方法。 - 子类构造函数需使用
super()
调用父类构造函数,确保父类初始化。 - 子类可覆盖父类方法或添加新方法,支持代码复用和扩展。
- 继承基于原型链,子类原型的
__proto__
指向父类原型。 - 需注意
super()
必须在子类构造函数中使用this
前调用,否则报错。
类继承的基本概念
JavaScript 类继承是面向对象编程的重要特性,允许子类继承父类的属性和方法,从而实现代码复用和功能扩展。ES6 引入的 class
语法使得继承更加直观,但底层仍基于原型链。
使用 extends
和 super
使用 extends
关键字定义子类,super()
调用父类构造函数。例如:
class Person {
constructor(name) {
this.name = name;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}`);
}
}
class Professor extends Person {
constructor(name, teaches) {
super(name); // 调用父类构造函数
this.teaches = teaches;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}, and I teach ${this.teaches}`);
}
}
const prof = new Professor("Alice", "Math");
prof.introduceSelf(); // 输出 "Hi! I'm Alice, and I teach Math"
原型链与继承
JavaScript 类继承基于原型链,子类原型的 __proto__
指向父类原型,确保子类实例可以访问父类方法。
注意事项
super()
必须在子类构造函数中使用this
前调用。- 子类可覆盖父类方法,但父类构造函数使用自身属性值,而非子类覆盖值。
class Person {
constructor(name) {
this.name = name;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}`);
}
}
class Professor extends Person {
constructor(name, teaches) {
super(name);
this.teaches = teaches;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}, and I teach ${this.teaches}`);
}
grade() {
console.log(`Grading assignments...`);
}
}
const prof = new Professor("Alice", "Math");
prof.introduceSelf(); // 输出 "Hi! I'm Alice, and I teach Math"
prof.grade(); // 输出 "Grading assignments..."
JavaScript 类继承全面讲解
JavaScript 类继承是 ES6 引入的面向对象编程特性,通过 class
和 extends
关键字实现,提供了更直观的语法来创建对象层次结构。尽管类语法是原型继承的语法糖,但它简化了代码结构,使开发者更容易实现代码复用和功能扩展。以下是对 JavaScript 类继承的详细讲解,涵盖定义、实现、原型链、注意事项和最佳实践。
1. 类继承的基本概念
类继承允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以:
- 继承父类的属性和方法。
- 添加新的属性和方法。
- 覆盖父类的方法以实现自定义行为。
例如,假设有一个 Person
类:
class Person {
constructor(name) {
this.name = name;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}`);
}
}
我们可以创建一个 Professor
类,继承自 Person
:
class Professor extends Person {
constructor(name, teaches) {
super(name); // 调用父类构造函数
this.teaches = teaches;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}, and I teach ${this.teaches}`);
}
grade() {
console.log(`Grading assignments...`);
}
}
const prof = new Professor("Alice", "Math");
prof.introduceSelf(); // 输出 "Hi! I'm Alice, and I teach Math"
prof.grade(); // 输出 "Grading assignments..."
2. 使用 extends
和 super
extends
关键字:用于声明子类继承父类。例如,class Professor extends Person
表示Professor
继承自Person
。super
关键字:- 在子类构造函数中,
super()
调用父类构造函数,初始化父类的属性。 - 必须在子类构造函数中使用
this
之前调用super()
,否则会抛出 ReferenceError。 - 也可以用
super.method()
调用父类的方法。例如:javascript class Professor extends Person { introduceSelf() { super.introduceSelf(); // 调用父类方法 console.log(`I teach ${this.teaches}`); } }
如果子类不定义构造函数,JavaScript 会自动生成一个默认构造函数:
constructor(...args) {
super(...args);
}
3. 原型链与继承机制
JavaScript 类继承的底层是基于原型链的:
- 子类的原型(
Professor.prototype
)的__proto__
属性指向父类的原型(Person.prototype
)。 - 子类实例通过原型链访问父类的方法。例如,调用
prof.introduceSelf()
时,JavaScript 首先查找Professor.prototype
,若未找到,则沿原型链查找Person.prototype
。 - 这种机制确保子类实例可以访问父类的属性和方法,同时允许子类覆盖父类方法。
4. 方法覆盖与扩展
子类可以覆盖父类的方法以实现自定义行为。例如,Professor
类覆盖了 introduceSelf()
方法,添加了与 teaches
属性相关的逻辑。子类还可以添加新方法,如 grade()
,以扩展功能。
5. 属性和字段的注意事项
- 属性覆盖:如果子类定义了与父类同名的属性,父类构造函数会使用自己的属性值。例如:
class Person {
constructor(name) {
this.name = name;
}
}
class Professor extends Person {
name = "Professor"; // 子类覆盖 name 属性
constructor() {
super("Alice"); // 父类构造函数设置 name 为 "Alice"
}
}
const p = new Professor();
console.log(p.name); // 输出 "Professor"
- 私有属性:使用
#
定义的私有属性仅在类内部可访问,子类无法直接访问父类的私有属性。
6. 继承的优点与应用
- 代码复用:子类无需重复定义父类的属性和方法。
- 扩展性:子类可以添加新功能或修改父类行为。
- 多态性:通过方法覆盖,子类可以实现不同的行为,适合构建复杂对象层次结构。
7. 注意事项与最佳实践
- 严格模式:类始终在严格模式下运行,
this
在未绑定上下文时为undefined
。 - 时间死区(TDZ):类声明受时间死区限制,必须先定义后使用。
- 调用
super()
:子类构造函数必须调用super()
,否则会导致错误。 - 避免复杂继承链:过深的继承链可能增加代码复杂性,建议保持简单。
- 使用
super
调用父类方法:在覆盖方法时,考虑是否需要调用父类方法以保留其功能。
8. 其他继承方式
虽然 ES6 的类继承是现代 JavaScript 的首选方式,但传统上 JavaScript 还支持其他继承方式,如:
- 原型链继承:将父类实例作为子类原型,简单但共享引用属性可能导致问题。
- 构造函数继承:在子类构造函数中调用父类构造函数,适合传递参数但不继承原型方法。
- 组合继承:结合原型链和构造函数继承,调用父类构造函数两次。
- 寄生组合继承:优化组合继承,避免重复调用父类构造函数,被认为是引用类型继承的理想方式。
这些传统方式在现代开发中较少使用,因为类语法更简洁且易于维护。
9. 表格:类继承与传统继承方式对比
继承方式 | 核心机制 | 优点 | 缺点 | 推荐指数 |
---|---|---|---|---|
类继承(ES6) | 使用 extends 和 super ,基于原型链 | 语法简洁、支持多态、易于维护 | 需注意 super() 调用顺序 | ★★★★★ (5/5) |
原型链继承 | 父类实例作为子类原型 | 简单、纯继承 | 引用属性共享、无法传递参数 | ★★ (2/5) |
构造函数继承 | 子类调用父类构造函数 | 支持参数传递、避免引用属性共享 | 不继承原型方法、函数不可复用 | ★★ (2/5) |
组合继承 | 结合原型链和构造函数继承 | 继承原型和实例属性、支持参数传递 | 调用父类构造函数两次 | ★★★★ (4/5) |
寄生组合继承 | 优化组合继承,避免重复调用父类构造函数 | 高效、保留原型链、避免重复实例 | 实现复杂 | ★★★★ (4/5) |
10. 总结
JavaScript 类继承通过 extends
和 super
关键字提供了一种直观的面向对象编程方式,基于原型链实现。子类可以继承父类的属性和方法,覆盖方法或添加新功能,从而实现代码复用和扩展。开发者需注意 super()
的正确使用以及原型链的底层机制,以避免常见错误。相比传统的原型继承方式,类继承语法更简洁,适合现代开发。
class Person {
constructor(name) {
this.name = name;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}`);
}
}
class Professor extends Person {
constructor(name, teaches) {
super(name);
this.teaches = teaches;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}, and I teach ${this.teaches}`);
}
grade() {
console.log(`Grading assignments...`);
}
}
const prof = new Professor("Alice", "Math");
prof.introduceSelf(); // 输出 "Hi! I'm Alice, and I teach Math"
prof.grade(); // 输出 "Grading assignments..."