在 JavaScript 中,继承是一种常见的模式,用来让一个对象或类可以共享另一个对象或类的属性和方法。
1.原型链继承
通过修改子类的原型为父类的实例,从而继承父类的属性和方法。
function Parent() {
this.name = "Parent";
}
Parent.prototype.sayHello = function () {
console.log("Hello from Parent");
};
function Child() {}
Child.prototype = new Parent();
const child = new Child();
child.sayHello(); // Hello from Parent
优点:子类可以访问父类的所有方法和属性。
缺点:所有实例共享父类的引用属性(如数组或对象会被共享)。无法向父类构造函数传递参数。
2.借用构造函数继承(经典继承)
在子类的构造函数中调用父类构造函数。
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
function Child(name) {
Parent.call(this, name);
}
const child1 = new Child("Child1");
child1.colors.push("yellow");
const child2 = new Child("Child2");
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]
优点:不共享父类的引用属性。可以向父类构造函数传参。
缺点:只能继承父类实例的属性和方法,不能继承原型上的方法。
3.组合继承(原型链 + 借用构造函数)
结合原型链和借用构造函数的优点,避免了它们的缺点。
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 借用构造函数
this.age = age;
}
Child.prototype = new Parent(); // 原型链继承
Child.prototype.constructor = Child;
const child1 = new Child('Child1', 18);
child1.colors.push('yellow');
console.log(child1.colors); // ['red', 'blue', 'green', 'yellow']
child1.sayName(); // 'Child1'
const child2 = new Child('Child2', 20);
console.log(child2.colors); // ['red', 'blue', 'green']
child2.sayName(); // 'Child2'
优点: 解决了原型链继承中引用类型共享的问题。子类实例可以独立拥有属性和方法。
缺点: 父类构造函数调用了两次:一次在 Parent.call,一次在 Child.prototype = new Parent(), 性能略有浪费。
4.原型式继承
通过 Object.create 方法以一个对象为原型创建另一个对象。
const parent = {
name: "Parent",
sayHello() {
console.log("Hello from Parent");
},
};
const child = Object.create(parent);
child.sayHello(); // Hello from Parent
优点:简单易用,性能较高。能够直接继承对象的属性和方法。
缺点:和原型链继承一样,共享引用类型的属性。
5.寄生式继承
在原型式继承的基础上,增强对象功能。
function createChild(parent) {
const clone = Object.create(parent);
clone.sayHi = function () {
console.log("Hi from Child");
};
return clone;
}
const parent = {
name: "Parent",
sayHello() {
console.log("Hello from Parent");
},
};
const child = createChild(parent);
child.sayHi(); // Hi from Child
优点:灵活,可以动态扩展功能。
缺点:不支持原型链。
6.寄生组合式继承
通过寄生式继承的方式优化组合继承,避免多次调用父类构造函数。
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayHello = function () {
console.log("Hello from Parent");
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child = new Child("Child", 10);
child.sayHello(); // Hello from Parent
优点:不会多次调用父类构造函数。性能更优,避免组合继承中的效率问题。
缺点:实现稍微复杂。
7.ES6 Class 继承
通过 class 和 extends 关键字实现继承。
class Parent {
constructor(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
sayHello() {
console.log("Hello from Parent");
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类构造函数
this.age = age;
}
}
const child = new Child("Child", 10);
child.sayHello(); // Hello from Parent
console.log(child.colors); // ["red", "blue", "green"]
优点:语法更加清晰简洁,符合现代 JavaScript 标准。支持父类和子类的更复杂操作。
缺点:语法糖,底层仍然基于原型实现。
总结
对于现代开发,ES6 的 Class 继承 和 寄生组合式继承 是推荐的方式,尤其是 Class 继承具有语法上的优势,更容易被开发者接受。