JavaScript开发者的繁殖速度和它的语言特性同样迅猛,这是好事,可是也把JS搞得比任何一种其余语言都更像流行乐,充满教派和玄学。但编程不是玄学,是科学和工程。这篇文章就用来阐述和探讨JavaScript中的一个比较关键的概念,虽然在实践上并不如在理论上那么意义重大。node
Prototype Inheritance是JavaScript里的一个标志性特性。实际上它叫作Inheritance是有一些问题的,JS没有type系统,instanceof也只是一个沿着原型链查找constructor的语法糖,一个对象是谁构造出来的并不说明任何问题,由于它的对象没有结构上的稳定性承诺,只能靠程序员自觉。程序员
Inheritance是OO近30年的工程实践里留下来的重要特性,可是它不是一个好的特性。固然好与很差是相对的,在绝大多数状况下它都不是太大的问题,尤为是工程进入尾声,开发者对问题和模型有充分认识的时候,Class Hierarchy能够是很合理的设计。npm
那么,在更General的层面上去问Inheritance设计解决的是什么问题呢?两个字,reuse。编程
在Java里,reuse有两个语法关键字,一个是extends,即inheritance,另外一个是implements,实现interface。函数
那么为何把implements也当成reuse呢?由于任何模块总有两个方面,使用者和提供者,implements实现了一个interface,因此等价于重用了使用者代码。this
固然你说extends也达到了一样的目的呀?并且我还重用了父类的状态和行为呢?是的,凡是两面,有得有失;这正是它倒霉的地方。设计
它倒霉的具体状况被称为fragile base class问题,wiki上有词条,不赘述。component
由于extends/inheritance是一种长程的关联,基类的修改对继承者的影响难以估计和维护,具备ripple effect特性,所以从耦合度的角度说,它大大提升了组件的耦合度,高耦合度不是罪过,但它应对变化的能力变差了。对象
好了,说了这么多咱们说到了问题的本原。继承
在Self语言中,也是最先试图解决这个问题的语言设计者们,给出了Prototypal Inheritance设计,
它的设计初衷有两个:
抹平Class和Object的差别,让修改基类变得容易;
若是你修改基类,复制一个基类对象而后修改,新继承者重新的基类对象开始继承。其余继承者不受影响。
你以为这个差异很重要吗?其实在实践上没有想象的那么重要。
JavaScript在设计上还有点不一样,它的原型对象是共享而非复制的,结果是只适合把方法装载到原型上去,偶尔有一些同类对象相同的只读context也能够这样作,其余每对象私有态还得经过调用父类构造函数作出来,即ES6里的super关键字,若是是ES5,得手动把this bind到父类构造函数上调用。这个特性就语言而言是重要特性,可是和咱们讨论的问题没太大关系。
更重要的问题出在设计上而不是语言层面。
James Gosling在一次研讨会上回答问题时,有人问了他这样一个问题,若是从新设计Java语言,他会有什么重要的取舍。Gosling的回答可能有点儿令你吃惊:他说若是能够从新来过,他不会赋予Java语言继承特性,只用implements。
呃?!
实际上是能够理解的。
继承设计看起来在代码重用上很方便,可是它的fragile base class问题,让它没法应对软件系统的scale问题。这一点不用论证,在整个软件工业上,继承这种whitebox reuse不能scale是一个定论,单一程序用继承书写代码不是问题,可是任何有点规模的系统都是靠interface,protocol,或者所谓的component-based工程方法来搭建的,也就是在更高粒度的设计层面只有基于Interface的blackbox reuse。
在任何语言中,都能用blackbox reuse构建复杂系统。在JavaScript中,庞大的npm包系统实现的最终应用,也是blackbox reuse。
JavaScript自己是否functional是洗剪吹们喜欢探讨的,严肃的工程师不应干这个事儿;可是整个JavaScript的Community的共识是基于blackbox reuse构建系统,这是好事。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
那么是否该使用extends呢?
不少时候没什么必要性。既然Gosling都说不应去经过inheritance-based hierarchy去组合复杂行为(牺牲低耦合度),谁还比他更有资格对这个问题发言呢?
一般能够把几个逻辑单元糖葫芦同样串起来实现一个从外部看来功能特别Powerful的一个对象时,也很容易把每一个独立单元用decorator,facade之类的pattern串起来,可能会多写点儿代码,但不会不少,JavaScript做为无类型动态语言在书写pattern时具备显著的简洁优点。(在C++/Java里是相反的,只写inheritance显著比写pattern简洁;可是问题就是问题你没法回避,若是必需要松开耦合,还得回到pattern上定义,这也是为何这些pattern被发明出来的缘由。)
固然用extends方便的时候也不必去抵触它,好比node里的event emitter,stream等等,该用就用呗,只要不去试图构造framework同样的hierarchy便可。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
若是有人跟你说JavaScript不是OO语言,请一脚把它踹沟里去,JS里除了用Object Literal写出来的ex nihilo对象以外,(逻辑上)全部对象都是用构造函数构造出来的,这甚至包括全局的Object, Array,Function等等,一切皆对象是JS的最高设计思想。你怎么能说它不是OO的?