详解Javascript的继承实现

681 查看

我最早掌握的在js中实现继承的方法是在w3school学到的混合原型链和对象冒充的方法,在工作中,只要用到继承的时候,我都是用这个方法实现。它的实现简单,思路清晰:用对象冒充继承父类构造函数的属性,用原型链继承父类prototype 对象的方法,满足我遇到过的所有继承的场景。正因如此,我从没想过下次写继承的时候,我要换一种方式来写,直到今天晚上看了三生石上关于javascript继承系列的博客(出的很早,现在才看,真有点可惜),才发现在js里面,继承机制也可以写的如此贴近java这种后端语言的实现,确实很妙!所以我想在充分理解他博客的思路下,实现一个自己今后用得到的一个继承库。

1. 混合方式实现及问题

了解问题之前,先看看它的具体实现:

从结果上来说,这种继承实现方式没有问题,Manager的实例同时继承到了Employee类的实例属性和实例方法,并且通过instanceOf运算的结果也都正确。但是从代码组织和实现细节层面,这种方法还有以下几个问题:

1)代码组织不够优雅,继承实现的关键部分的逻辑是通用的,都是如下结构:

这段代码缺乏封装。另外在添加子类的实例方法时,不能通过SubClass.prototype = { method1: function() {} }这种方式去设置,否则就把子类的原型整个又修改了,继承就无法实现了,这样每次都得按SubClass.prototype.method1 = function() {} 的结构去写,代码看起来很不连续。

解决方式:利用模块化的方式,将通用的逻辑封装起来,对外提供简单的接口,只要按照约定的接口调用,就能够简化类的构建与类的继承。具体实现请看后面的内容介绍,暂时只能提供理论的说明。

2)在给子类的原型设置成父类的实例时,调用的是new SuperClass(),这是对父类构造函数的无参调用,那么就要求父类必须有无参的构造函数。可是在javascript中,函数无法重载,所以父类不可能提供多个构造函数,在实际业务中,大部分场景下父类构造函数又不可能没有参数,为了在唯一的一个构造函数中模拟函数重载,只能借助判断arguments.length来处理。问题就是,有时候很难保证每次写父类构造函数的时候都会添加arguments.length的判断逻辑。这样的话,这个处理方式就是有风险的。要是能把构造函数里的逻辑抽离出来,让类的构造函数全部是无参函数的话,这个问题就很好解决了。

解决方式:把父类跟子类的构造函数全部无参化,并且在构造函数内不写任何逻辑,把构造函数的逻辑都迁移到init这个实例方法,比如前面给出的Employee和Manager的例子就能改造成下面这个样子: