Skip to content

继承方式

JavaScript 中的继承主要有以下几种实现方式,每种方式都有其特点和适用场景。

原型链继承

核心思想:将父类的实例作为子类的原型

js
Child.prototype = new Parent();

优点

  • 简单直接
  • 所有子类实例共享父类的方法

缺点

  • 子类实例不能给父类构造函数传参
  • 原型对象包含引用类型的属性会被所有实例共享

示例

js
function Parent() {
    this.name = "parent";
}
Parent.prototype.getName = function () {
    return this.name;
};

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

const child = new Child();
console.log(child.getName()); // 'parent'

构造函数继承

核心思想:在子类构造函数中调用父类构造函数

js
function Child() {
    Parent.call(this);
}

优点

  • 子类实例可以向父类构造函数传参
  • 避免了引用类型属性被共享的问题

缺点

  • 父类方法无法复用(每次创建实例都会重新创建方法)

示例

js
function Parent(name) {
    this.name = name;
}
Parent.prototype.getName = function () {
    return this.name;
};

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

const child = new Child("child");
console.log(child.name); // 'child'

组合继承

核心思想:结合原型链继承和构造函数继承的优点

js
function Child() {
    Parent.call(this);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

优点

  • 解决了构造函数继承无法复用方法的问题
  • 子类实例可以向父类构造函数传参
  • 父类方法通过原型链被所有实例共享

缺点

  • 调用了两次父类构造函数(性能开销)

示例

js
function Parent(name) {
    this.name = name;
}
Parent.prototype.getName = function() {
    return this.name;
};

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

const child = new Child('child', 18);
console.log(child.getName()); // 'child'
console.log chestnut.age); // 18

原型继承

核心思想:Object.create() 创建一个新对象,使用现有对象作为新对象的原型

js
const parent = { name: "parent" };
const child = Object.create(parent);

特点

  • 不会调用构造函数
  • 可以实现基于原型的继承
  • ES5 新增

寄生组合继承(最优方案)

核心思想:通过寄生方式砍掉父类的示例属性,同时又保持原型链不变

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

function inheritPrototype(child, parent) {
    const prototype = Object.create(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

inheritPrototype(Child, Parent);

优点

  • 只调用一次父类构造函数
  • 原型链保持不变
  • 能够正常访问 instanceof

示例

js
function Parent(name) {
    this.name = name;
}
Parent.prototype.getName = function () {
    return this.name;
};

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

function inheritPrototype(child, parent) {
    const prototype = Object.create(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

inheritPrototype(Child, Parent);
Child.prototype.getAge = function () {
    return this.age;
};

const child = new Child("child", 18);
console.log(child.getName()); // 'child'
console.log(child instanceof Parent); // true

ES6 Class 继承

核心思想:使用 class 关键字和 extends 实现继承

js
class Child extends Parent {
    constructor() {
        super();
    }
}

优点

  • 语法更简洁、更符合面向对象编程习惯
  • 内置支持 extends 和 super
  • constructor 中的 super() 相当于 Parent.call(this)

示例

js
class Parent {
    constructor(name) {
        this.name = name;
    }
    getName() {
        return this.name;
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    getAge() {
        return this.age;
    }
}

const child = new Child("child", 18);
console.log(child.getName()); // 'child'
console.log(child instanceof Parent); // true

继承方式对比

方式调用构造函数次数方法复用能传参缺点
原型链继承1引用类型共享
构造函数继承多次方法不复用
组合继承2性能开销
寄生组合继承1写法复杂
ES6 Class1需要编译支持