js中如何实现继承

js中如何实现继承
最新回答
只想说再见╮

2021-02-19 08:52:21

在 JavaScript 中,实现继承的核心方式包括原型链继承构造函数继承组合继承原型式继承寄生式继承寄生组合式继承。以下是具体实现方法及优缺点分析:

1. 原型链继承

通过将子类型的原型指向父类型的实例实现继承。

function Parent() { this.name = 'Parent'; this.colors = ['red', 'blue', 'green'];}Parent.prototype.sayName = function() { console.log(this.name);};function Child() { this.childName = 'Child';}Child.prototype = new Parent(); // 关键:子类型原型指向父类型实例Child.prototype.constructor = Child; // 修正constructor指向let child1 = new Child();child1.colors.push('black');let child2 = new Child();console.log(child2.colors); // ["red", "blue", "green", "black"]
  • 优点:实现简单,父类方法可复用。
  • 缺点

    子类型实例共享父类型原型上的引用类型属性(如colors数组)。

    无法向父类构造函数传递参数。

2. 构造函数继承(经典继承)

通过在子类型构造函数中调用父类型构造函数实现继承。

function Parent(name) { this.name = name; this.colors = ['red', 'blue', 'green']; this.sayName = function() { console.log(this.name); };}function Child(name) { Parent.call(this, name); // 关键:调用父类型构造函数 this.childName = 'Child';}let child1 = new Child('Child1');child1.colors.push('black');let child2 = new Child('Child2');console.log(child2.colors); // ["red", "blue", "green"]
  • 优点

    子类型实例拥有独立的属性(如colors数组)。

    可向父类构造函数传递参数。

  • 缺点

    父类型方法定义在构造函数中,每次创建实例都会重新创建方法,无法复用。

    无法访问父类型原型上的属性和方法。

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;let child1 = new Child('Child1', 10);child1.colors.push('black');let child2 = new Child('Child2', 12);console.log(child2.colors); // ["red", "blue", "green"]
  • 优点

    父类型方法可复用。

    子类型实例拥有独立属性。

    可向父类构造函数传递参数。

  • 缺点:父类构造函数被调用两次(设置子类型原型和创建实例时),导致性能浪费。
4. 原型式继承

基于现有对象创建新对象,适用于无需自定义类型的场景。

let person = { name: 'Person', friends: ['Shelby', 'Court', 'Van']};let anotherPerson = Object.create(person); // 关键:基于person创建新对象anotherPerson.name = 'Greg';anotherPerson.friends.push('Rob');console.log(person.friends); // ["Shelby", "Court", "Van", "Rob"]
  • 优点:实现简单,无需创建构造函数。
  • 缺点:子对象共享父对象的引用类型属性(如friends数组)。
5. 寄生式继承

在原型式继承基础上增强对象,适合临时扩展功能。

function createAnother(original) { let clone = Object.create(original); // 创建新对象 clone.sayHi = function() { // 增强对象 console.log('hi'); }; return clone;}let person = { name: 'Person', friends: ['Shelby', 'Court', 'Van']};let anotherPerson = createAnother(person);anotherPerson.sayHi(); // "hi"
  • 优点:可灵活扩展对象功能。
  • 缺点:每次都会创建新方法,无法复用。
6. 寄生组合式继承(最理想方式)

通过继承父类原型的副本来避免构造函数重复调用。

function inheritPrototype(child, parent) { let prototype = Object.create(parent.prototype); // 创建父类原型副本 prototype.constructor = child; // 修正constructor指向 child.prototype = prototype; // 赋值给子类原型}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;}inheritPrototype(Child, Parent); // 关键:继承父类原型副本let child1 = new Child('Child1', 10);child1.colors.push('black');let child2 = new Child('Child2', 12);console.log(child2.colors); // ["red", "blue", "green"]
  • 优点

    避免父类构造函数重复调用,性能最优。

    结合了原型链继承和构造函数继承的优点。

  • 缺点:实现相对复杂。
继承方式选择建议
  • 简单场景:原型链继承(需复用父类方法)。
  • 需传递参数:构造函数继承(不关心方法复用)。
  • 完善继承:寄生组合式继承(性能最优,推荐使用)。
  • 无需自定义类型:原型式继承或寄生式继承(快速创建对象副本)。

根据需求选择合适的继承方式,可平衡代码简洁性、性能和可维护性。