Javascript 继承方式
- 原型链继承
原型链继承通过让子类的原型指向父类的实例来实现继承。这样,子类可以继承父类的属性和方法。
function Parent() {
// 通过构造函数this ,继承属性
this.count = 1
}
// 通过原型链,继承方法
Parent.prototype.say = function () {
console.log('say:', this.count++)
}
function Child() {
// 子类构造函数中调用父类构造函数,实现属性的继承
Parent.call(this);
}
// 子类原型指向父类的实例,实现方法的继承
Child.prototype = new Parent();
const instance1 = new Child();
instance1.say(); // "say: 1"
const instance2 = new Child();
instance2.say(); // "say: 2"
优点:
- 实现简单
缺点:
- 当创建多个实例对象时,父类的构造函数被执行多次。
- 无法向父类传递参数
- 父类原型上的属性被修改,会影响所有子类属性改变。
- 构造函数继承(借用构造函数):
构造函数继承通过在子类构造函数中调用父类构造函数来实现继承,从而实现属性的继承。
function Parent(name) {
this.name = name || "Parent";
}
Parent.prototype.sayHello = function() {
console.log("Hello, " + this.name);
};
function Child(name) {
// 子类构造函数中调用父类构造函数,实现属性的继承
Parent.call(this, name);
}
const childObj = new Child("Child");
childObj.sayHello(); // "Hello, Child"
优点:
- 可以通过
call
方法,向父类传入参数
缺点:
- 父类与子类实例无关
- 子类无法访问父类原型对象上的方法
- 每个子类实例都会有父类的副本。因此,每当
new
一个子类实例之后,都会调用父类构造函数,重新创建父类所有属性和方法。
- 组合继承
组合继承结合了原型链继承和构造函数继承的优点,通过调用父类构造函数并设置子类原型为父类实例来实现继承。
function Parent() {
this.count = 1;
}
Parent.prototype.say = function () {
console.log('say:', this.count++);
}
function Child(...args) {
// this 指向父类构造函数,实现属性继承
Parent.apply(this, args)
}
// 原型链指向父类的实例,实现方法继承
Child.prototype = new Parent();
const instance = new Child('aa');
instance.say();
优点:
- 可以通过Parent.call方法,向父类传参
- 不存在引用属性值共享的问题
缺点
- Parent 的构造函数被执行两次
- 原型式继承:
原型式继承通过创建一个临时构造函数,将传入的对象作为该构造函数的原型,从而实现继承。
const parentObj = {
name: 'Parent',
say() {
console.log('say:', this.name)
}
}
function createObj(obj) {
function F() {}
F.prototype = obj;
return new F();
}
const instance = createObj(parentObj);
instance.name = 'Child';
instance.say(); // say: Child
缺点:
- 共享引用类型的属性,可能导致子对象修改属性时影响其他对象。
- 寄生式继承:
寄生式继承在原型式继承的基础上,增强了对象,返回一个新的对象。
function createObj(obj) {
const clone = Object.create(obj);
// 在对象上新增属性和方法
clone.say = function () {
console.log('say:', this.name);
}
return clone;
}
const parentObj = {
name: 'Parent'
}
const childObj = createObj(parentObj);
childObj.name = 'Child';
childObj.say() // "say: Child"
缺点:
- 与原型式继承相似,共享引用类型的属性可能导致子对象修改属性时影响其他对象。
- 寄生组合式继承:
寄生组合式继承是组合继承的一种优化,避免了调用两次父类构造函数。
function Parent(name) {
this.name = name || "Parent";
}
Parent.prototype.sayHello = function() {
console.log("Hello, " + this.name);
};
function Child(name) {
// 调用父类的构造函数,实现属性的继承
Parent.call(this, name);
}
// 子类原型指向父类原型对象的副本,并实现方法的继承
Child.prototype = Object.create(Parent.prototype); // 避免子类修改父类的属性
// 重新修改子类构造函数的指向
Child.prototype.constructor = Child // 避免父类构造函数被执行两次
const childObj = new Child('Child');
childObj.say();
优点
- 只调用一次父类的构造函数
- 不会存在引用属性共享问题
- 支持给父类传递参数 缺点:
- 太复杂,不易理解