JavaScript的类出现了什么问题?

全文共3031字,估计进修时长8分钟

图源:unsplash

固然JavaScript类看起来一切正常,但若你用它一段时光,尤其是之前用过ES5的人,就有可能看见原型持续演变成现行类模式的过程。

为什么呢?原型链出什么问题了?依我拙见,谜底是一切正常。但技巧界花费了数年的时光,迫使类的概念进入不合的构造和库中,是以ECMA技巧委员会决定无论若何都要添加它。

这有什么问题吗?在我们已经拥有的原型持续之上添加了一些构成,并决定将其称为类,这反过来又闪开辟人员认为他们正在处理一种面向对象的说话,而实际上它们并不是。

类仅仅是语法糖(syntactic sugar)

JavaScript没有OOP的周全支撑,因为它从来都不须要OOP。从外面看,现行类版式出现出OPP典范,因为:

? 可以定义根本类、分类状况和行动以及特别经典语法。

? 可以把一种类沿用到另一种类。

? 可以定义属性和办法的可行度,公共和私家均可。

? 可认为属性定义获得者和设置者。

? 可以忽视类持续的办法。

? 当然还可以实例化各类类。

我之所以说类是语法糖,是因为尽管外面来看,类看起来异常面向对象,假如做一些跨越范畴可能性范围的事,如定义一种涉及别的两种类的类(这是今朝弗成能实现的事),须要应用如下代码:

//Thehelper function functionapplyMixins(derivedCtor,baseCtors) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name=> { let descriptor =Object.getOwnPropertyDescriptor(baseCtor.prototype, name) Object.defineProperty(derivedCtor.prototype, name, descriptor); }); }); } //The parent classes classA { methodA(){ console.log("A") } } classB { methodB(){ console.log("B") } } //The child class classC { } //Using mixins applyMixins(C, [A, B]) let o =newC() o.methodA() o.methodB()

我们须要做这个,因为无法编辑JS:

classA { methodA(){ console.log("A") } } classB { methodB(){ console.log("B") } } classCextendsA, B { }

在某些情况下,这种行动可能会派上用处,JavaScript的员工创建了上面的代码片段,我只是删除了额外的代码,使它实用于通俗JS。

然则样本代码的重要信息应当是applyMixins功能。即使不充分懂得它的功能,也能发明,它用于评估各类类的原型属性以复制和重分派办法和属性。这是发明事实的全部证据:类只不过是在经由验证的原型持续模型之上的语法糖。

这解释应当停止用类吗?并不是。懂得它很重要,假如须冲要破类能做和不克不及做的界线,将不得不处理原型来实现这一点。

JavaScript的OOP 模型错过了什么?

假如如今的OOP模型不敷完美,只是原型持续的抽象体,那么我们错过了什么?什么使JS成为真正的OOP?

要解答这个问题,就先要看看JavaScript的功能,说话背后的团队肯定要创造能把JavaScript转换成JS的器械,来把JavaScript推到极限。这反过来也会限制它们的功能,然则,开端OOP欲望列表的一个好办法是查看它们与OOP相干的特点。

你立时会留意到一个警告:今朝JavaScript中缺掉的一些OOP构造具有内涵的类型检查功能,在动态类型说话中没有真正的意义,这可能是因为它们还没有被添加。

接口

这些是很好的构造,有助于定义类应当遵守的API。接口在无类型JS中可能会损掉,它的一个重要好处是,你可认为任何实现雷同接口的类定义一个变量,并安然地调用它的任何办法。

interfaceAnimal { speak() } classDog implements Animal{ speak() { console.log("Woof!") } } classCat implements Animal{ speak() { console.log("Meau!") } } classHuman implements Animal{ speak() { console.log("Hey dude, what's up?") } } //if we had Interfaces in JS we could safely do: let objects = [newDog(), newCat(), newHuman()] objects.forEach(o => o.speak())

这在通俗JS中是无法做到的。当然可以经由过程定义speak办法并覆盖它的类来实现同样的目标。但话又说回来,也可以在任何其他强OOP说话中如许做,接口加倍清楚和简洁。

抽象类

每当我测验测验用代码进行全OOP时,肯定会错过JS中的抽象类。抽象类定义并实现办法,但永远不会被实例化。它是一种对可以扩大但不克不及直接应用的常见行动进行分组的办法。它绝对可以在当前的JS范畴内实现,而不会造成太多的破坏。

静态多态

静态多态许可我们在同一个类中多次定义雷同的办法,然则应用不合的签名。换句话说,反复名称,但要确保它接收到不合的参数。如今我们应用JS有了rest参数,这许可我们有随便率性数字,然而,这也意味着必须向办法中添加额外的代码来处理这个层次的动态。

相反,假如可以更清楚地区分办法签名,那么可以直接将雷同业为的不合风格封装到不合的办法中。

上边版是无效的JS,但它的代码更干净,是以须要来进行心理分析的认知负荷也更少。然而,下边版完全有效。它须要一些思维复合,四周有更多的代码,因为它不仅记录日记(这应当是它的独一目标),并且还试图根据供给的参数决定若何记录日记。

静态多态性平日经由过程查看办法中接收的参数类型来实现。然而,因为JS的工作方法,我们知道这是弗成能的。

受保护的属性和办法

已经有了公开的可见性,并且很快就获得了办法和属性的私有可见性。下一步应当是添加受保护的可见性,假如你想要有一个合适的OOP体验,这三者都是须要的。受保护的属性和办法只能从类内部或它的一个子类中拜访(与私有可见性相反,私有可见性将拜访限制为只能拜访父类)。

我一向在尽力把JS称为OOP说话,直到我看到一种不消引用原型链就能处理类内部的办法,我才会持续尽力下去。为什么他们不克不及持续扩大原型持续模型,而不是给我们这个便宜的类版本呢?这是一个由来已久的问题。

如今,我要对添加的语法糖说声感谢,也会持续存眷将来的新的基于oop的特点。

留言点赞存眷

我们一路分享AI进修与成长的干货

如转载,请后台留言,遵守转载规范