一、原型链继承
原型链继承是 JavaScript 中最基本的继承方式。在原型链继承中,子类对象的原型指向其父类对象,通过这样的方式实现了继承。
代码示例:
function Parent() {
this.name = "parent";
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child() {}
Child.prototype = new Parent();
var child = new Child();
child.getName(); // "parent"
实现原理:当访问 child.getName() 时,JavaScript 引擎会先查找 child 对象身上是否有此方法,没有则去原型链中的 Parent.prototype 上查找,找到了则执行,否则继续向上查找,直到找到 Object.prototype。
优点:代码简单易懂,易于实现。
缺点:原型链继承会将父类的引用类型属性共享给所有子类实例,容易造成意外修改,同时无法向父类构造函数传递参数。
二、借用构造函数继承(经典继承)
借用构造函数继承是在子类构造函数内部调用父类构造函数,并通过 call 或 apply 方法将父类实例绑定到子类实例上,从而实现继承。
代码示例:
function Parent(name) {
this.name = name;
}
function Child(name) {
Parent.call(this, name);
}
var child = new Child("child");
console.log(child.name); // "child"
实现原理:通过在子类构造函数内部调用父类构造函数,并绑定 this,从而实现了继承。借用构造函数继承避免了父类引用类型属性被共享的问题。
优点:避免了引用类型属性被共享的问题,同时可以向父类构造函数传递参数。
缺点:每次都需要调用父类构造函数,无法复用父类原型对象上的方法,导致内存浪费,并且子类实例无法访问父类原型上的属性和方法。
三、组合继承(原型链继承和经典继承的组合)
组合继承是将原型链继承和借用构造函数继承结合使用的方式。通过借用构造函数继承父类的实例属性,通过原型链继承父类的原型属性和方法,从而实现继承。
代码示例:
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child("child", 18);
child.getName(); // "child"
实现原理:通过在子类构造函数内部通过 call 或 apply 方法调用父类构造函数,从而实现继承父类实例属性。然后将子类原型指向父类的实例,从而实现继承父类原型属性及方法。
优点:既继承了父类构造函数属性,又继承了父类原型上的属性及方法。
缺点:会调用两次父类构造函数,造成内存浪费。
四、原型式继承
原型式继承通过利用一个空对象作为中介实现对对象的复制,从而实现继承。
代码示例:
const parent = {
name: "parent",
getName: function () {
console.log(this.name);
}
};
const child = Object.create(parent);
child.name = "child";
child.getName(); // "child"
实现原理:通过 Object.create() 方法以一个对象作为参数,创建一个继承自该对象的新对象。
优点:可以实现对某个对象进行简单继承,避免了构造函数和原型的复杂度。
缺点:父类引用类型属性共享给所有子类实例,容易造成意外修改,无法向父类构造函数传递参数。
五、寄生式继承
寄生式继承是在原型式继承的基础上进行了封装,利用一个函数返回一个以父类对象为基础的新对象,实现继承。
代码示例:
const parent = {
name: "parent",
getName: function () {
console.log(this.name);
}
};
function createChild(parent, name) {
const child = Object.create(parent);
child.name = name;
return child;
}
const child = createChild(parent, "child");
child.getName(); // "child"
实现原理:借助工厂模式的思想,通过一个函数返回一个对象,从而实现继承。
优点:在不需要耦合构造函数的情况下实现继承。
缺点:与构造函数、原型链继承等方式一样,父类引用类型属性共享给所有子类实例,容易造成意外修改,无法向父类构造函数传递参数。
六、寄生组合式继承(最优解)
寄生组合式继承是在组合继承的基础上进行改良,通过寄生式继承来继承父类的原型对象,从而避免了组合继承中调用两次父类构造函数的问题。
代码示例:
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
var child = new Child("child", 18);
child.getName(); // "child"
实现原理:通过 Object.create() 方法以父类原型对象作为参数,创建一个继承自该对象的新对象,从而避免了调用两次父类构造函数的问题。
优点:避免了调用两次父类构造函数的问题,既继承了父类构造函数属性,又继承了父类原型上的属性及方法。
缺点:代码复杂度较高。