在 JavaScript 中,构造函数继承可以通过 原型链 和 构造函数调用 实现。以下是 ES5 和 ES6 的实现方式:
ES5 实现方式
关键步骤
- 继承实例属性:在子构造函数中调用父构造函数的
call
/apply
,绑定this
。 - 继承原型方法:将子构造函数的原型设置为父构造函数的实例,形成原型链。
- 修复
constructor
:修正子构造函数原型的constructor
指向自身。
代码实现
// 父构造函数
function Parent(name) {
this.name = name; // 实例属性
}
Parent.prototype.sayName = function() { // 原型方法
console.log("Parent name:", this.name);
};
// 子构造函数
function Child(name, age) {
// 1. 继承父构造函数的实例属性
Parent.call(this, name); // 绑定 this,传递参数
this.age = age;
}
// 2. 继承父构造函数的原型方法
Child.prototype = Object.create(Parent.prototype); // 建立原型链
// 3. 修复 constructor 指向
Child.prototype.constructor = Child;
// 子类新增原型方法
Child.prototype.sayAge = function() {
console.log("Child age:", this.age);
};
// 测试
const child = new Child("Alice", 10);
child.sayName(); // Parent name: Alice
child.sayAge(); // Child age: 10
关键点
Parent.call(this)
:确保子类实例继承父类的实例属性。Object.create(Parent.prototype)
:避免直接调用new Parent()
(可能触发父类副作用),仅继承原型方法。- 修复
constructor
:防止原型链混乱(否则child.constructor
会指向Parent
)。
ES6 实现方式
ES6 通过 class
和 extends
简化继承,底层依然基于原型链。
代码实现
// 父类
class Parent {
constructor(name) {
this.name = name; // 实例属性
}
sayName() { // 原型方法
console.log("Parent name:", this.name);
}
}
// 子类继承父类
class Child extends Parent {
constructor(name, age) {
super(name); // 必须调用 super(),继承父类实例属性
this.age = age;
}
sayAge() { // 子类原型方法
console.log("Child age:", this.age);
}
}
// 测试
const child = new Child("Bob", 12);
child.sayName(); // Parent name: Bob
child.sayAge(); // Child age: 12
关键点
extends
:建立原型链,自动继承父类原型方法。super()
:必须优先调用,相当于Parent.call(this)
,负责初始化父类实例属性。- 无需手动维护原型链和
constructor
:语法糖自动处理。
对比总结
特性 | ES5 | ES6 |
---|---|---|
语法 | 手动操作原型链和构造函数调用 | 使用 class 和 extends 语法糖 |
实例属性继承 | 通过 Parent.call(this) |
通过 super() |
原型方法继承 | 手动设置 Child.prototype = Object.create() |
自动通过 extends 实现 |
constructor 修复 |
需手动修正 Child.prototype.constructor |
自动维护 |
静态方法继承 | 需手动处理(如 Child.__proto__ = Parent ) |
自动继承 |
注意事项
- ES6 的
super
:- 必须在子类
constructor
中优先调用,否则报错。 super
在方法中可调用父类原型方法(如super.sayName()
)。
- 必须在子类
- 静态方法继承:
- ES5 中需手动设置
Child.__proto__ = Parent
。 - ES6 中通过
extends
自动继承父类的静态方法。
- ES5 中需手动设置
通过 ES5 显式原型链操作 或 ES6 的 class
语法,均可实现构造函数继承。ES6 的语法更简洁且更接近传统面向对象语言,推荐优先使用。