MST
星途 面试题库

面试题:JavaScript中继承的常见实现方式及其优缺点

请详细阐述JavaScript中至少三种继承的实现方式,并分析每种方式的优缺点。
13.0万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

原型链继承

实现方式

function Parent() {
    this.name = 'parent';
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};
function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
let child = new Child();
child.sayName(); 

优点

  1. 非常纯粹的继承关系,通过原型链实现,结构清晰。
  2. 父类新增原型方法/原型属性,子类都能访问到。

缺点

  1. 所有子类实例共享父类的引用类型属性,一个实例对引用类型属性的修改会影响其他实例。
  2. 创建子类实例时,无法向父类构造函数传参。

构造函数继承(经典继承)

实现方式

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}
let child1 = new Child('child1', 5);
let child2 = new Child('child2', 6);
child1.colors.push('green');
console.log(child1.colors); 
console.log(child2.colors); 

优点

  1. 可以在子类构造函数中向父类构造函数传参。
  2. 解决了子类实例共享父类引用类型属性的问题。

缺点

  1. 只能继承父类的实例属性和方法,不能继承原型属性/方法。
  2. 每创建一个子类实例,父类构造函数都会被调用一次,造成性能开销。

组合继承(伪经典继承)

实现方式

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};
function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
let child = new Child('child', 5);
child.sayName(); 

优点

  1. 融合了原型链继承和构造函数继承的优点,既能继承实例属性和方法,又能继承原型属性和方法。
  2. 可以向父类构造函数传参。

缺点

  1. 调用了两次父类构造函数,一次在Child.prototype = new Parent(),一次在Parent.call(this, name),有一定性能浪费。

寄生组合式继承

实现方式

function createObject(o) {
    function F() {}
    F.prototype = o;
    return new F();
}
function inheritPrototype(Child, Parent) {
    let prototype = createObject(Parent.prototype);
    prototype.constructor = Child;
    Child.prototype = prototype;
}
function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};
function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}
inheritPrototype(Child, Parent);
let child = new Child('child', 5);
child.sayName(); 

优点

  1. 只调用一次父类构造函数,避免了组合继承中两次调用父类构造函数的性能浪费。
  2. 完美实现继承,既继承实例属性和方法,又继承原型属性和方法。

缺点

  1. 实现相对复杂,需要额外的辅助函数。

ES6 class 继承

实现方式

class Parent {
    constructor(name) {
        this.name = name;
        this.colors = ['red', 'blue'];
    }
    sayName() {
        console.log(this.name);
    }
}
class Child extends Parent {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
}
let child = new Child('child', 5);
child.sayName(); 

优点

  1. 语法更加简洁、清晰,易于理解和维护。
  2. 融合了原型链继承和构造函数继承的优点,既能继承实例属性和方法,又能继承原型属性和方法。
  3. 可以方便地使用super关键字调用父类的构造函数和方法。

缺点

  1. 本质上还是基于原型链和构造函数的继承,并没有完全摆脱传统继承方式的一些固有问题。
  2. 对于不支持ES6的环境,需要进行转码。