JavaScript是怎样实现继承的

49 阅读3分钟

JavaScript是怎样实现继承的

JavaScript 是一种基于原型的语言,它的继承机制与传统的基于类的语言(如 Java、C++)不同。JavaScript 通过原型链实现继承,同时 ES6 引入了 class 语法糖,使得继承更加直观。以下是 JavaScript 实现继承的几种方式:

1. 原型链继承

  • 原理

    • 通过将子类的原型指向父类的实例,实现继承。
function Parent() {
  this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
  console.log('Hello from ' + this.name);
};

function Child() {
  this.name = 'Child';
}
Child.prototype = new Parent(); // 继承

const child = new Child();
child.sayHello(); // 输出: Hello from Child
  • 优点

    • 简单易用。
  • 缺点

    • 所有子类实例共享父类实例的属性,可能导致数据污染。

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

2. 构造函数继承

  • 原理

    • 在子类构造函数中调用父类构造函数,使用 callapply 方法。
function Parent(name) {
  this.name = name;
}
Parent.prototype.sayHello = function() {
  console.log('Hello from ' + this.name);
};

function Child(name) {
  Parent.call(this, name); // 继承属性
}

const child = new Child('Child');
console.log(child.name); // 输出: Child
// child.sayHello(); // 报错,无法继承父类原型方法
  • 优点

    • 可以解决原型链继承中共享属性问题。

    • 可以向父类构造函数传参。

  • 缺点

    • 无法继承父类原型上的方法。

3. 组合继承

  • 原理

    • 结合原型链继承和构造函数继承,既能继承属性,又能继承方法。
function Parent(name) {
  this.name = name;
}
Parent.prototype.sayHello = function() {
  console.log('Hello from ' + this.name);
};

function Child(name) {
  Parent.call(this, name); // 继承属性
}
Child.prototype = new Parent(); // 继承方法

const child = new Child('Child');
child.sayHello(); // 输出: Hello from Child
  • 优点

    • 既能继承属性,又能继承方法。
  • 缺点

    • 调用了两次父类构造函数,性能开销较大。

4. 原型式继承

  • 原理

    • 基于一个现有对象创建新对象,使用 Object.create() 方法。
const parent = {
  name: 'Parent',
  sayHello() {
    console.log('Hello from ' + this.name);
  }
};

const child = Object.create(parent);
child.name = 'Child';
child.sayHello(); // 输出: Hello from Child
  • 优点

    • 简单灵活。
  • 缺点

    • 所有子类实例共享父类属性,可能导致数据污染。

5. 寄生式继承

  • 原理

    • 在原型式继承的基础上,增强对象的功能。
function createChild(parent) {
  const child = Object.create(parent);
  child.sayHi = function() {
    console.log('Hi from ' + this.name);
  };
  return child;
}

const parent = {
  name: 'Parent',
  sayHello() {
    console.log('Hello from ' + this.name);
  }
};

const child = createChild(parent);
child.name = 'Child';
child.sayHello(); // 输出: Hello from Child
child.sayHi(); // 输出: Hi from Child
  • 优点

    • 可以增强对象功能。
  • 缺点

    • 无法复用方法,每个对象都会创建新方法。

6. 寄生组合式继承

  • 原理

    • 结合组合继承和寄生式继承,解决组合继承的性能问题。
function inheritPrototype(child, parent) {
  const prototype = Object.create(parent.prototype); // 创建父类原型的副本
  prototype.constructor = child; // 修复构造函数指向
  child.prototype = prototype; // 将副本赋值给子类原型
}

function Parent(name) {
  this.name = name;
}
Parent.prototype.sayHello = function() {
  console.log('Hello from ' + this.name);
};

function Child(name) {
  Parent.call(this, name); // 继承属性
}
inheritPrototype(Child, Parent); // 继承方法

const child = new Child('Child');
child.sayHello(); // 输出: Hello from Child
  • 优点

    • 只调用一次父类构造函数,性能最优。

    • 既能继承属性,又能继承方法。

  • 缺点

    • 实现较为复杂。

7. ES6 的 class 继承

  • 原理

    • 使用 classextends 关键字实现继承。
class Parent {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    console.log('Hello from ' + this.name);
  }
}

class Child extends Parent {
  constructor(name) {
    super(name); // 调用父类构造函数
  }
}

const child = new Child('Child');
child.sayHello(); // 输出: Hello from Child
  • 优点

    • 语法简洁,易于理解。

    • 底层实现基于寄生组合式继承,性能最优。

  • 缺点

    • 需要支持 ES6 的环境。

总结

继承方式优点缺点
原型链继承简单易用共享属性,无法传参
构造函数继承可以传参,解决共享属性问题无法继承父类原型方法
组合继承既能继承属性,又能继承方法调用两次父类构造函数,性能开销大
原型式继承简单灵活共享属性,可能导致数据污染
寄生式继承可以增强对象功能无法复用方法
寄生组合继承性能最优,既能继承属性又能继承方法实现复杂
ES6 class语法简洁,性能最优需要支持 ES6 的环境

根据具体需求选择合适的继承方式,可以提高代码的可维护性和性能。

更多vue相关插件及后台管理模板可访问vue admin reference,代码详情请访问github