继承方式
JavaScript 中的继承主要有以下几种实现方式,每种方式都有其特点和适用场景。
原型链继承
核心思想:将父类的实例作为子类的原型
js
Child.prototype = new Parent();优点:
- 简单直接
- 所有子类实例共享父类的方法
缺点:
- 子类实例不能给父类构造函数传参
- 原型对象包含引用类型的属性会被所有实例共享
示例:
js
function Parent() {
this.name = "parent";
}
Parent.prototype.getName = function () {
return this.name;
};
function Child() {}
Child.prototype = new Parent();
const child = new Child();
console.log(child.getName()); // 'parent'构造函数继承
核心思想:在子类构造函数中调用父类构造函数
js
function Child() {
Parent.call(this);
}优点:
- 子类实例可以向父类构造函数传参
- 避免了引用类型属性被共享的问题
缺点:
- 父类方法无法复用(每次创建实例都会重新创建方法)
示例:
js
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function () {
return this.name;
};
function Child(name) {
Parent.call(this, name);
}
const child = new Child("child");
console.log(child.name); // 'child'组合继承
核心思想:结合原型链继承和构造函数继承的优点
js
function Child() {
Parent.call(this);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;优点:
- 解决了构造函数继承无法复用方法的问题
- 子类实例可以向父类构造函数传参
- 父类方法通过原型链被所有实例共享
缺点:
- 调用了两次父类构造函数(性能开销)
示例:
js
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
Child.prototype.getAge = function() {
return this.age;
};
const child = new Child('child', 18);
console.log(child.getName()); // 'child'
console.log chestnut.age); // 18原型继承
核心思想:Object.create() 创建一个新对象,使用现有对象作为新对象的原型
js
const parent = { name: "parent" };
const child = Object.create(parent);特点:
- 不会调用构造函数
- 可以实现基于原型的继承
- ES5 新增
寄生组合继承(最优方案)
核心思想:通过寄生方式砍掉父类的示例属性,同时又保持原型链不变
js
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
function inheritPrototype(child, parent) {
const prototype = Object.create(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
inheritPrototype(Child, Parent);优点:
- 只调用一次父类构造函数
- 原型链保持不变
- 能够正常访问 instanceof
示例:
js
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function () {
return this.name;
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
function inheritPrototype(child, parent) {
const prototype = Object.create(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
inheritPrototype(Child, Parent);
Child.prototype.getAge = function () {
return this.age;
};
const child = new Child("child", 18);
console.log(child.getName()); // 'child'
console.log(child instanceof Parent); // trueES6 Class 继承
核心思想:使用 class 关键字和 extends 实现继承
js
class Child extends Parent {
constructor() {
super();
}
}优点:
- 语法更简洁、更符合面向对象编程习惯
- 内置支持 extends 和 super
- constructor 中的 super() 相当于 Parent.call(this)
示例:
js
class Parent {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
getAge() {
return this.age;
}
}
const child = new Child("child", 18);
console.log(child.getName()); // 'child'
console.log(child instanceof Parent); // true继承方式对比
| 方式 | 调用构造函数次数 | 方法复用 | 能传参 | 缺点 |
|---|---|---|---|---|
| 原型链继承 | 1 | ✓ | ✗ | 引用类型共享 |
| 构造函数继承 | 多次 | ✗ | ✓ | 方法不复用 |
| 组合继承 | 2 | ✓ | ✓ | 性能开销 |
| 寄生组合继承 | 1 | ✓ | ✓ | 写法复杂 |
| ES6 Class | 1 | ✓ | ✓ | 需要编译支持 |