前端不需要 Class

4,032 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

导读

本文是个短篇,需要解释一下,这里的前端,指较狭义的与开发页面较紧密的工作。至于独立开发 npm,服务端等场景另当别论,后面会有论述。

背景

Class 几乎可以跟 OOP(面向对象编程) 划等号。OOP 的特点:封装、继承、多态,代表语言 JavaC++。Erlang 语言的作者 Joe Armstrong 说过:

面向对象语言的问题是,它们永远都要随身携带那些隐式的环境。你只需要一个香蕉,但却得到一个拿着香蕉的大猩猩...以及整个丛林。

在 JS 里,函数是一等公民,可以直接声明并使用。但是在 Java 里,函数一定要依附于 Class 存在,所以如果你只想单纯的声明并使用一个函数时,不得不先声明一个 Class,上述名言形象的说明了这点。对于习惯了 OOP 语言的程序员,这甚至已经形成了一种不需要思考的条件反射。也许因为目前高校里教 Java、C++ 是主流,所以笔者经常会在前端代码中看到这种“条件反射”的遗留,多数情况都是不必要的。

本文会从 OOP 的三大特点来分析为什么 Class 在前端不是必要,甚至应该避免使用的。之后会辩证地补充适合使用 Class 的场景。

正文

为什么不需要 Class

封装

页面是所有人都看得见的具体的东西,它可以被抽象为一个个的组件的组合,所以前端天然适合组件式开发的模式,组件就是前端最基本的封装单位,人们非常容易理解组件的概念。

后端却不一样,很难有一个形象具体准确的对象来承载大家的认知(因为看不见),内聚的范围和粒度很难统一,所以需要创造一个抽象的概念,这就是“对象”或者 Class 的作用。

以上是笔者自己粗浅的理解,如果正确,那么 Class 这个抽象,从封装角度来说,对于前端不是必须的,甚至是应该被禁止的。因为其与组件化同时存在,会让封装的粒度产生分歧,这对于项目维护来说,绝不是好事。

继承

前端有 Object 数据类型,通常被称为“对象”,不需要声明 Class,不需要 new 就可以直接定义,这是前端的常识,也是 JS 的特点之一。没有 Class 怎么实现继承呢?我们来看一段代码:

const basePeople: People = {
  name: '',
  age: 0,
  shout() { alert('hahaha') },
};

const zhangsan: People = {
  ...basePeople,
  name: 'zhangsan',
  age: 18,
};

解构赋值即可,从继承来说,Class 也不是必须的。多继承也可以如此实现。

多态

以笔者极为粗浅的理解(可能有误),多态本质是为了解决方法(函数)的复用和代码可读性问题。再究其本质原因,是因为声明新函数在 OOP 语言中成本太大,不得已而为之的一种方式。在函数是一等公民的 JS 当中,声明和使用新函数的成本非常低,更意味着更多的灵活性。所以可以说多态这种特性,本身在前端就是不需要的。

什么场景适合用 Class

正如上文“封装”小节里提到的,如果项目的性质不是跟页面打交道,就不存在天然的“组件”概念,也就需要 Class 这种抽象来辅助。所以结论就很明显了:

不与页面打交道,如封装 npm 包、写服务端代码等,抽象度较高的项目,使用 Class 和 OOP 的思想,还是很有必要的。但是也并非必须,比如 React 的 Hooks 这种写法,可以说提供了另外的思路,但也有其适用的局限性,这里就不展开了,暂时只记住前半句的结论就行了。

结语

总结一下结论:

  • Class 在以处理页面为主的项目中应该避免使用
  • 非页面项目,可以采用 OOP Class 的方式,但也有其他选择,如 Hooks

无论是 OOP、AOP、FP 还是其他的什么编程范式,脱离了具体使用场景去讨论,都是耍流氓。虽然它们的确具有高度的抽象性和普适性,但落实到实践层面,还是有很大的使用与效果差异的。

所以作为一个负责实施的工程师,笔者主张一切都要从实践出发,从如何切实提高生产效率出发。先有实践,后有理论,自下而上的思考问题。而不是反过来,否则就犯了“本本主义”的错误。

鉴于笔者能力有限,本文可能会存在谬误,但笔者自信文中反映出的问题是切实存在的,即“不明确的封装粒度,会造成项目维护上的问题,Class 会引起这种问题”。即使文档有些许偏颇,如果能引起更多人的思考,也勉强算是抛砖引玉了。

检验一流智力的标准,就是在头脑中同时存在两种截然相反的想法时,仍能保持行动能力。 —— F.Scott Fitzgerald