MST

星途 面试题库

面试题:JavaScript中实现子类继承的常见方式有哪些及它们的区别

请详细阐述JavaScript中通过原型链、构造函数借用以及ES6的class关键字实现子类继承的具体方法,并说明它们在继承属性和方法、原型链结构等方面的区别。
24.4万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

1. 通过原型链实现子类继承

具体方法

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

function Child() {
    this.age = 10;
}
// 将Child的原型指向Parent的实例,这样Child的实例就可以访问Parent原型上的属性和方法
Child.prototype = new Parent(); 
Child.prototype.constructor = Child; // 修正构造函数指向

let child = new Child();
child.sayName(); // 输出 'parent'

继承属性和方法

  • 继承属性:实例可访问父类原型上的属性,但无法直接访问父类实例属性。
  • 继承方法:子类实例可以继承父类原型上的方法,方法定义在父类原型上,所有子类实例共享这些方法。

原型链结构

Child的原型是Parent的实例,Child实例的__proto__指向Child的原型(即Parent的实例),Parent实例的__proto__指向Parent的原型,形成一条原型链。

2. 通过构造函数借用实现子类继承

具体方法

function Parent(name) {
    this.name = name;
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 在Child构造函数中调用Parent构造函数,将Parent的属性绑定到Child实例上
    this.age = age;
}
// Child.prototype = Object.create(Parent.prototype); // 可选择性添加,以便访问父类原型方法
// Child.prototype.constructor = Child;

let child = new Child('childName', 10);
// child.sayName(); // 如果没有设置Child.prototype = Object.create(Parent.prototype),此行会报错

继承属性和方法

  • 继承属性:子类实例可以拥有与父类实例相同的属性,因为在子类构造函数中调用了父类构造函数,将父类属性绑定到了子类实例上。
  • 继承方法:默认情况下,子类实例无法访问父类原型上的方法,除非手动设置Child.prototype = Object.create(Parent.prototype)

原型链结构

Child的原型与Parent的原型没有直接联系(若不手动设置Child.prototype = Object.create(Parent.prototype)),Child实例的__proto__指向Child的原型,而不是Parent的实例或原型。

3. 通过ES6 class关键字实现子类继承

具体方法

class Parent {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(this.name);
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name); // 调用父类构造函数,初始化继承的属性
        this.age = age;
    }
}

let child = new Child('childName', 10);
child.sayName(); // 输出 'childName'

继承属性和方法

  • 继承属性:子类通过super关键字调用父类构造函数来继承父类的属性,子类实例拥有父类实例的属性。
  • 继承方法:子类可以继承父类的方法,并且可以重写父类方法。如果子类定义了与父类相同的方法,会覆盖父类方法。

原型链结构

ES6 class本质上还是基于原型链的,Child的原型是Parent原型的一个实例,Child实例的__proto__指向Child的原型,Child原型的__proto__指向Parent的原型,形成原型链。

区别总结

  • 继承属性
    • 原型链继承:无法直接继承父类实例属性,只能访问父类原型属性。
    • 构造函数借用:可继承父类实例属性,但默认无法访问父类原型方法(需额外设置)。
    • ES6 class继承:通过super调用父类构造函数继承属性,可直接访问父类实例和原型属性及方法。
  • 继承方法
    • 原型链继承:共享父类原型方法。
    • 构造函数借用:默认无法继承,需手动设置原型关系。
    • ES6 class继承:可继承并可重写父类方法。
  • 原型链结构
    • 原型链继承:子类原型是父类实例,形成原型链。
    • 构造函数借用:默认情况下子类原型与父类原型无直接联系(不手动设置时)。
    • ES6 class继承:基于原型链,子类原型是父类原型的实例,形成原型链。