JavaScript 常见继承方式详解:原型链、借用构造函数与组合继承

作者:闭包旅人时间:2026-10-25 20:36:50浏览:564

本文深入解析 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 相关继承知识,建议查阅本站历史文章或继续浏览后续内容,感谢大家的支持!

作者最新文章