JavaScript的类出现了什么问题?

开发 前端
虽然JavaScript类看起来一切正常,但若你用它一段时间,尤其是之前用过ES5的人,就有可能看见原型继承演变成现行类模式的进程。

 本文转载自公众号“读芯术”(ID:AI_Discovery)

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

[[389233]]

为什么呢?原型链出什么问题了?依我拙见,答案是一切正常。但技术界花费了数年的时间,迫使类的概念进入不同的结构和库中,因此ECMA技术委员会决定无论如何都要添加它。

这有什么问题吗?在我们已经拥有的原型继承之上添加了一些组成,并决定将其称为类,这反过来又让开发人员以为他们正在处理一种面向对象的语言,而实际上它们并不是。

类仅仅是语法糖(syntactic sugar)

JavaScript没有OOP的全面支持,因为它从来都不需要OOP。从表面看,现行类版式呈现出OPP范例,因为:

  • 可以定义基本类、分类状态和行为以及特殊经典语法。
  • 可以把一种类沿用到另一种类。
  • 可以定义属性和方法的可行度,公共和私人均可。
  • 可以为属性定义获得者和设置者。
  • 可以忽略类继承的方法。
  • 当然还可以实例化各种类。

我之所以说类是语法糖,是因为尽管表面来看,类看起来非常面向对象,如果做一些超过领域可能性范围的事,如定义一种涉及另外两种类的类(这是目前不可能实现的事),需要使用如下代码:

  1. //Thehelper function  
  2.         functionapplyMixins(derivedCtor,baseCtors) {  
  3.             baseCtors.forEach(baseCtor => {  
  4.                 Object.getOwnPropertyNames(baseCtor.prototype).forEach(name=> {  
  5.                     let descriptor =Object.getOwnPropertyDescriptor(baseCtor.prototype, name)  
  6.                     Object.defineProperty(derivedCtor.prototype, name, descriptor);  
  7.                 });  
  8.             });  
  9.         }  
  10.         //The parent classes  
  11.         classA {  
  12.                methodA(){  
  13.                 console.log("A")  
  14.             }  
  15.         }  
  16.         classB {  
  17.             methodB(){  
  18.                 console.log("B")  
  19.             }  
  20.         }  
  21.         //The child class  
  22.         classC {  
  23.         }  
  24.         //Using mixins  
  25.         applyMixins(C, [A, B])  
  26.         let o =newC()  
  27.         o.methodA()  
  28.         o.methodB()  

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

  1. classA {  
  2.             methodA(){  
  3.              console.log("A")  
  4.          }  
  5.      }  
  6.         classB {  
  7.             methodB(){  
  8.              console.log("B")  
  9.          }  
  10.      }  
  11.         classCextendsA, B {  
  12.         }  

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

但是样本代码的重要信息应该是applyMixins功能。即使不充分理解它的功能,也能发现,它用于评估各种类的原型属性以复制和重分配方法和属性。这是发现事实的全部证据:类只不过是在经过验证的原型继承模型之上的语法糖。

这说明应该停止用类吗?并不是。理解它很重要,如果需要突破类能做和不能做的界限,将不得不处理原型来实现这一点。

JavaScript的OOP 模型错过了什么?

如果现在的OOP模型不够完美,只是原型继承的抽象体,那么我们错过了什么?什么使JS成为真正的OOP?

要解答这个问题,就先要看看JavaScript的功能,语言背后的团队肯定要发明能把JavaScript转换成JS的东西,来把JavaScript推到极限。这反过来也会限制它们的功能,但是,开始OOP愿望列表的一个好方法是查看它们与OOP相关的特性。

你马上会注意到一个警告:目前JavaScript中缺失的一些OOP构造具有内在的类型检查功能,在动态类型语言中没有真正的意义,这可能是因为它们还没有被添加。

接口

这些是很好的结构,有助于定义类应该遵循的API。接口在无类型JS中可能会丢失,它的一个主要好处是,你可以为任何实现相同接口的类定义一个变量,并安全地调用它的任何方法。

  1. interfaceAnimal {  
  2.            speak()  
  3.          }  
  4.         classDog implements Animal{  
  5.            speak() {  
  6.              console.log("Woof!")  
  7.            }  
  8.          }  
  9.         classCat implements Animal{  
  10.            speak() {  
  11.              console.log("Meau!")  
  12.            }  
  13.          }  
  14.         classHuman implements Animal{  
  15.            speak() {  
  16.              console.log("Hey dude, what's up?")  
  17.            }  
  18.          }  
  19.         //if we had Interfaces in JS we could safely do:  
  20.          let objects = [newDog(), newCat(), newHuman()]  
  21.          objects.forEach(o => o.speak())  

这在普通JS中是无法做到的。当然可以通过定义speak方法并覆盖它的类来实现同样的目的。但话又说回来,也可以在任何其他强OOP语言中这样做,接口更加清晰和简洁。

抽象类

每当我尝试用代码进行全OOP时,肯定会错过JS中的抽象类。抽象类定义并实现方法,但永远不会被实例化。它是一种对可以扩展但不能直接使用的常见行为进行分组的方法。它绝对可以在当前的JS领域内实现,而不会造成太多的破坏。

静态多态

静态多态允许我们在同一个类中多次定义相同的方法,但是使用不同的签名。换句话说,重复名称,但要确保它接收到不同的参数。现在我们使用JS有了rest参数,这允许我们有任意数字,然而,这也意味着必须向方法中添加额外的代码来处理这个层次的动态。

相反,如果可以更清楚地区分方法签名,那么可以直接将相同行为的不同风格封装到不同的方法中。

JavaScript的类出现了什么问题?
JavaScript的类出现了什么问题?

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

静态多态性通常通过查看方法中接收的参数类型来实现。然而,由于JS的工作方式,我们知道这是不可能的。

受保护的属性和方法

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

我一直在努力把JS称为OOP语言,直到我看到一种不用引用原型链就能处理类内部的方法,我才会继续努力下去。为什么他们不能继续扩展原型继承模型,而不是给我们这个便宜的类版本呢?这是一个由来已久的问题。

现在,我要对添加的语法糖说声谢谢,也会继续关注未来的新的基于oop的特性。

 

 

责任编辑:华轩 来源: 读芯术
相关推荐

2021-02-08 08:04:52

JavaScript语言OOP

2012-07-30 09:49:44

云计算

2014-09-28 10:28:59

Docker云计算

2012-08-07 09:37:23

虚拟化

2022-05-05 08:00:00

团队敏捷流程

2011-11-30 15:28:32

在线协作系统

2022-06-13 10:07:13

物联网开发物联网

2023-05-31 07:32:37

2020-11-02 13:25:45

Redis数据库开源

2020-06-15 08:06:25

ES数据

2023-11-08 14:03:47

数据可视化数字化转型

2024-12-09 09:30:00

适配器模式设计模式代码

2024-11-05 08:16:04

HTTP/3HTTP 2.0QUIC

2023-10-27 13:31:18

线程安全多线程

2019-04-26 13:01:16

ServiceMesh微服务架构

2021-12-15 23:42:56

Webpack原理实践

2021-07-13 07:52:03

ReactHooks组件

2020-06-12 09:40:32

消息队列Java线程

2022-04-04 07:51:32

Web框架

2021-07-29 07:55:20

React Fiber架构引擎
点赞
收藏

51CTO技术栈公众号