最棒的 JavaScript 是简单明了,没有被这些所谓的垃圾特性,比如类、this、继承和装饰器所沾染。
TLDR; (摘要)JavaScript 是一种非常强大的编程语言,具有多样化的语言特性。说实话,这些年来,大多数特性简直没用,反而限制了工程师写出高质量代码的能力。我只用对象和函数,感觉好多了。
关于JavaScript的问题Javascript 是非常棒的语言。关于 Javascript 最令人惊叹的事情是它自发明以来的巨大进步。大多数语言的发展路径是被创造、淘汰直至消亡。当弱语言不够强大时,它们会被新的语言超越,最终消失。
JavaScript 就是那个不按常理出牌的语言。
尽管JavaScript很薄弱,因为它运行着Web,所以它不能被废弃。相反,新的特性被添加、拼凑,并用各种方式绑在外部。
今天我们又熟悉又讨厌又爱的 JavaScript 是一个特性的贫民窟,这些特性堆叠在一个从一开始就备受质疑的核心引擎之上的。
社区遇到的问题真正的問題不是JavaScript,而是工程師被教导如何去思考和使用JavaScript的方式。他們被教导每个语言特性都有其独到之处和用途,并且都有其适用的场合和时机,因此,他们应该学习并尽可能地应用每一个特性。
不是真的。
我们以原型继承为例。它既奇怪又丑陋,它在现代编程语言中完全没有地位。仅仅因为这种语言里有它,并不意味着我们应该接受它。
解决办法我反对所有那些肮脏的特性。我直接拒绝。尤其重要的是,我不会使用类、this
关键字、装饰器,也不会依赖任何形式的继承。我的Javascript代码纯粹由对象和函数构成。
类其实就是一个被赋予了状态的对象,真的,就这么简单!
以下两个代码片段在功能或作用上是一致的。两者之间的唯一区别在于,类实例化解析器需要 new 关键字,而常量解析器则不需要。
那我为什么要用类呢?如果你在想继承,可以看看下面这段代码。
任何类能做到的事,我用对象和函数也能做到,而且这并不难做。
我不做装饰这行我其实很喜欢装饰器这个概念。遗憾的是,那个 Javascript 委员会在决定这种格式的那天晚上,喝醉了不该喝的酒,已经酩酊大醉。
简单来说,装饰器是一个函数,它装饰另一个函数,在被装饰的函数执行前后做一些可选的处理。
要做到这一点,Javascript 创建了一种你需要学习的语法。我更倾向于 Python 的方式。在 Python 中,装饰器 (decorator) 是一个接受函数作为参数并返回一个函数的函数。
这是我用 Javascript 实现的:
不带 @
的 JS 装饰器
在这个例子中,我们用 uppercase
和 emphasize
装饰器装饰了 speak
函数,从而创建了 scream
函数。实际上,我们并没有在装饰,而是在进行组合。这并不是错误,而是特性之一。无论怎样,我们还是达到了装饰的基本目的。
有几条原因让我只使用对象和函数。简而言之,这样做可以使我的代码更简洁、易读、运行更快,并使代码更“好”。我特别喜欢这种“更好”的感觉。
类倾向于累积状态这并不是一条严格的规定,而是在我的职业生涯中观察到的一种趋势。我个人强烈偏好确定性的代码,以及在可能的情况下,纯函数。如果你能自律,你可以通过类来达成这些目标。然而,这似乎总是不能持久。即使我自律,也不太可能接下来的10个工程师都会跟我有同样的想法。
随时间推移,类会慢慢积累更多的属性,并添加更多依赖这些属性的方法。当类变得难以控制时,这些属性在任何时刻的正确性变得对方法至关重要。也就是我们所说的“状态噩梦”。
这种有状态的复杂性是我希望 揭示出来 的。将状态揭示出来是函数式编程的一个原则。有状态的代码和可能会产生副作用的代码应该上升到软件的最高层,这一层之下的所有代码都应该是严格确定性的,并尽可能做到纯粹。
不可变的对象实例,具有模块作用域内的确定性函数。
通过对象,我可以在模块级编写确定性函数,并从对象实例调用它们,仅传递必要的信息。这强制手动传递状态给函数,并且希望这样,使工程师在添加内容时更加谨慎。这会促使工程师在添加内容时更加小心。
你可以用类来做到这一点,但已经有三十多年的先例表明不应该这样去做。当其他人查阅你的代码并发现一个类时,他们会不假思索地增加类的状态。
类更难测试了这一点和前面提到的一点紧密相连。一个类的状态越多,它就越难进行测试。当我们通过将该类变为不可变对象来移除其状态,然后将复杂的功能移至模块作用域内,使其可以独立进行测试时,我们大大降低了测试的复杂性。
我是单元测试代码覆盖率的忠实粉丝。因为一些痛苦的经历,我可以告诉你,状态类的代码覆盖率 几乎毫无意义。 ,另一方面,如果我的类是不可变的对象,模块作用域的函数都是确定性的,并且我已经用单元测试覆盖了所有确定性函数,这样我可以确定逻辑已经被完全覆盖。
摩托车分类
我们的摩托车类别有些荒诞——大家都认为红色的摩托车跑得最快——但它确实展示了现实中的一个例子,其中结果是由政府决定的。
在编写测试代码时,可能不会意识到,例如颜色,其实会影响速度。
一个不可变的摩托车对象
在函数和对象版本里,为getSpeed
函数编写测试会让开发者注意到颜色这个必需的参数会影响结果。
作为一个人,为了理解一个类中的方法,我必须把所有相关状态装在我的脑子里,然后思考运行时的行为。这通常意味着我会在方法中寻找像 this
或 self
这样的关键字来识别依赖的状态,看看它们是什么。
这类函数通过参数来确定依赖关系,通常来说,这使得理解和推理它们变得更加容易。
装饰器永远绑定到函数上当我使用 Javascript 中的 @decorator
特性时,我会将装饰器函数永久绑定到被装饰的函数上。这样我就不能单独进行该函数的单元测试了。
让我们来看一个使用 speak/scream 的装饰器示例的版本。这个示例已经转换为使用 @decorator
特性。
正在工作的装饰者
我无法不运行装饰器就测试speak
方法。作为一个注重单元测试质量的人,这对我来说完全无法接受。
请看以下代码片段,我们的装饰器函数,并告诉我:目标是什么?名称是什么?描述符又是什么?
强调装饰
唉,JavaScript 用复杂的格式扭曲了一个真正优雅的模式。装饰器的定义简洁且优美——一个函数,它接受并返回一个函数。
装饰器要求这个就像我们在两个装饰器中看到的那样,它们需要使用 apply
和 this
来正确设置被装饰方法的运行环境,其中 apply
和 this
是关键的编程关键字用来设置上下文。
如果你能和我一起稍微打开心扉一会儿,我想和你们分享一下我对这的看法……20年前这是一个错误,今天它依然是个错误。JavaScript,请不要再围绕它构建语言特性,工程师们,请不要继续围绕它编写代码。
顺便提一下,如果我在面试你,问你解释一下javascript中的this
关键字,正确的回答应该是“我不是很清楚,我不使用那个老古董玩意儿,它只会无谓地增加代码的复杂度而已。”
我是Ray,我是Vessel的联合创始人工程师,我创建了Radash、ACP设计模式和Exobase库。我并不想要你们的钱,我只是想在一个不那么糟糕的代码库中工作。所以请关注我、评论和互动,让我们一起做出更好的实践。