?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*lb9uOxL-in_ij9Pj9CWvHA.png&valid=false)
TLDR;
Javascript 是一种极其强大的编程语言,具有多种语言的特性。不过其中大多数完全是垃圾,它们只会抑制工程师编写高质量代码的能力,如果我只使用对象和函数,它就好多了。
最好的 Javascript 是简单、干净的,并且不受类、this、继承和装饰器等肮脏的所谓功能的污染。
JavaScript 的问题
Javascript 是一种令人难以置信的语言。 最令人难以置信的是自它被发明以来它已经取得了多么大的进步。大多数语言都遵循创造、废弃和消亡的自然顺序。当弱编程语言不具备所需的能力时,它们就会被新语言超越并最终消亡。
JavaScript 是该规则的例外。尽管 Javascript 作为运行网络的技术很弱,但它并没有被允许消亡。相反,新的功能被敲入、胶合并用绳子和电线绑在外部。Javascript 就成了一个棚户区,其功能堆放在一个从一开始就受到质疑的核心引擎之上。
社区的问题
真正的问题不是 Javascript,而是工程师被教导思考 Javascript 的方式。他们被告知每个语言功能都是特殊的、有用的,并且都有一个合适的时机和场景来应用,所以你应该学习每个功能并尽可能使用它们。这是错误的!
我们以原型继承为例。它很奇怪,很丑陋,而且在现代编程语言中没有地位。仅仅因为它存在于语言中,但并不意味着我们应该拥抱它。
解决方案
我对所有那些肮脏的特性说不。特别是,我不使用类、 this
关键字、装饰器或依赖于任何形式的继承。我所有的 Javascript 代码都是对象和函数。
我不使用类
类只是一个用状态初始化的对象。真的,就只是这样!
下面的代码本质上是相同的,唯一的区别是,类解析器需要 new 关键字,而 const 解析器不需要。

?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*GbNZjMhElTxd3NcE2ZUA3A.png&valid=false)
那么我为什么要使用一个类呢?如果您正在考虑继承,请考虑以下代码片段。
?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*3jhfW9BNTCfOCt1ibwlyRg.png&valid=false)

你可以用类做的任何事情,我都可以用对象和函数来做 —— 而且这并不是很困难。
我不使用装饰器
实际上,我很喜欢装饰器的想法。简单来说,装饰器是一个包装另一个函数的函数,在底层函数进行处理之前和/或之后进行可选处理。
为了实现这一目标,Javascript 创造了一种你必须学习才能使用的格式。相比之下,我更喜欢 Python 方法。在Python中,装饰器是一个接受函数作为参数并返回函数的函数。
以下是我如何在 Javascript 中实现这一点:

?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*TIE3YMXKdPXTwFxMtBEZow.png&valid=false)
在此示例中,我们使用 uppercase
和 emphasize
装饰器装饰 speak
函数,以创建 scream
函数。实际上,我们不是在装饰函数,而是在组合函数。这不是一个错误,而是一个功能。不管怎样,我们正在实现装饰器一样的功能。
But Why?
有几个原因导致我将自己限制在对象和函数上。一般来说,它有助于保持我的代码简单、可读、快速,并且总体上更好。我更喜欢。
类倾向于积累状态
这不是一条硬性规定,而是我在整个职业生涯中看到的一种趋势。就我个人而言,我非常喜欢确定性的代码,并且,在可能的情况下,更喜欢使用纯函数。如果你遵守规则,你可以通过 Class 实现这些目标。然而,它似乎永远不会持续下去。即使我遵守规则,后续接手的工程师也不太可能认同我的观点。
随着时间的推移,类倾向于存储更多属性,并添加更多依赖于这些属性的函数。当类真正失去控制时,这些属性在任何给定时间的正确值对于方法的正确功能变得至关重要。变成了状态管理的噩梦。
我想将这种状态复杂性带到外层,将状态带到外层是函数式编程的一个原则。有状态代码和导致副作用的代码应该被带到我们软件的最高级别,并且该顶级级别以下的所有内容都应该是严格确定性的,并且在可能的情况下是纯粹的。

使用对象,我可以在模块范围内编写确定性函数,并从我的对象实例中调用它们,仅传递必要的内容。这迫使工程师手动将状态传递给函数,并希望能够让工程师重新考虑他们要添加的内容。
你可以通过类来做到这一点,但代价就是,当其他人深入研究您的代码并找到一个类时,他们会不假思索地增加状态。
类更难测试
这一点与上一点紧密结合。一个类的状态越多,测试它就越困难。
当我们通过使其成为不可变对象来消除类的状态性,然后将复杂的函数放置在可以单独测试它们的模块范围中时,我们就显着降低了测试的复杂性。
我非常喜欢单元测试的代码覆盖率。由于一些痛苦的经历,我可以告诉你,有状态类的代码覆盖率没有任何意义。另一方面,如果我的类是模块范围内具有确定性函数的不可变对象实例,并且我已经通过单元测试覆盖了 100% 的确定性函数,那么我可以确信逻辑实际上已被覆盖。

?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*FI-XHsOsMg2rZkagGguQww.png&valid=false)
我们的摩托车类别有点荒谬-
在编写测试时,像对象颜色这样看似无关的东西对速度的影响可能并不明显。
?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*0qHfF1rq5I_zeNR_fLU7jw.png&valid=false)

在函数和对象版本中,为 getSpeed
函数编写测试会向开发人员大声疾呼,颜色(必需的参数)对结果有一些影响。
对象减少认知负荷
作为一个人,为了理解类方法,我必须将所有状态加载到我自己的头脑中,然后考虑给定它所依赖的状态的运行时行为。作为开发人员,这通常意味着我正在扫描类方法中的关键字,例如 this
或 self
来识别依赖状态。
确定性函数通过它们的参数定义它们的依赖关系。作为一种趋势,这使得它们更容易理解和推理。
装饰器永远将装饰器绑定到函数
当我在 Javascript 中使用 @decorator
功能时,我将装饰器函数永远绑定到装饰函数。这完全破坏了单独对我的函数进行单元测试的能力。
让我们看一下 talk/scream 装饰器示例的一个版本。此功能已转换为使用@decoartor功能。

?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*54uDoDzwaOG6GCC-NE7vtA.png&valid=false)
如果不执行装饰器,我无法测试 speak
方法。作为重视质量单元测试的人,这是不可接受的。
装饰器需要了解晦涩的格式
请看下面的代码片段,我们的强调装饰器,并告诉我:什么是目标?什么是名字?什么是描述符?

?imageUrl=https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2F1*VdNJ1H6oh3J8lWpDFzfH6A.png&valid=false)
装饰器的定义简单而美观------一个接受一个函数并返回一个函数的函数。 JavaScript 用复杂的格式玷污了真正优雅的模式。
装饰器需要 this
正如我们在前面两个装饰器中所看到的,它们需要使用 apply
和 this
来正确设置装饰方法的上下文。
我想分享一下我对 this
的看法......这在 20 年前是一个错误,在今天也是一个错误。亲爱的上帝,请 JavaScript 请停止围绕它构建语言功能,工程师请亲爱的上帝,请停止围绕它编写代码。
顺便说一句,如果我正在面试你,并要求你解释 javascript 中的 this
关键字,正确的答案是"我不确定,我不使用那个过时的垃圾,它只会让我的代码不必要地复杂化。"