通过寄生组合式继承创建js的异常类
最近项目中在做js的统一的异常处理,需要自定义异常类。理想的设计方案为:自定义一个异常错误类BaseError,继承自Error,然后再自定义若干个系统异常,例如用户取消异常、表单异常、网络异常,这些异常类都继承自BaseError。系统中,根据各个自定义异常做统一的异常处理,例如如果是用户发出取消操作指令,当前调用链则抛出一个用户取消异常,然后由统一异常处理捕获,先判断他是不是继承自BaseError,如果是则再根据事先定义好的处理方案处理。
为啥说这只是理想的设计方案呢?因为es5根本就没有提供js的继承语法,更没有提供能够继承自Error的方案。
但是,虽然es5的js没有真正意义上的继承方法,但是还是提供了很多折中的方案,例如使用修改原型对象,实现仿继承的方案,参考代码如下
function MyObject(){} function MySonObject(){} MySonObject.prototype = new MyObject()
例子代码中,MyObject是父类,他的一个对象赋给了子类MySonObject的原型,这样MySonObject就获取到MyObject的所有属性。通过这种形式,我们可以模拟出继承。
然后就是想办法继承自Error。这里先阐明一下为什么要继承自Error,作为异常类最重要一点就是一定要返回错误调用的堆栈信息,否则出错了都不知道到底是哪个地方抛出的,根本无法调试。js语法中,任何对象都可以被throw抛出,但是只有Error抛出才有堆栈信息,如下列测试。
try{throw "string"}catch(e){console.log(e)} try{throw new Object()}catch(e){console.log(e)} try{throw new Error()}catch(e){console.log(e)}
为了能够携带错误的堆栈信息,就必须要有Error对象,如果我们直接“继承”自Error对象,会有什么样的效果呢?在chrome里做如下测试:
function BaseError(){} BaseError.prototype = new Error() try{throw new BaseError()}catch(e){console.log(e)}
结果堆栈信息丢了。
不能直接继承Error,我们可以在BaseError里面的构造函数里定义Error,这样虽然没有直接继承Error,但是仍然有Error对象作为属性。
function BaseError(){this.error = new Error()} //或者BaseError.prototype.error = new Error()BaseError.prototype.printError = function(){console.log(this.error.stack)};
function MyError(){} MyError.prototype = new BaseError()
try{throw new MyError()}catch(e){e.printError()}
这样写虽然携带了堆栈信息,但是堆栈信息的位置却是“function BaseError(){this.error = new Error()}”这句,而不是“new MyError()”,这样根本就没有什么意义。同样把Error创建过程放入BaseError的原型链上赋值(BaseError.prototype.error = new Error())一样无法获得正确的堆栈信息。只有把new Error()这句放入子类都构造器里,才能正确显示堆栈信息,不过这样就增加了子类的维护成本,继承的意义也丢失了。
有没有更好的解决方案呢?问题就这样我们的“继承”方式,因为js中,类的原型也是一个对象,BaseError子类中的原型是一个BaseError实例,所以属性error相当于一个静态属性,各个子类共享了这个error变量,同时仅在声明继承的时候调用了父类的构造函数,不能在子类创建对象时候调用构造函数,这使得Error对象不能在子类创建的时候被创建。所以只有改变目前的这种继承方式,实现Error随子类创建而创建,这样才能返回正确的堆栈信息。
因此继续深入地学习js的继承方式,发现被认为是最理想的“寄生组合继承”可以解决这个问题,首先简单介绍一下什么是寄生组合继承。
组合继承:因为类似上述我遇到的问题,原型链继承时候父类的属性是静态共享型属性,所以必须要在子类型的构造函数内,通过apply函数调用父类型的构造函数的一种继承方法。代码如下
function BaseError(){this.error = new Error()} BaseError.prototype.printError = function(){console.log(this.error.stack)}; function MyError(){BaseError.apply(this);} try{throw new MyError()}catch(e){console.log(e)}
这样创建MyError的时候,也调用了BaseError的构造函数,并打印出了正确的堆栈。不过也出现了一个问题,那就是BaseError的printError没有继承到。使用上述代码仅仅继承了属性,却不能继承父类的方法,这样显然也是不能满足需求的。
所谓组合继承方式,就是在apply调用超类构造函数继承基础上,再调用原型链继承。
function BaseError(){ this.error = new Error(); console.log(this.error); //为了显示父类的构造方法调用了两次} BaseError.prototype.printError = function(){console.log(this.error.stack)}; function MyError(){BaseError.apply(this);} MyError.prototype = new BaseError()
try{throw new MyError()}catch(e){e.printError()}
打印出结果为
从结果看得出基本已经满足了我们的需求,但是需要注意点是父类的构造方法执行了两次,所以执行了两次console.log(this.error)。为了解决这个问题,需要引入另一个js继承方式,即寄生继承。
寄生组合继承:寄生继承的核心就是,现将超链的原型赋给另一个寄生类,然后创建这个寄生类的实例,再子类继承这个寄生类实例,这样就去除了原型继承中,需要创建基类的过程,代替为创建一个寄生类。将组合继承和寄生继承同时使用,就是组合寄生继承了。
function BaseError(){this.error = new Error();console.log(this.error); } BaseError.prototype.printError = function(){console.log(this.error.stack)}; //构造寄生类 function parasiticObject(){} parasiticObject.prototype = BaseError.prototype;function MyError(){BaseError.apply(this);} MyError.prototype = new parasiticObject() try{throw new MyError()}catch(e){e.printError()}
这样就解决了所有问题。
因为寄生继承导致整个继承的代码过于啰嗦,所有我们一般把继承过程写到一个函数里执行。这样就简化了整个继承代码了,同时也隐藏了无需暴漏的寄生类parasiticObject。
function BaseError(){this.error = new Error();console.log(this.error); } BaseError.prototype.printError = function(){console.log(this.error.stack)}; //继承方法 function inheritPrototype(superType,subType){ var prototype = Object(superType.prototype);prototype.constructor = subType;subType.prototype = prototype; } function MyError(){BaseError.apply(this);} inheritPrototype(BaseError,MyError) try{throw new MyError()}catch(e){e.printError()}
这样就完成了整个js异常类的设计。通过寄生组合继承方式,可以很好地实现js的类的继承,并通过创建Error获得错误的堆栈信息,方便调试,也为统一的异常处理机制奠定了基础,下一次将分享我们项目中的统一异常处理的设计。
转载于:https://www.cnblogs.com/laden666666/p/5264072.html
通过寄生组合式继承创建js的异常类相关推荐
- JS继承之寄生组合式继承
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法. 组合继承弥补了原型链和盗用构造函数的不足,是js中使用最多的继承模式. 实现思路: 不必为了指定子类型的原型而调用父 ...
- JS面向对象——原型式继承函数、寄生式继承函数、寄生组合式继承
一.原型式继承函数 回顾一下JavaScript想实现继承的目的:重复利用另外一个对象的属性和方法. 最终的目的:student对象的原型指向了person对象: 二.寄生式继承函数 寄生式(Para ...
- JS基础--组合继承,寄生组合式继承
以下内容总结自<JavaScript高级程序设计(第3版)> 一. 组合继承 组合继承使用原型链实现对原型属性和方法的继承,使用借用构造函数实现对实例属性的继承(引用类型的属性写在构造函数 ...
- [js高手之路]寄生组合式继承的优势
在之前javascript面向对象系列的文章里面,我们已经探讨了组合继承和寄生继承,回顾下组合继承: 1 function Person( uName ){ 2 this.skills = [ 'ph ...
- 初学JavaScript:原型继承/盗用构造函数继承/组合继承/寄生式继承/原型式继承/寄生组合式继承
文章目录 继承 简介 1.原型链继承 默认原型 判断原型与实例间是否为继承关系 原型继承中的方法 原型链的破坏 原型继承的问题 2.盗用构造函数继承 简介 盗用构造函数继承的问题 3.组合继承 简介 ...
- 【JavaScript】手撕前端面试题:寄生组合式继承 | 发布订阅模式 | 观察者模式
- 【JS继承】JS继承之寄生式继承
自我介绍:大家好,我是吉帅振的网络日志:微信公众号:吉帅振的网络日志:前端开发工程师,工作4年,去过上海.北京,经历创业公司,进过大厂,现在郑州敲代码. JS继承专栏 1[JS继承]什么是JS继承? ...
- 【JS继承】JS继承之构造函数继承
自我介绍:大家好,我是吉帅振的网络日志:微信公众号:吉帅振的网络日志:前端开发工程师,工作4年,去过上海.北京,经历创业公司,进过大厂,现在郑州敲代码. JS继承专栏 1[JS继承]什么是JS继承? ...
- 【JS继承】JS继承之原型链继承
自我介绍:大家好,我是吉帅振的网络日志:微信公众号:吉帅振的网络日志:前端开发工程师,工作4年,去过上海.北京,经历创业公司,进过大厂,现在郑州敲代码. JS继承专栏 1[JS继承]什么是JS继承? ...
- 详细解析JavaScript中的继承(包括组合继承和寄生式继承)
继承:相信很多学习过Java等面向对象语言的同学,都会接触过继承,它们实现继承的主要方式是接口继承和实现继承.但由于JavaScript函数没有签名,所以无法实现接口继承.ECMAScript支持实现 ...
最新文章
- Unreal Engine 4 —— Post Process Shader练手(HLSL)
- javascript 自动填写表单
- 现代密码学5.2--域扩张:Merkle-Damgard Transform
- xfce4设置屏保/锁屏时间
- SAP C4C Mashup port bindingF4帮助对话框里的数据源
- ajax php接收不到数据库,PHP更新MySQL数据库与AJAX调用没有做任何事情
- Tomcat的目录结构详解
- [Unity] 战斗系统学习 11:Buff 框架 1
- php 绘图 jpeg,PHP gd库增加jpeg支持
- mysql 客户端乱码_Mysql客户端中文乱码问题解决
- 冰兮坊Java_java 中文字符 获取首字母(一级二级字符)
- 2018今日头条大数据方向笔试题
- Android程序暂停sh,init进程 解析Android启动脚本init.rc 修改它使不启动android init.rc中启动一个sh文件...
- Java实现简易四则运算器
- LeetCode - Remove Duplicates from Sorted List
- Matlab 2016a安装和破解教程
- Kafka原理介绍+安装+基本操作
- 数学建模学习(27):对策论模型,代码+案例,讲很详细,别开小差,很烧脑!
- 常见问题:try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候执行?
- html表格图片右对齐,更好的方式在HTML表格中右对齐文本
热门文章
- 计算机二级考试c语言 上机,计算机等级考试二级C语言上机题[2]
- linux 离线 nfs,Linux 系统 NFS服务
- java ir_基本功 | Java即时编译器原理解析及实践
- 经典CNN之:VGGNet
- 容器技术Docker K8s 21 容器服务ACK基础与进阶-存储管理
- 数据集:科研经费投入对生产能力提高的影响
- 2021-08-30二叉树后向遍历 leetcode 栈
- JavaWeb检测注册内容是否在数据库中有相同的内容
- not是什么意思在c语言,为什么在C样式语言中逻辑NOT运算符是“!”而不是“ ~~”?...
- python编程*三角形图形创意图片_Python图形编程探索系列-04-网上图片与标签组件的结合...