JavaScript 常见继承方式详解:原型链、借用构造函数与组合继承
本文深入解析 JavaScript 中的三种常见继承模式:原型链继承、借用构造函数继承以及组合继承。详细阐述各模式的实现原理、优缺点及适用场景,帮助开发者理解子类与父类的关系,掌握属性与方法继承的核心机制,为实际开发中的面向对象编程提供理论支持。
前言
在前端开发的学习路径中,继承是一个不可避免的核心概念。
从本质上看,继承描述了构造函数之间建立关联的一种机制。
具体而言,当构造函数 A 的实例能够访问并调用构造函数 B 所定义的属性和方法时,即认定构造函数 A 继承自构造函数 B。
在此语境下,通常将构造函数 A 称为子类,而将构造函数 B 称为父类。
要透彻理解继承机制,首先必须厘清原型链的工作原理。
原型链

function Person() {
// 属性
this.name = 'Jack'
this.age = 18
this.gender = '男'
}
// 实例
const p = new Person()
// 1.p 的 __proto__ 指向了谁?
console.log(p.__proto__ === Person.prototype)
// 2.Person 的 __proto__ 指向了谁 ?
console.log(Person.__proto__ === Function.prototype)
// 3.Person.prototpye 的 __proto__ 指向了谁 ?
console.log(Person.prototype.__proto__ === Object.prototype)
// 4.Function.prototype 的 __proto__ 指向了谁 ?
console.log(Function.prototype.__proto__ === Object.prototype)
// 5.Function 的 __proto__ 指向了谁 ?
console.log(Object.prototype.__proto__)
// 6.Object.prototype 的 __proto__ 指向了谁 ?
console.log(Function.__proto__ === Function.prototype)
// 7.Object 的 __proto__ 指向了谁 ?
console.log(Object.__proto__ === Function.prototype)常用的继承方法
在面试考核或实际项目开发中,最为高频使用的继承方式主要包括以下几种:
- 原型继承
- 原型链继承
- 组合继承
此外,还存在其他多种继承实现方式,但相较于上述三种主流模式,它们在实际应用中的使用频率较低。
本文重点对前述三种核心继承方式进行深入剖析。
原型继承
原理
- 原型继承的核心机制在于:将子类的原型对象(prototype)指向父类的一个实例。
- 这一过程可通俗理解为:将父类的实例赋值给子类的原型对象。
优点
- 通过这种方式,父类构造函数体内定义的属性以及原型链上挂载的方法,均可被子类实例继承。
缺点
- 继承而来的属性并非直接存在于子类实例自身,而是位于原型链上。
- 创建构造函数实例时,需要在两个不同的位置传递参数,操作较为繁琐。
- 所有由子类生成的实例,其 name 和 age 属性的值完全一致,缺乏独立性。
// 父类
function Person(name, age) {
//属性
this.name = name
this.age = age
}
// 方法
Person.prototype.paly = function () { console.log('玩游戏'); }
// 子类
function Students(classRoom) {
// 属性
this.classRoom = classRoom
}
// 把子类的原型指向父类的实例对象
// 把父类的实例赋值给子类的原型(原型对象)
// 创建一个父类的实例
const p = new Person('Jack', 25)
// 子类的实例指向父类的原型对象
Students.prototype = p
// 实例化一个对象
let s = new Students('高级1班')
console.log(s);
let s1 = new Students('高级2班')
console.log(s1);执行结果

借用构造函数继承
原理
- 该方法的核心思路是:在子类构造函数的内部,将父类构造函数作为普通函数进行调用。
- 通过调用 call 方法,强制改变函数执行时的 this 指向,使其指向子类的新实例。
优点
- 子类继承的所有属性均直接存在于实例自身,而非原型链上。
- 子类所需的所有参数只需在构造函数调用时一次性传递,逻辑清晰。
- 每个子类实例都可以为继承而来的属性赋予不同的值,实现了数据的隔离。
缺点
父类原型对象(prototype)上定义的方法无法被继承,导致方法复用性差。
// 父类
function Person(name, age) {
// 属性
this.name = name
this.age = age
}
// 原型对象上添加一个方法
Person.prototype.paly = function () { console.log('玩游戏'); }
// 子类
function Students(name, age,classRoom) {
this.classRoom = classRoom
// 因为这个this指向的就是子类的实例
Person.call(this, name, age)
}
// 实例化一个对象
let s = new Students('张三',25,'高级1班')
console.log(s);
let s1 = new Students('李四',20,'高级2班')
console.log(s1);执行结果

组合继承
原理
- 组合继承策略是将原型继承与借用构造函数继承两种模式结合使用。
优点
- 通过结合原型继承和借用构造函数继承的优势,实现更完善的继承效果。
- 既确保了所有属性和方法都能被完整继承,又保证了属性值存储在实例自身,互不干扰。
缺点
子类的原型对象上会保留一套多余的属性(来自父类实例),造成一定的内存浪费。
// 父类
function Person(name, age) {
// 属性
this.name = name
this.age = age
}
// 原型对象上添加一个方法
Person.prototype.paly = function () { console.log('玩游戏'); }
// 子类
function Students(name, age,classRoom) {
this.classRoom = classRoom
// 借用构造函数继承
Person.call(this, name, age)
}
// 原型继承
// 主要的目的就是为了使用父类身上的方法
Students.prototype = new Person()
// 实例化一个对象
let s = new Students('张三',25,'高级1班')
console.log(s);
let s1 = new Students('王五',24,'高级2班')
console.log(s1);
s1.play()执行结果

至此,关于 JavaScript 中常见继承方式的总结介绍完毕。如需了解更多 JS 相关继承知识,建议查阅本站历史文章或继续浏览后续内容,感谢大家的支持!
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
