1. 原型链继承
- 特点:简单易懂,但是所有实例共享原型上的属性,如果原型上定义的是引用类型值,那么所有实例会共享同一份数据。
- 适用场景:当不需要在父类的原型上定义引用类型属性时,或者当需要共享某些状态时可以使用原型链继承。
-
{//原型链继承 function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; }; function Student(name,grade){ Person.call(this,name);//调用父类构造函数 this.grade = grade; } //让'Student'继承'Person' Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; const student = new Student("Alice",10); console.log(student.getName());//Alice console.log(student.grade);//10 console.log(student.name);//Alice }
2. 构造函数继承
- 特点:每个实例都有自己的一份属性副本,不会共享引用类型的数据,但是无法实现函数复用,每次创建子类实例时都需要重新执行父类构造函数。
- 适用场景:当需要每个实例都拥有独立的属性副本时,或者当需要在子类构造函数中执行一些特定的初始化操作时。
-
{//构造函数继承 //构造函数继承的主要特点是每次示例都有自己的一份属性副本,不会共享引用类型的数据。但是,这种方法无法实现函数复用,每次 //创建子类实例时都需要重新执行父类构造函数。 function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; } function Student(name,grade){ Person.call(this,name); this.grade = grade; } const student = new Student('Alice',10); console.log(student.getName());//Alice console.log(student.grade);//10 console.log(student.name);//Alice }
3. 组合继承
- 特点:结合了原型链继承和构造函数继承的优点,既实现了函数复用(不必在每个实例中重新定义函数),又保证了每个实例都有自己的属性副本。
- 适用场景:当需要实现函数复用并且每个实例都需要拥有独立的属性副本时,组合继承是最常用的选择之一。
4. 原型式继承(使用 Object.create
)
- 特点:允许创建一个新对象,使其继承另一个对象的状态,而不需要构造函数或原型链。
- 适用场景:当需要继承一个纯粹的对象(不是构造函数实例)的状态时,例如创建一个具有特定属性配置的新对象。
-
// 定义一个对象 const person = { name: 'Parent', age: 30, getName: function() { return this.name; }, getAge: function() { return this.age; } }; // 使用 Object.create 创建新对象 const student1 = Object.create(person); const student2 = Object.create(person); // 为 student1 添加特定的属性 student1.name = 'Alice'; student1.age = 15; student1.grade = 10; // 为 student2 添加特定的属性 student2.name = 'Bob'; student2.age = 16; student2.grade = 11; // 测试 console.log(student1.getName()); // 输出: Alice console.log(student1.getAge()); // 输出: 15 console.log(student1.grade); // 输出: 10 console.log(student2.getName()); // 输出: Bob console.log(student2.getAge()); // 输出: 16 console.log(student2.grade); // 输出: 11
5. 寄生式继承
- 特点:基于原型式继承,但在创建新对象之后,可以增强这个对象,为其添加更多的功能。
- 适用场景:当需要对继承的对象进行定制化修改或增强时。
-
const person = { name: 'Parent', age: 30, getName: function() { return this.name; }, getAge: function() { return this.age; } }; function createStudent(original, grade, school) { const student = Object.create(original); student.grade = grade; student.school = school; student.getGrade = function() { return this.grade; }; return student; } const student1 = createStudent(person, 10, 'XYZ School'); const student2 = createStudent(person, 11, 'ABC School'); console.log(student1.getName()); // 输出: Parent console.log(student1.getAge()); // 输出: 30 console.log(student1.grade); // 输出: 10 console.log(student1.school); // 输出: XYZ School console.log(student1.getGrade()); // 输出: 10 console.log(student2.getName()); // 输出: Parent console.log(student2.getAge()); // 输出: 30 console.log(student2.grade); // 输出: 11 console.log(student2.school); // 输出: ABC School console.log(student2.getGrade()); // 输出: 11
6. 寄生组合式继承
- 特点:避免了组合继承中两次调用父类构造函数的问题,通过复制父类原型的属性到子类原型上。
- 适用场景:当希望避免多次调用父类构造函数带来的性能问题时。
-
function Parent(name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function() { return this.name; }; function Child(name, toy) { Parent.call(this, name); // 调用父类构造函数 this.toy = toy; // 子类特有的属性 } // 寄生组合式继承的关键部分 Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; // 创建子类实例 const child1 = new Child('Alice', 'doll'); const child2 = new Child('Bob', 'car'); // 测试 console.log(child1.getName()); // 输出: Alice console.log(child1.toy); // 输出: doll console.log(child1.colors); // 输出: ['red', 'blue', 'green'] console.log(child2.getName()); // 输出: Bob console.log(child2.toy); // 输出: car console.log(child2.colors); // 输出: ['red', 'blue', 'green']
7. ES6 类继承
- 特点:提供了更接近传统面向对象语言的语法糖,使得继承更加直观,易于理解和维护。
- 适用场景:在现代的JavaScript开发中,特别是在使用模块化和构建工具如Webpack的环境中,ES6类继承因其简洁性和标准支持而被广泛采用。
-
class Parent{ constructor(){ this.name = 'parent'; } getName(){ return this.name; } } class Child extends Parent{ constructor() { super(); this.type="child"; } } const child = new Child(); console.log(child.getName());//parent console.log(child.type);//child console.log(child.name);//parent
总的来说,选择哪种继承方式取决于项目的具体需求、代码的可维护性、性能考虑以及是否遵循现代的JavaScript最佳实践。对于新的项目,建议优先考虑使用ES6的类继承,因为它不仅语法简洁,而且得到了广泛的浏览器支持。