JavaScript实现继承的6种常用方式总结

作者:小括号

时间:2026-10-25

浏览:252

本文总结了JavaScript中实现继承的6种常用方式,包括原型链继承、借用构造函数继承、组合继承、原型式继承、寄生式继承以及寄生组合式继承,并介绍了ES6 Class的实现机制。每种方式都分析了其优缺点及适用场景,帮助开发者根据实际需求选择合适的继承方案。

文章目录

在JavaScript中,实现继承的核心目的是复用另一个对象的属性和方法。

1. 原型链继承

通过让一个构造函数的原型指向另一个类型的实例,使得该构造函数new出来的实例拥有该实例的属性。

当访问对象的属性时,JavaScript引擎会沿原型链向上搜索,依次检查该对象的原型、原型的原型,直到找到匹配的属性或到达原型链末尾。

function Parent() {
   this.isShow = true
   this.info = {
       name: "mjy",
       age: 18,
   };
}

Parent.prototype.getInfo = function() {
   console.log(this.info);
   console.log(this.isShow);
}

function Child() {};
Child.prototype = new Parent();

let Child1 = new Child();
Child1.info.gender = "男";
Child1.getInfo(); // {name: 'mjy', age: 18, gender: '男'} ture

let child2 = new Child();
child2.isShow = false
console.log(child2.info.gender) // 男
child2.getInfo(); // {name: 'mjy', age: 18, gender: '男'} false

优点:实现方式简洁直观,易于理解。

缺点:所有实例共享继承的属性和方法;创建子类型实例时无法传递参数,因为对象是一次性创建的,缺乏定制化能力。

2. 借用构造函数继承

function Parent(gender) {
  this.info = {
    name: "yhd",
    age: 19,
    gender: gender
  }
}

function Child(gender) {
    Parent.call(this, gender)
}

let child1 = new Child('男');
child1.info.nickname = 'xiaoma'
console.log(child1.info);

let child2 = new Child('女');
console.log(child2.info);

在子类型构造函数内部调用父类型构造函数,利用apply()或call()方法将父对象的构造函数绑定到子对象上。

优点:解决了原型链继承中无法传参和父类原型共享的问题。

缺点:方法必须在构造函数内定义,导致无法实现函数复用;父类型原型中定义的方法对子类型不可见,所有类型只能使用构造函数模式。

3. 组合继承(经典继承)

结合原型链和借用构造函数:使用原型链继承原型属性和方法,通过借用构造函数继承实例属性。这样既实现了函数复用,又保证每个实例拥有自己的属性。

function Person(gender) {
  console.log('执行次数');
  this.info = {
    name: "mjy",
    age: 19,
    gender: gender
  }
}

Person.prototype.getInfo = function () {   // 使用原型链继承原型上的属性和方法
  console.log(this.info.name, this.info.age)
}

function Child(gender) {
  Person.call(this, gender) // 使用构造函数法传递参数
}

Child.prototype = new Person()

let child1 = new Child('男');
child1.info.nickname = 'xiaoma'
child1.getInfo()
console.log(child1.info);

let child2 = new Child('女');
console.log(child2.info);

优点:解决了原型链继承和借用构造函数继承各自的局限性。

缺点:无论何种情况,都会调用两次超类型构造函数:一次在创建子类型原型时,另一次在子类型构造函数内部。

4. 原型式继承

方法一:借用构造函数

在函数A内部创建一个临时构造函数,将传入对象作为其原型,最后返回该临时类型的新实例。本质上是对传入对象执行了一次浅复制。

function createObject(obj) {
  function Fun() {}
  Fun.prototype = obj
  return new Fun()
}

let person = {
  name: 'mjy',
  age: 18,
  hoby: ['唱', '跳'],
  showName() {
    console.log('my name is:', this.name)
  }
}

let child1 = createObject(person)
child1.name = 'xxxy'
child1.hoby.push('rap')
let child2 = createObject(person)

