深入理解JavaScript面向对象继承机制——以frontend-hard-mode-interview项目为例

深入理解JavaScript面向对象继承机制——以frontend-hard-mode-interview项目为例

frontend-hard-mode-interview 《前端内参》,有关于JavaScript、编程范式、设计模式、软件开发的艺术等大前端范畴内的知识分享,旨在帮助前端工程师们夯实技术基础以通过一线互联网企业技术面试。 frontend-hard-mode-interview 项目地址: https://gitcode.com/gh_mirrors/fr/frontend-hard-mode-interview

前言

面向对象编程(OOP)是JavaScript中非常重要的编程范式,而继承则是OOP的核心概念之一。本文将以frontend-hard-mode-interview项目中的内容为基础,全面解析JavaScript中实现继承的六种方式,帮助开发者深入理解JavaScript的继承机制。

JavaScript继承的本质

在JavaScript中,继承主要通过原型链(prototype chain)来实现。每个对象都有一个内部属性[[Prototype]](可通过__proto__访问),当访问一个对象的属性时,如果该对象本身没有这个属性,JavaScript引擎会沿着原型链向上查找。

六种继承方式详解

1. 类式继承(原型链继承)

类式继承是最基本的继承方式,通过将子类的原型指向父类的实例来实现。

function Parent() {
  this.parentProperty = true;
}
Parent.prototype.getParentValue = function() {
  return this.parentProperty;
};

function Child() {
  this.childProperty = false;
}

// 关键步骤:继承实现
Child.prototype = new Parent();

const instance = new Child();
console.log(instance.getParentValue()); // true

优点

  • 实现简单直观
  • 子类可以访问父类原型上的方法

缺点

  • 父类实例属性成为子类原型属性,所有子类实例共享这些属性
  • 创建子类实例时无法向父类构造函数传参

适用场景:适合方法继承但不适合属性继承的场景

2. 构造函数继承

通过在子类构造函数中调用父类构造函数实现继承。

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

function Child(name) {
  Parent.call(this, name); // 关键调用
}

const child1 = new Child('child1');
child1.colors.push('green');

const child2 = new Child('child2');
console.log(child2.colors); // ['red', 'blue']

优点

  • 解决了引用类型共享问题
  • 可以向父类传递参数

缺点

  • 无法继承父类原型上的方法
  • 方法都在构造函数中定义,无法复用

适用场景:适合属性继承但不适合方法继承的场景

3. 组合继承

结合类式继承和构造函数继承的优点。

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); // 第二次调用Parent
  this.age = age;
}

Child.prototype = new Parent(); // 第一次调用Parent
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
  console.log(this.age);
};

const child = new Child('Tom', 10);
child.sayName(); // Tom
child.sayAge(); // 10

优点

  • 融合了两种继承方式的优点
  • 实例属性私有,共享方法

缺点

  • 父类构造函数被调用两次
  • 子类原型上会有多余的父类实例属性

适用场景:通用场景下的继承实现

4. 原型式继承

基于已有对象创建新对象,不自定义类型。

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

const person = {
  name: 'Nicholas',
  friends: ['Shelby', 'Court']
};

const anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');

优点

  • 不需要创建构造函数
  • 实现简单

缺点

  • 引用类型属性会被共享
  • 无法实现代码复用

适用场景:简单对象之间的继承

5. 寄生式继承

在原型式继承基础上增强对象。

function createAnother(original) {
  const clone = Object.create(original);
  clone.sayHi = function() {
    console.log('hi');
  };
  return clone;
}

const person = {
  name: 'Nicholas',
  friends: ['Shelby', 'Court']
};

const anotherPerson = createAnother(person);
anotherPerson.sayHi(); // hi

优点

  • 可以为对象添加额外方法

缺点

  • 方法不能复用
  • 引用类型属性共享

适用场景:需要为对象添加额外方法的场景

6. 寄生组合式继承

最理想的继承方式,解决了组合继承的问题。

function inheritPrototype(child, parent) {
  const prototype = Object.create(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);

Child.prototype.sayAge = function() {
  console.log(this.age);
};

const child = new Child('Tom', 10);
child.sayName(); // Tom
child.sayAge(); // 10

优点

  • 只调用一次父类构造函数
  • 原型链保持不变
  • 最理想的继承方式

适用场景:所有需要继承的场景

ES6中的class继承

虽然本文主要讨论ES5的继承方式,但了解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;
  }
  sayAge() {
    console.log(this.age);
  }
}

const child = new Child('Tom', 10);
child.sayName(); // Tom
child.sayAge(); // 10

ES6的class语法实际上是寄生组合式继承的语法糖,更加简洁易读。

总结

  1. 原型链继承:通过改变原型链实现继承,但引用类型属性会被共享
  2. 构造函数继承:通过调用父类构造函数实现继承,但无法继承原型方法
  3. 组合继承:结合前两种方式,但父类构造函数会被调用两次
  4. 原型式继承:基于已有对象创建新对象,适合简单继承
  5. 寄生式继承:在原型式继承基础上增强对象
  6. 寄生组合式继承:最理想的继承方式,解决了所有问题

在实际开发中,推荐使用ES6的class语法或寄生组合式继承,它们是最完善、最高效的继承方式。理解这些继承方式的原理和区别,有助于我们在不同场景下选择最合适的实现方式。

frontend-hard-mode-interview 《前端内参》,有关于JavaScript、编程范式、设计模式、软件开发的艺术等大前端范畴内的知识分享,旨在帮助前端工程师们夯实技术基础以通过一线互联网企业技术面试。 frontend-hard-mode-interview 项目地址: https://gitcode.com/gh_mirrors/fr/frontend-hard-mode-interview

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯璋旺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值