js中的继承

525 阅读2分钟

JavaScript 中的继承主要基于原型链(Prototype Chain),而不是传统的类继承(如 Java 或 C++)。简单来说,就是让子类能访问到父类的属性和方法。以下是 JavaScript 继承的几种主要实现方式及详细说明:

1. 原型链继承

核心思想:通过将子类的原型对象指向父类的实例,实现属性和方法的继承。

Parent.prototype.say = function () {
    console.log('hello')
}

function Parent(age) {
    this.name = 'parent';
    this.age = age
}

Child.prototype = new Parent()  // {name: 'parent', age: 50}.__proto__ === Parent.prototype
function Child(name) {
    this.name = name
}

const c = new Child('child')   // c.__proto__ === Child.prototype
console.log(c.name);  // child
console.log(c.age);  // 50
c.say()  // hello

问题

  • 引用类型的属性会被所有子类实例共享(如数组、对象)。
  • 无法在创建子类实例时向父类构造函数传参。

2. 借用构造函数继承

核心思想:在子类构造函数中调用父类构造函数,通过 call 或 apply 绑定 this

Parent.prototype.say = function () {
    console.log('hello')
}

function Parent(age) {
    this.name = 'parent';
    this.age = age
}

function Child(name, age) {
    Parent.call(this, age)
    // 相当于:
    // this.name = 'parent';
    // this.age = age
    this.name = name
}

const c = new Child('child', 50)
console.log(c);


console.log(c.name);  // child
console.log(c.age);  // 50
// c.say()  // hello

优点

  • 解决引用类型属性共享问题。
  • 支持向父类构造函数传参。

缺点

  • 无法继承父类原型上的方法(只能继承实例属性)。

3. 组合继承

核心思想:结合原型链继承和构造函数继承,是 JavaScript 中最常用的继承模式。

Parent.prototype.say = function () {
    console.log('hello')
}

function Parent(age) {
    this.name = 'parent';
    this.age = age
}

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

const c = new Child('child', 50)


console.log(c.name);  // child
console.log(c.age);  // 50
c.say()  // hello

优点

  • 实例属性独立,原型方法共享。
  • 支持传参。

缺点

  • 父类构造函数被调用两次,导致子类原型中存在冗余属性。

4. 原型式继承

核心思想:基于已有对象创建新对象,通过 Object.create() 实现。

const obj = {
    name: '张三',
    age: 40
}

let newObj = Object.create(obj)

console.log(newObj.name);

特点

  • 适用于不需要构造函数的场景。
  • 类似原型链继承的引用类型共享问题。

5. 寄生组合继承

核心思想:最优解决方案,通过借用构造函数继承属性,通过原型链混成形式继承方法。

Parent.prototype.say = function () {
    console.log('hello')
}
function Parent(age) {
    this.name = 'parent';
    this.age = age
}

Child.prototype = Object.create(Parent.prototype)  // {}
Child.prototype.constructor = Child  // 修正构造函数
function Child(name, age) {
    Parent.call(this, age)
    this.name = name
}

const c = new Child('child', 50)

// console.log(c.name);
// console.log(c.age);
// c.say()

console.log(c.constructor);

优点

  • 只调用一次父类构造函数。
  • 避免子类原型上创建冗余属性。
  • 原型链保持不变,支持 instanceof 操作符。

6. class继承: extends + super()

核心思想:语法糖,底层基于寄生组合式继承。

class Parent {
    constructor(name, age) {  // 构造函数
        this.name = name
        this.age = age
    }
    say() {
        console.log('hello');
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name, age)  // 必须调用 super()
        this.sex = 'boy'
    }
}

const c = new Child('张三', 18)
c.say()

特点

  • 使用 extends 继承父类。
  • super 调用父类构造函数或方法。
  • 支持静态方法继承(static 关键字)。