【译】JS继承常见误区扫盲(三)

原文链接:Common Misconceptions About Inheritance in JavaScript

作者:Eric Elliott

翻译:野草

本文首发于前端早读课【第911期】

往期回顾:

【译】JS继承常见误区扫盲(一)

【译】JS继承常见误区扫盲(二)

++问:难道类继承不比原型继承更常用吗?++

++答:不是!++

每当我听到这种误解,就想忍不住抛下一句:“你到底用过JavaScript没有?!”,然后走人。但,我还是忍住了,仔细跟对方澄清这个问题。

如果你也有这样的问题,不要沮丧。这不是你的问题,JavaScript培训TMD太差劲了!!!

答案是一个巨大的

错,但是。。。
原型是JavaScript中的惯用继承模式,而“类”是外来入侵模式。

我们来回顾JavaScript库的简单发展史:

最初,开发者各自开发自己的库,也乐于开源出来。此时原型就随之出现了。原型利用链接继承(concatenative inheritance)来扩展内置的原型。

后来当网上充斥着大量的原生方法覆盖或者库冲突的时候,大家都意识到修改内置原型并不是一个好的模式。不过,那又是另一回事了。

后来最火的JavaScript库出来了——jQuery。而丰富的jQuery插件是jQuery极具吸引力的原因之一。它们通过链接继承来拓展jQuery的委托原型。

你有感觉到某种设计模式了吗?

至今jQuery一直是最受欢迎的JavaScript,远远超过其他的库。

然而这是JavaScript混乱的开始,用类拓展的方式开始侵入JavaScript。John Resig写了一篇文章Simple Class Inheritance in JavaScript,开发者们纷纷开始效仿。即使John Resig本人也觉得jQuery不需要类,因为原型是更佳的选择。

接着,像ExtJS的类Java框架开始出现并且慢慢流行起来,开启了JavaScript类的非主流使用时代。那是2007年,JavaScript在12年之后因为一个流行库引入了类继承。

三年后,Backbone大爆发,其中.extend() 方法模仿类继承的实现,带来了很多糟糕的特性。这是JavaScript全面崩塌的开始。

曾经我们用Backbone开发一个百万行代码级别的应用。其中有个bug我花了几个月的时间,代码涉及6层继承关系。我沿着super链一行一行地检查每个构造函数的代码。最终在顶级类中找到bug。然后我又得修复子类中所有依赖这个错误的代码。本来几分钟能搞定的事,我花了几个小时才修复完成。太糟心了!

那代码根本不是JavaScript。我感觉我又回到了Java地狱,那种因为高耦合而带来的牵一发而动全身的恐惧感,无助感,孤独感迎面袭来。

这些也是重构最怕遇到的问题。

幸运的是,Backbone还有一片生机。

var object = {};

_.extend(object, Backbone.Events);

object.on("alert", function(msg) {
  alert("Triggered " + msg);
});

object.trigger("alert", "an event"); 

Backbone.Events 混入的思想其实就是链接继承。

事实上,如果你仔细思考那些库,你会发现很多使用链接和委托的例子。这些例子太简单常见了,开发者根本没有意识到那就是继承。

JavaScript继承非常简单,大家都认为它应该很难。于是为了达到我们的预期,我们引入了更复杂的类。

那我们是怎么引入类的呢?利用原型继承进行委托原型以及对象链接。这完全就是简单问题复杂化了。

++问:难道选择类继承还是原型继承不是决定于实际情况吗?++

++答:不是!++

数年来,我一直主张这样的观点:以原型为基础的面向对象更加简单,灵活,更不容易出错。很多人都听取了我的意见。我鼓励大家能举出一个使用类的例子来反驳我。我收到了一些回复,很不幸这些答案都是基于本文提到的一些误区。

曾经我也很喜欢用类继承,几乎在哪都用,代码中到处都是我创建的对象层级。为了帮助架构师设计对象层级关系,我还开发了一个可视化的快速应用开发工具。也即是说,在使用类继承的企业级应用中,我们需要一个可视化工具来协助我们理解和绘制对象层级关系。

在我从C++, Java转到JavaScript之后没多久,我不再这样做了。不是因为我开发的应用简单了(相反,我开发的更复杂了),而是因为JavaScript比C++, Java简单好用。从此,我再不没用过可视化工具来设计对象关系。

别人经常会咨询我软件设计的问题,我通常会建议他彻底重构。为什么呢?因为所有的对象层级在创建新的实例时总会出错。

我并不是一家之言。现如今,很多新版本开发都会彻底重构,其中大部分都是因为难维护的类层级而遗留下来的历史问题。几乎每个开发者都会有本《设计模式》,整本书都在讲述关系错误的设计模式,以及如何避免或者重构。

我还是劝告你,在这个问题上,你应该接纳四人帮(设计模式的作者)的意见:

优先使用对象组合,而非类继承。

Java中,这个比使用类继承还难,因为你需要用类来实现(Java所有对象都是通过类生成的)。

在JavaScript中,我们就没有这个借口了。事实上,比起管理对象层级,使用原型组合的方式来创建对象会简单很多。

什么?!

没骗你!如果你需要一个将日期变成megaCalendarWidget的jQuery对象,你不需要新开辟一个类。JavaScript能动态地拓展对象,只要拓展jQuery的原型对象就能做到。

/*
如何扩展jQuery原型对象?
*/

jQuery.fn.megaCalendarWidget = megaCalendarWidget;

你再调用jQuery工厂函数,你会能得到上述你需要的实例。

同样,你也可以用Object.assign()来组合任意多个对象,以后者优先的顺序。

import ninja from 'ninja'; // ES6 modules
import mouse from 'mouse';

let ninjamouse = Object.assign({}, mouse, ninja);

真的可以是任意数量的对象:

// I'm not sure Object.assign() is available (ES6)
// so this time I'll use Lodash. It's like Underscore,
// with 200% more awesome. You could also use
// jQuery.extend() or Underscore's _.extend()
var assign = require('lodash/object/assign');

var skydiving = require('skydiving');
var ninja = require('ninja');
var mouse = require('mouse');
var wingsuit = require('wingsuit');

// The amount of awesome in this next bit might be too much
// for seniors with heart conditions or young children.
var skydivingNinjaMouseWithWingsuit = assign({}, // create a new object
  skydiving, ninja, mouse, wingsuit); // copy all the awesome to it.

这就是我之前提到的链接继承,继承的那个原型有时会称为模版原型。区别于委托原型的委托属性,我们直接复制属性。

++问:ES6新增class关键词,我们不应该使用吗?++

++答:不应该!++

事实上,我们有一万个避免使用ES6类理由,更别说类其实是JavaScript中一个尴尬的存在。

我们已经拥有了JavaScript非常强大富有表现力的对象系统。如今JavaScript中实现的类具有限制性(贬义词,不是指类型修正的限制),并且模糊了语言中内置的原型面向对象系统。

所以,你知道怎么用JavaScript比较好了吗?作为对原型面向对象设计非常熟悉的过来人告诉你,利用原型进行语法糖和抽象处理最好。

【译者话】文章到这里终于好了,这真的是又长又有深度又费解的文章。作者的观点比较另类前卫,反正我是远远没到这种觉悟的。我也无法肯定或者否定他的说法,希望这篇文章能带给你一些思考吧。最后附上这个作者的介绍,我就不翻译了,要吐了。。。

Eric Elliott is the author of “Programming JavaScript Applications” (O’Reilly), & host of the documentary film-in-production, “Programming Literacy”. He has contributed to software experiences for Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica, and many more.

He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.