前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >day018: JS如何实现继承?继承一定是好的设计吗?​

day018: JS如何实现继承?继承一定是好的设计吗?​

作者头像
用户3806669
发布于 2021-03-10 12:51:34
发布于 2021-03-10 12:51:34
65400
代码可运行
举报
文章被收录于专栏:前端三元同学前端三元同学
运行总次数:0
代码可运行

第六篇: JS如何实现继承?继承一定是好的设计吗?

第一种: 借助call

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  function Parent1(){    this.name = 'parent1';  }  function Child1(){    Parent1.call(this);    this.type = 'child1'  }  console.log(new Child1);

这样写的时候子类虽然能够拿到父类的属性值,但是问题是父类原型对象中一旦存在方法那么子类无法继承。那么引出下面的方法。

第二种: 借助原型链

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  function Parent2() {    this.name = 'parent2';    this.play = [1, 2, 3]  }  function Child2() {    this.type = 'child2';  }  Child2.prototype = new Parent2();
  console.log(new Child2());

看似没有问题,父类的方法和属性都能够访问,但实际上有一个潜在的不足。举个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  var s1 = new Child2();  var s2 = new Child2();  s1.play.push(4);  console.log(s1.play, s2.play);

可以看到控制台:

明明我只改变了s1的play属性,为什么s2也跟着变了呢?很简单,因为两个实例使用的是同一个原型对象。

那么还有更好的方式么?

第三种:将前两种组合

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  function Parent3 () {    this.name = 'parent3';    this.play = [1, 2, 3];  }  function Child3() {    Parent3.call(this);    this.type = 'child3';  }  Child3.prototype = new Parent3();  var s3 = new Child3();  var s4 = new Child3();  s3.play.push(4);  console.log(s3.play, s4.play);

可以看到控制台:

之前的问题都得以解决。但是这里又徒增了一个新问题,那就是Parent3的构造函数会多执行了一次(Child3.prototype = new Parent3();)。这是我们不愿看到的。那么如何解决这个问题?

第四种: 组合继承的优化1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  function Parent4 () {    this.name = 'parent4';    this.play = [1, 2, 3];  }  function Child4() {    Parent4.call(this);    this.type = 'child4';  }  Child4.prototype = Parent4.prototype;

这里让将父类原型对象直接给到子类,父类构造函数只执行一次,而且父类属性和方法均能访问,但是我们来测试一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  var s3 = new Child4();  var s4 = new Child4();  console.log(s3)

子类实例的构造函数是Parent4,显然这是不对的,应该是Child4。

第五种(最推荐使用): 组合继承的优化1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  function Parent5 () {    this.name = 'parent5';    this.play = [1, 2, 3];  }  function Child5() {    Parent5.call(this);    this.type = 'child5';  }  Child5.prototype = Object.create(Parent5.prototype);  Child5.prototype.constructor = Child5;

这是最推荐的一种方式,接近完美的继承,它的名字也叫做寄生组合继承。

ES6的extends被编译后的JavaScript代码

ES6的代码最后都是要在浏览器上能够跑起来的,这中间就利用了babel这个编译工具,将ES6的代码编译成ES5让一些不支持新语法的浏览器也能运行。

那最后编译成了什么样子呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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),这是用来干啥的呢?

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
追问: 面向对象的设计一定是好的设计吗?

不一定。从继承的角度说,这一设计是存在巨大隐患的。

从设计思想上谈谈继承本身的问题

假如现在有不同品牌的车,每辆车都有drive、music、addOil这三个方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Car{  constructor(id) {    this.id = id;  }  drive(){    console.log("wuwuwu!");  }  music(){    console.log("lalala!")  }  addOil(){    console.log("哦哟!")  }}class otherCar extends Car{}

现在可以实现车的功能,并且以此去扩展不同的车。

但是问题来了,新能源汽车也是车,但是它并不需要addOil(加油)。

如果让新能源汽车的类继承Car的话,也是有问题的,俗称"大猩猩和香蕉"的问题。大猩猩手里有香蕉,但是我现在明明只需要香蕉,却拿到了一只大猩猩。也就是说加油这个方法,我现在是不需要的,但是由于继承的原因,也给到子类了。

继承的最大问题在于:无法决定继承哪些属性,所有属性都得继承。

当然你可能会说,可以再创建一个父类啊,把加油的方法给去掉,但是这也是有问题的,一方面父类是无法描述所有子类的细节情况的,为了不同的子类特性去增加不同的父类, 代码势必会大量重复,另一方面一旦子类有所变动,父类也要进行相应的更新, 代码的耦合性太高,维护性不好。

那如何来解决继承的诸多问题呢?

用组合,这也是当今编程语法发展的趋势,比如golang完全采用的是面向组合的设计方式。

顾名思义,面向组合就是先设计一系列零件,然后将这些零件进行拼装,来形成不同的实例或者类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function drive(){  console.log("wuwuwu!");}function music(){  console.log("lalala!")}function addOil(){  console.log("哦哟!")}
let car = compose(drive, music, addOil);let newEnergyCar = compose(drive, music);

