面试官:说一下Javascript如何实现继承?

文摘   2024-11-18 10:17   山西  

今天聊一个挺经典的面试题——“JavaScript 如何实现继承?”嗯,作为程序员,我倒是挺喜欢这种问题的,因为它不仅考察了基础知识的掌握程度,还能看你是否能够在实际开发中灵活运用这些知识点。毕竟在 JavaScript 这个语言里,继承可不单单是类的那点事,灵活度可是特别高的。

在我看来,继承是面向对象编程中一个非常重要的概念。简单来说,它允许一个对象“继承”另一个对象的属性和方法。就像我们在生活中,经常听到“父母遗传了孩子的眼睛”这样的说法,其实它和继承的概念是类似的。

如果有个类(比如“汽车”),然后通过继承,它的子类(比如“轿车”或“货车”)就可以自动继承父类的属性和方法,而且还可以有自己的特色。接下来,我就通过几个例子,给大家讲解一下 JavaScript 中的继承方式。

首先来简单回顾一下继承的基本概念。假设我们有一个“汽车”类,这个类有两个属性:颜色和速度,接着我们定义两个子类——“轿车”和“货车”。

“轿车”和“货车”会继承“汽车”的属性(颜色、速度),但是它们还会有自己的独特属性,比如“轿车”有一个后备厢,“货车”有一个大货箱。

// 基类:汽车
class Car {
  constructor(color, speed) {
    this.color = color;
    this.speed = speed;
  }
}

// 子类:货车
class Truck extends Car {
  constructor(color, speed) {
    super(color, speed); // 调用父类构造函数
    this.container = true// 新增属性
  }
}

在这个例子中,Truck 继承了 Car 的属性,还能为 Truck 添加自己的属性,比如 container。这是最基础的继承方式,但是,JavaScript 中的继承可不仅仅是这样,还有很多种实现方式。接下来,我们就来看看这些实现方式,尤其是当面试官问到这个问题时,你可以从哪几个方面展开讨论。

1. 原型链继承

原型链继承是最常见的一种实现方式。其原理是通过设置一个对象的原型为另一个对象的实例,从而让一个对象继承另一个对象的属性和方法。可以这么理解:当我们在子类中访问某个属性时,如果子类本身没有这个属性,它就会去父类的原型对象中找,直到找到为止。

function Parent({
  this.name = 'parent';
  this.play = [123];
}

function Child({
  this.type = 'child';
}

Child.prototype = new Parent();

var child1 = new Child();
child1.play.push(4);

var child2 = new Child();
console.log(child2.play); // [1, 2, 3, 4]

这里有个问题,虽然 child1 继承了 Parent,但由于 child1child2 都共享同一个原型对象,因此 child1 修改了 play 属性后,child2 也会受到影响。这是原型链继承的一大缺点。

2. 构造函数继承(借用构造函数)

为了解决原型链继承的问题,我们可以使用构造函数继承,也就是通过在子类构造函数内部调用父类构造函数。这样,子类会有自己的属性,而不会与其他实例共享。

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

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

function Child({
  Parent.call(this); // 借用父类构造函数
  this.type = 'child';
}

var child = new Child();
console.log(child.name); // parent
console.log(child.getName()); // 错误:getName 没有继承

在这个例子中,Child 调用了 Parent.call(this),从而继承了父类的实例属性,但父类的原型方法并没有被继承。也就是说,Child 可以访问父类的属性(比如 name),但无法访问父类原型上的方法(比如 getName())。这种方式的缺点是无法继承父类的原型方法。

3. 组合继承

组合继承将原型链继承和构造函数继承结合起来,它既能继承父类的实例属性,也能继承父类的原型方法。这样我们就能避免原型链继承中的属性共享问题,同时也能继承父类的原型方法。

function Parent({
  this.name = 'parent';
  this.play = [123];
}

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

function Child({
  Parent.call(this); // 继承实例属性
  this.type = 'child';
}

Child.prototype = new Parent(); // 继承原型方法
Child.prototype.constructor = Child; // 修正 constructor 指向

var child1 = new Child();
var child2 = new Child();
child1.play.push(4);
console.log(child1.play); // [1, 2, 3, 4]
console.log(child2.play); // [1, 2, 3]
console.log(child1.getName()); // parent

组合继承的优势在于,它解决了原型链继承和构造函数继承的缺陷。每个实例有自己的属性,原型方法被共享。唯一的问题是 Parent 构造函数会被调用两次——一次是在 Child 的构造函数中调用,另一次是在设置 Child.prototype 时调用。虽然这个性能开销不大,但如果父类构造函数非常复杂,可能会成为问题。

4. 原型式继承

原型式继承是一种通过 Object.create() 方法来实现继承的方式。它的原理是创建一个新对象,这个对象的原型指向父对象。相比于传统的继承方式,原型式继承更加简洁。

let parent = {
  name'parent',
  friends: ['p1''p2'],
};

let child = Object.create(parent);
child.name = 'child';
child.friends.push('p3');

console.log(child.name); // child
console.log(child.friends); // ['p1', 'p2', 'p3']

这种方式的问题在于,friends 是引用类型,所以 childparent 会共享 friends 数组。如果一个实例修改了 friends,另外一个实例也会受到影响。这是因为它们共享相同的内存地址。

5. 寄生式继承

寄生式继承是在原型式继承的基础上,通过扩展或者增强对象的方法和属性来实现。其优点是可以在继承时给新对象添加一些额外的功能。

let parent = {
  name'parent',
  friends: ['p1''p2'],
};

function createChild(parentObj{
  let child = Object.create(parentObj);
  child.getFriends = function({
    return this.friends;
  };
  return child;
}

let child = createChild(parent);
child.friends.push('p3');
console.log(child.getFriends()); // ['p1', 'p2', 'p3']

通过这种方式,createChild 创建了一个继承自 parent 的新对象,并给它添加了 getFriends 方法。这种方法是对原型式继承的优化,可以避免引用类型的问题,但它依然存在对象共享的缺陷。

JavaScript 继承的方式有很多,每种方式都有其适用场景。

原型链继承简单,但是容易导致属性共享的问题;构造函数继承解决了这个问题,但无法继承父类的原型方法;组合继承是比较常见的做法,能兼顾两者的优点;原型式继承和寄生式继承则提供了一种更加灵活的方式,在某些场景下非常有用。最终,你应该根据项目的需求来选择合适的继承方式。

这就是面试官可能问的继承问题,你可以从基础概念到不同实现方式全方位地展示你的知识深度。希望大家下次面试碰到这个问题时,能一口气讲出来!

目前,对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。

虎哥私藏精品 热门推荐

虎哥作为一名老码农,整理了全网最前端资料合集

资料包含了《前端面试题PDF合集》、《前端学习视频》、《前端项目及源码》,总量高达108GB。

全部免费领取全面满足各个阶段程序员的学习需求!

web前端专栏
回复 javascript,获取前端面试题。分享前端教程,AI编程,AI工具,Tailwind CSS,Tailwind组件,javascript教程,webstorm教程,html教程,css教程,nodejs教程,vue教程。
 最新文章