在 JavaScript 中,继承主要通过原型链实现,常见的继承方式有以下几种,每种方式都有其优缺点:
1. 原型链继承
1. 实现方式:将子类的原型对象指向父类的实例。
function Parent() {}
function Child() {}
Child.prototype = new Parent();
2. 优点:简单直观,能继承父类原型上的属性和方法。
3. 缺点
(1). 所有子类实例共享父类的引用类型属性,修改一个实例会影响其他实例;
(2). 无法向父类构造函数传递参数,即子类实例化时无法定制父类属性;
2. 构造函数继承
1. 实现方式:在子类构造函数中调用父类构造函数。
function Parent(name) { this.name = name; }
function Child(name) { Parent.call(this, name); }
2. 优点
(1). 解决了引用类型属性共享的问题,使得每个实例独立;
(2). 支持向父类构造函数传递参数;
3. 缺点:无法继承父类原型上的方法,方法只能在构造函数中定义,导致重复创建函数,浪费内存;
3. 组合继承(经典继承)
1. 实现方式:结合原型链继承和构造函数继承;
function Parent(name) { this.name = name; }
function Child(name) { Parent.call(this, name); }
Child.prototype = new Parent();
Child.prototype.constructor = Child;
2. 优点
(1). 既能继承父类实例属性,又能继承父类原型方法;
(2). 引用类型属性不共享,支持传参;
3. 缺点:父类构造函数被调用两次,分别为Parent.call 和 new Parent(),导致子类原型对象中多出一份冗余属性;
4. 原型式继承
1. 实现方式 :基于已有对象创建新对象,类似 Object.create
const parent = { name: "Parent" };
const child = Object.create(parent);
2. 优点:适用于不需要单独构造函数的场景,直接基于对象继承;
3. 缺点:引用类型属性会被所有实例共享,这一点与原型链继承相同;
5. 寄生式继承
1. 实现方式:在原型式继承的基础上增强对象。
function createChild(parent) {const obj = Object.create(parent);obj.sayHi = function() { console.log("Hi"); };return obj;
}
2. 优点:可以在不修改原对象的情况下扩展新方法;
3. 缺点:方法在每次创建实例时重复定义,效率低;
6. 寄生组合式继承(最优解)
1. 实现方式:通过寄生式继承父类原型,避免调用父类构造函数;
function Parent(name) { this.name = name}
function Child(name) { Parent.call(this, name)}Child.prototype = Object.create(Parent.prototype);
prototype.constructor = Child;
2. 优点
(1). 解决了组合继承中父类构造函数被调用两次的问题,性能最优;
(2). 完美实现原型链继承,且引用类型属性不共享;
3. 缺点:实现稍复杂;
7. ES6 class 继承
1. 实现方式:使用 extends 和 super 关键字;
class Parent {constructor(name) {this.name = name;}
}
class Child extends Parent {constructor(name) {super(name);}
}
2. 优点
(1). 语法简洁,符合传统面向对象语言的写法;
(2). 底层基于寄生组合式继承,是最佳实践的语法糖;
3. 缺点:需要环境支持 ES6+
8. 总结对比
推荐使用:现代开发中优先使用 ES6 class 继承。兼容旧环境时使用 寄生组合式继承。