console.log(child1)
console.log(child2)
console.log(person.hoby) // ['唱', '跳', 'rap']

方法二:Object.create()

Object.create()将现有对象的属性挂到新建对象的原型上,新建对象本身为空对象。

ECMAScript 5通过Object.create()方法规范化了原型式继承。该方法接收两个参数:作为新对象原型的对象,以及定义额外属性的对象(第二个可选)。仅有一个参数时,效果与函数A方法相同。

let person = {
  name: 'mjy',
  age: 19,
  hoby: ['唱', '跳'],
  showName() {
    console.log('my name is: ', this.name)
  }
}

let child1 = Object.create(person)
child1.name = 'xxt'
child1.hoby.push('rap')
let child2 = Object.create(person)

console.log(child1)
console.log(child2)
console.log(person.hoby) // ['唱', '跳', 'rap']

优点:无需单独创建构造函数。

缺点:属性中包含的引用值始终在相关对象间共享,子类实例无法向父类传参。

5. 寄生式继承

思路与原型式继承和工厂模式类似:创建一个封装继承过程的函数,内部以某种方式增强对象,最后返回对象,仿佛它完成了所有工作。

function objectCopy(obj) {
  function Fun() { };
  Fun.prototype = obj;
  return new Fun();
}

function createAnother(obj) {
  let clone = objectCopy(obj);
  clone.showName = function () {
    console.log('my name is:', this.name);
  };
  return clone;
}

let person = {
     name: "mjy",
     age: 18,
     hoby: ['唱', '跳']
}

let child1 = createAnother(person);
child1.hoby.push("rap");
console.log(child1.hoby); // ['唱', '跳', 'rap']
child1.showName(); // my name is: mjy

let child2 = createAnother(person);
console.log(child2.hoby); // ['唱', '跳', 'rap']

优点:写法简单,无需单独创建构造函数。

缺点:通过寄生式继承添加函数会导致难以重用,因无法实现函数复用而降低效率,这一点与构造函数模式类似。

6. 寄生组合式继承

前文提及,组合继承为经典且常用的继承模式,但其核心缺陷在于父类构造函数会被调用两次:一次发生于子类型实例化过程中,另一次发生于子类型构造函数内部执行时。寄生组合继承旨在优化此过程,以降低调用父类构造函数的开销。

通过借用构造函数继承属性,通过原型链混成形式继承方法。本质上是用寄生式继承继承超类型原型,再将结果指定给子类型原型。

function objectCopy(obj) {
  function Fun() { };
  Fun.prototype = obj;
  return new Fun();
}

function inheritPrototype(child, parent) {
  let prototype = objectCopy(parent.prototype);
  prototype.constructor = child;
  Child.prototype = prototype;
}

function Parent(name) {
  this.name = name;
  this.hoby = ['唱', '跳']
}

Parent.prototype.showName = function () {
  console.log('my name is:', this.name);
}

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);
Child.prototype.showAge = function () {
  console.log('my age is:', this.age);
}

let child1 = new Child("mjy", 18);
child1.showAge(); // 18
child1.showName(); // mjy
child1.hoby.push("rap");
console.log(child1.hoby); // ['唱', '跳', 'rap']

let child2 = new Child("yl", 18);
child2.showAge(); // 18
child2.showName(); // yl
console.log(child2.hoby); // ['唱', '跳']

优点:高效率,只调用一次父构造函数,避免在子原型上创建不必要、多余的属性;同时保持原型链不变。

缺点:代码复杂度较高。

7. ES6、Class实现继承

ES5的继承实质是先创造子类的实例对象this,再将父类方法添加到this上(Parent.apply(this))。ES6的继承机制不同,实质是先将父类实例对象的属性和方法加到this上(因此必须先调用super方法),再用子类构造函数修改this。

优点:语法简单易懂,操作更便捷。

缺点:并非所有浏览器都支持class关键字。

以上就是JavaScript实现继承的6种常用方式总结的详细内容,更多关于JavaScript继承的资料请关注本站其它相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章