JavaScript ES5 实现继承的4种常用方法详解

作者:拾光时间:2026-10-31 12:35:26浏览:634

本文详细解析 JavaScript ES5 中实现继承的四种常用方式。从基于原型链的原型继承,到通过构造函数绑定 this 的构造函数继承,再到结合两者的组合式继承,最后引出最优解——寄生组合式继承。文章对比了各方法的优缺点,重点阐述如何解决引用类型共享及构造函数重复执行的问题,帮助开发者深入理解 JS 面向对象编程的核心机制。

前言

继承是面向对象软件开发中的核心概念。若类别 B “继承自”类别 A,则 B 被称为 A 的子类,A 则是 B 的父类(或超类)。

继承的主要优势在于子类可直接复用父类的属性与方法,无需重复编写代码。同时,子类可在继承基础上重新定义属性或重写方法,从而覆盖父类的原有功能,实现差异化行为。

JavaScript 实现面向对象编程的方式与传统基于类的语言存在显著差异。其核心机制依赖于原型和原型链,而非传统的类结构。尽管 ES6 引入了 class 关键字,但这仅是一种语法糖,底层依然基于原型机制运作。

接下来将重点介绍 ES5 中四种常用的继承实现方式。

ES5 实现的四种方式

1. 原型链继承

原型链继承的核心逻辑是让子类的原型对象指向父类的实例。当在子类实例中查找属性或方法失败时,引擎会沿着原型链向上查找父类原型,从而继承父类的属性和方法。

// 父类
function Person() {
  this.name = 'gan'
}

Person.prototype.getName = function () {
  return this.name
}

// 子类
function Student() { }

Student.prototype = new Person()

// 根据原型链的规则,顺便绑定constructor 不影响继承
Student.prototype.constructor = Student

const student = new Student()

console.log(student.name) // gan

console.log(student.getName()) // gan

缺点

1. 所有 Student 实例共享同一个 Person 实例。若 Person 包含引用类型属性,当某个 student1 实例修改该属性时,会影响所有其他实例。

function Person() {
  this.hobby = ['篮球', '足球']
}

function Student() { }

Student.prototype = new Person()

const student1 = new Student()
const student2 = new Student()

student1.hobby.push('排球')
console.log(student1.hobby) // [ '篮球', '足球', '排球' ]
console.log(student2.hobby) // [ '篮球', '足球', '排球' ]

2. 子类实例无法向父类构造函数传递参数,即缺乏类似 super() 的功能。

2. 构造函数继承

构造函数继承通过在子类构造函数内部执行父类构造函数,并将父类构造函数的 this 绑定到子类实例上,使父类的属性和方法挂载到子类实例中。这种方式避免了多个子类实例共享同一个原型对象的问题,并支持向父类传递参数。

function Person() {
  this.name = 'gan'
}

Person.prototype.getName = function () {
  return this.name
}

function Student() {
  Person.apply(this, arguments)
}

const student = new Student()
console.log(student.name) // gan

缺点

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

console.log(student.getName) // undefined

3. 组合式继承

组合式继承结合了原型链继承和构造函数继承两种模式的优势。

function Person() {
  this.name = 'gan'
}

Person.prototype.getName = function () {
  return this.name
}

function Student() {
  Person.apply(this, arguments)
}

Student.prototype = new Person()

Student.prototype.constructor = Student

const student = new Student()
console.log(student.getName()) // gan

缺点

每次创建子类实例时,父类构造函数会被调用两次(分别通过 Person.apply 和 new Person)。这导致原型中存在两份相同的属性和方法,虽然不影响继承逻辑,但显得不够优雅。

4. 寄生式组合继承

该方法解决了执行两次父类构造函数的问题,将子类的原型指向改为直接指向父类的原型。

function Person() {
  this.name = 'gan'

}

Person.prototype.getName = function () {
  return this.name
}

function Student() {
  // 构造函数继承
  Person.apply(this, arguments)
}

// Student.prototype = new Person()
Student.prototype = Object.create(Person.prototype)

Student.prototype.constructor = Student

const student = new Student()
console.log(student.getName()) // gan

这种方法被视为目前最优的解决方案。

总结

  • 回顾继承机制,首先想到的是原型链继承:子类原型指向父类实例。但其缺陷在于子类实例处理父类引用类型时会出现共享问题,且无法向父类传递参数。
  • 随后是构造函数继承:在子类构造函数中执行父类构造函数并绑定 this。其局限在于无法继承父类原型链上的属性和方法。
  • 接着出现了组合式继承,它是前两者的结合体,但主要问题在于每次都会执行两次父类构造函数。
  • 最终衍生出寄生式组合继承,解决了上述冗余问题。

至此,关于 JavaScript ES5 实现继承的四种常用方法小结完毕。如需了解更多 JavaScript ES5 继承相关内容,请搜索本站往期文章或继续阅读后续文章,期待大家的支持!

作者最新文章