当前位置: 首页 > news >正文

JavaScript 继承百花齐放:从原型链到 ES6 类

 前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元

在 JavaScript 中,继承是一个重要的知识点,上篇文章中我们已经了解了原型和原型链的概念,本文就来介绍一下js中实现继承的几种方式。


目录

继承

实现继承的方法

1.原型链继承

2.借用构造函数继承

3.组合继承

4.原型式继承

5.寄生式继承

6.寄生组合式继承

7.class继承

结语


继承

继承,简单来讲就是让子类能够访问到父类的属性和方法,继承的主要作用就是实现代码的重用。

在JavaScript中,主要通过原型链来实现继承。我们重温一下构造函数、原型和实例的关系:

每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个指针指向原型。

实现继承的方法

1.原型链继承

原型链继承的基本思想:让子类构造函数的原型指向父类的实例。

function Parent () {this.name = 'kevin';
}
Parent.prototype.getName = function () {console.log(this.name);
}
function Child () {}//让子类原型指向父类实例
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1.getName()) // kevin

缺点是引⽤类型的属性被所有实例共享,并且创建子类型实例时,不能向父类型传参。

2.借用构造函数继承

基本思想:在子类构造函数中调用父类构造函数,使用call将父对象的构造函数绑定在子对象上。

function Parent () {this.names = ['kevin', 'daisy'];
}
function Child () {// 调用父类构造函数Parent.call(this);
}var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy"]

解决了原型链继承不能传参问题和父类原型共享问题,但是无法实现函数方法的复用,方法都在构造函数中定义,每次创建实例都会创建一遍方法,方法本质上都变成了实例自己的方法,不是公共的方法。

3.组合继承

基本思想:将原型链和借用构造函数组合起来使用。使用原型链实现对原型方法的继承,通过借用构造函数的方式来实现属性的继承。

function Parent (name) {this.name = name;this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {console.log(this.name)
}
function Child (name, age) {Parent.call(this, name);this.age = age;
}Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child1 = new Child('kevin', '18');
child1.colors.push('black');
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
var child2 = new Child('daisy', '20');
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]

这种方式的优点:既实现了函数复用,又保证每个实例都有自己的属性。

缺点:调用了两次父类的构造函数,造成了子类的原型中多了很多不必要的属性。

4.原型式继承

基本思想:基于已有的对象来创建新的对象。

// 利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型
function createObj(o) {function F() {}F.prototype = o;return new F();
}
var person = {name: 'kevin',friends: ['daisy', 'kelly']
}
var person1 = createObj(person);
var person2 = createObj(person);
person1.name = 'person1';
console.log(person2.name); // kevin
person1.friends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]

缺点是引⽤类型的属性被所有实例共享。

ES5中存在Object.create()的方法,能够代替上面的createObj方法。

5.寄生式继承

寄生式继承与原型式继承很接近,它的思想就是在原型式继承的基础上以某种方式增强对象,然后返回这个对象。

function createObj (o) {let clone = Object.create(o);clone.sayName = function () {console.log("hi");}return clone;
}

6.寄生组合式继承

组合继承的缺点就是调用了2次父构造方法。寄生组合式继承就是改造组合式继承,使用父类的原型的副本来作为子类的原型,这样就只调用一次父构造函数,避免了创建不必要的属性。

function Parent (name) {this.name = name;this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {console.log(this.name)
}
function Child (name, age) {Parent.call(this, name);this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;//把子类的构造指向子类本身
var child1 = new Child('kevin', '18');
console.log(child1.colors);//[ 'red', 'blue', 'green' ]
console.log(child1.getName());//kevin

7.class继承

在 ES6 中,可以使用 class 去实现继承。使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super 。

class Parent {constructor(name) {this.name = name;
}getName() {console.log(this.name);
}
}
class Child extends Parent {constructor(name, age) {super(name);//使用this之前必须先调用super()this.age = age;
}
}
// 测试
let child = new Child("kevin", 18);
console.log(child.name); // kevin
console.log(child.age); // 18
child.getName(); // kevin

注意:在 JS 中并不存在类, class 只是语法糖,本质还是函数。

class Person {}
Person instanceof Function // true

class实现的继承本质上是寄生组合式继承。下面是使用babel将ES6代码编译成ES5后的代码:

function _possibleConstructorReturn(self, call) {// ...return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
}function _inherits(subClass, superClass) {// ...//看到没有subClass.prototype = Object.create(superClass && superClass.prototype, {constructor: {value: subClass,enumerable: false,writable: true,configurable: true}});if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}var Parent = function Parent() {// 验证是否是 Parent 构造出来的 this_classCallCheck(this, Parent);
};var Child = (function (_Parent) {_inherits(Child, _Parent);function Child() {_classCallCheck(this, Child);return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));}return Child;
}(Parent));

核心是_inherits函数,可以看到它采用的依然也是寄生组合继承方式。不过这里加了一个Object.setPrototypeOf(subClass, superClass),这是用来干啥的呢?

答案是用来继承父类的静态方法。这也是原来的继承方式疏忽掉的地方。

  • ES5 继承和 ES6 继承的区别

ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上 Parent.call(this) 

ES6 的继承不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~ 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 软设之TCP/IP协议
  • 软科中国大学排名爬虫+数据可视化
  • 图片管理组建
  • Flink 实时数仓(三)【DWD 层搭建(一)】
  • 《人性的枷锁:菲利普的人生探索能解开枷锁吗?》
  • 树套树模板
  • PYTHON专题-(5)类的专有方法
  • 每日学术速递8.3
  • Xilinx管脚验证流程及常见问题
  • conda环境pip 安装Tensorflow-gpu 2.10.2提示nbconvert 的包依赖冲突
  • OpenStack Yoga版安装笔记(十二)nova安装(下)
  • 林轩田机器学习基石——笔记1.2 Learn to Answer Yes/No(如何进行学习)
  • Flink 实时数仓(二)【DIM 层搭建】
  • 中介子方程七十九
  • Apache Kylin数据模型设计:从ETL到多维分析
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • Android 架构优化~MVP 架构改造
  • Create React App 使用
  • ECMAScript6(0):ES6简明参考手册
  • github从入门到放弃(1)
  • iOS 颜色设置看我就够了
  • Java超时控制的实现
  • PaddlePaddle-GitHub的正确打开姿势
  • Promise初体验
  • Redis学习笔记 - pipline(流水线、管道)
  • Terraform入门 - 1. 安装Terraform
  • Webpack 4 学习01(基础配置)
  • Windows Containers 大冒险: 容器网络
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 工作手记之html2canvas使用概述
  • 诡异!React stopPropagation失灵
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 让你的分享飞起来——极光推出社会化分享组件
  • 如何进阶一名有竞争力的程序员?
  • 深度学习入门:10门免费线上课程推荐
  • 使用parted解决大于2T的磁盘分区
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 小程序测试方案初探
  • 怎么把视频里的音乐提取出来
  • ​数据结构之初始二叉树(3)
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #pragma once与条件编译
  • #VERDI# 关于如何查看FSM状态机的方法
  • (7) cmake 编译C++程序(二)
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (java)关于Thread的挂起和恢复
  • (leetcode学习)236. 二叉树的最近公共祖先
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (不用互三)AI绘画工具应该如何选择
  • (分布式缓存)Redis哨兵
  • (附源码)spring boot车辆管理系统 毕业设计 031034