代码干净,复用性也很好。这就是面向组合的设计方式。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端三元同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【第六篇】SAP HANA XS使用JavaScript(JS)调用存储过程(Procedures)
前面几篇讲了SAP HANA XS JavaScript(JS)的基础内容和相应API,这篇讲一下JavaScript如何与后台交互,此篇讲存储过程(Procedures)。
matinal
2020/11/27
8370
【SAP HANA系列】SAP UI5上传图片 用XSJS存储在HANA中的方法
玩就玩一个高级的,此篇文章包含了SAP UI5,SAP HANA,SAP XS HANA串联起来
matinal
2020/11/13
7120
【SAP HANA系列】SAP UI5上传图片 用XSJS存储在HANA中的方法
【SAP HANA系列】SAP HANA XS使用服务器JavaScript Libraries详解
上一篇我们讲了SAP HANA XS JavaScript的安全事项,这一篇讲一下JavaScript的资源库使用。
matinal
2020/11/11
7300
【SAP HANA系列】SAP HANA XS使用服务器JavaScript Libraries详解
【SAP HANA系列】SAP HANA XS的JavaScript API详解
SAP HANA扩展应用程序服务(SAP HANA XS)提供了一组服务器端JavaScript应用程序编程接口(API),可配置应用程序与SAP HANA进行交互。
matinal
2020/11/11
8180
【SAP HANA系列】SAP HANA XS的JavaScript API详解
SAP HANA 技能[四] 存储过程
本篇只是讲述了一些简单的SAP HANA中的存储过程创建方法,后面会有详细内容讲解如何优雅的写存储过程。
Adil_zhang
2023/05/02
5960
【SAP HANA系列】SAP HANA XS使用JavaScript数据交互详解
在SAP HANA Extended Application Services(SAP HANA 扩展应用服务)中
matinal
2020/11/12
4890
【SAP HANA系列】SAP HANA XS使用JavaScript数据交互详解
【SAP HANA系列】SAP HANA XS使用Data Services查询CDS实体【一】
使用SAP HANA XS数据服务(XSDS)库来查询CDS实体,类似于JavaScript对象一样。
matinal
2020/11/11
9100
【SAP HANA系列】SAP HANA XS使用Data Services查询CDS实体【一】
【SAP HANA系列】SAP HANA使用XS和HTTP创建proxy
1、一个server.xshttpdest文件,用于定义我们要访问的服务器的详细信息
matinal
2020/11/12
6600
【SAP HANA系列】SAP HANA使用XS和HTTP创建proxy
java调用存储过程(stored procedures)的HelloWorld例子
1.java调用存储过程(stored procedures)的HelloWorld程序
马克java社区
2021/05/12
1.1K0
【SAP HANA系列】SAP HANA XS使用JavaScript编程详解
SAP HANA XS支持JavaScript中的服务器端应用程序编程。开发的服务器端应用程序使用JavaScript API将授权数据公开给客户端请求,例如,由客户端GUI(如Web浏览器,SAPUI5应用程序或移动设备)这些客户端。
matinal
2020/11/12
5360
【SAP HANA系列】SAP HANA XS使用JavaScript编程详解
【SAP ABAP系列】ABAP使用AMDP调用HANA存储过程
数据建模在SAP HANA视图中完成,需要读取视图的输出并将其保留在HANA表中。编写存储过程以从HANA视图读取数据并将数据插入HANA表。
matinal
2020/11/11
2K0
【SAP ABAP系列】ABAP使用AMDP调用HANA存储过程
【SAP HANA系列】SAP HANA XS的JavaScript安全事项
我们都知道web程序都有潜在的安全隐患问题,那么SAP HANA XS的JavaScript也是一样,使用服务器端JavaScript编写应用程序代码,也有潜在的外部攻击(和风险)。
matinal
2020/11/12
8690
MySQL 进阶之存储过程/存储函数/触发器
上面给大家演示了存储过程中的基本语法,现在只是在存储过程中定义了一条简单的select 语句 ,并没有任何逻辑。
叫我阿杰好了
2022/11/07
2.4K0
MySQL 进阶之存储过程/存储函数/触发器
存储过程----sqlserver
存储过程:存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升。--摘自百度百科。
cultureSun
2023/05/18
1.2K0
MySQL 存储过程
存储过程(Stored Procedure)是一种存储在数据库中的程序,可供外部程序调用的一种数据库对象。
恋喵大鲤鱼
2023/10/12
6930
MySQL 存储过程
从头开始学MySQL——-存储过程与存储函数(1)
存储过程就是一条或者多条SQL语句的集合,可以视为批文件。它可以定义批量插入的语句,也可以定义一个接收不同条件的SQL。
全栈程序员站长
2022/09/23
4920
从头开始学MySQL——-存储过程与存储函数(1)
其他混杂存储过程 | 全方位认识 sys 系统库
在上一篇《用于查看配置的存储过程 | 全方位认识 sys 系统库》中,我们介绍了sys 系统库中用于查看performance_schema配置信息的快捷存储过程,本期给大家介绍sys 系统库中不太好归类的一些存储过程,这也是本系列最后一个篇幅介绍存储过程。
沃趣科技
2018/10/11
2K0
其他混杂存储过程 | 全方位认识 sys 系统库
相关推荐
【第六篇】SAP HANA XS使用JavaScript(JS)调用存储过程(Procedures)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档