JavaScript 干净代码的敌人详解

到现在,我们应该有一个相当清晰的图像,当我们说干净代码是什么意思。 在前一章中,我们探讨了可靠性、效率、可维护性和可用性的原则。 总之,这些指导我们编写更干净的代码,但如果我们不小心,我们仍然可能被发现。 在本章中,我们将探讨干净代码的敌人:那些可能阻碍我们编写可靠、高效、可维护或可用代码的东西。

这些敌人都不应该被视为你的敌人; 相反,他们应该被看作是干净代码的煽动者。 我们需要全面地看待这些潜在的有害因素,并在我们的代码库、团队和工作场所密切关注它们。

具体来说,我们将在本章中介绍的敌人包括:

最糟糕的 JavaScript 特性也是最好的。 它是一种非常普遍的语言,必须以非常快的速度发展和适应。 语言本身及其在浏览器中的位置促成了这种普遍性。

JavaScript 是一种极具表现力和多样性的语言,它的函数灵感来自于 Lisp 和 Scheme,原型继承自 Self,还有一种类似于 c 的语法,反映了 Java。 它是一种有许多范例的语言。 无论您是想以典型的面向对象方式、原型方式还是完全的函数式方式进行编程,JavaScript 都能满足您的需求。 JavaScript 的灵活性和它在更广泛的 web 堆栈中的位置也使它非常适合初学者。 用它你可以立刻多产,这正是布兰登·艾希的初衷。 它的目的是让设计师和程序员都能很容易地学会,为他们提供编写浏览器的能力,而浏览器曾经只是一个单一用途的平台。 然而,曾经不起眼的浏览器已经成长为一组非常广泛和复杂的互补抽象。

JavaScript 本身以及它在客户端和服务器端(甚至更远!)上的广泛应用集的发展意味着该语言已经被推向和拉向了上千个不同的方向。 大量的框架、库、衍生语言(例如 CoffeeScript)、语言扩展(例如 JSX)、编译器、构建工具和其他抽象已经涌现出来,并试图以新的和独特的方式利用 JavaScript。 这些工具共同构成了 JavaScript 的版图,它非常丰富和多样化。 有无数的方法可以做同样的事情,结果,我们几乎不能希望做正确的事情。 这就是为什么我说 JavaScript 的普遍性既是它自己最大的敌人也是它自己最大的资产。

在本书中,我们将探讨一些基本概念,这些概念将教会我们批判性地思考干净代码的本质,并将允许我们在并不总是满足代码整洁的语言和环境中编写干净的代码。 如果使用得当,JavaScript 的效力和表达能力会让您感到惊讶,并且,只要投入一定的时间和精力,它在可靠性和可维护性方面可以与任何其他语言相媲美。

干净的代码不仅与语法有关,也与促进它的过程和原则有关。 无论我们的代码在孤立状态下是多么完美和美丽,它通常是作为项目的一部分编写的,与团队一起编写的,由不可靠的人和不可靠的过程管理。 只有看到和理解这些错误,我们才有希望预防或避免它们。

现在我们都在做更有挑战性的工作。 JavaScript 被限制在带有时髦导航滚动功能的不起眼的小册子网站的日子已经一去不复返了。 网络的缔造者们肩负着建设更宏伟项目的重任。 随着抽象技术之塔发展到新的高度,这些项目的复杂性只会增加。 因此,如果我们要真正地编写干净的代码,我们必须广泛地考虑这种复杂性。 我们必须超越我们的代码基础,考虑我们工作的团队和组织的环境。

将管理视为敌人似乎表明管理者本身应该受到责备,但事实并非如此。 在本节中,我们将发现,个人的文化实践使得发布干净的代码具有挑战性。 其中包括发布的压力、糟糕的参数和缺乏所有权。

交付代码的压力,通常是由于最后期限或其他管理命令,是软件世界中一直存在的令人讨厌的力量。 对于外部的利益相关者或管理者来说,期限是一件伟大的事情; 它似乎提供了确定性和可问责性,但对项目工作人员来说,它可能只会被视为强制执行不受欢迎的妥协。 有时候,做出的第一个妥协就是代码质量。 这并不是故意的,只是优先完成而不是质量的自然结果。

A stakeholder, in this context, is any individual or organization that relies on the output of your work. Usual stakeholders include project managers, other teams within the same organization, outside clients, and users.

当有交付压力时,有几种方式会导致代码质量缓慢下降。 其中包括:

  • 文档:开发人员,当匆忙的时候,将无法花费必要的时间来确保他们的代码和 api 是正确的文档化。 现有的文件将会萎缩。
  • 架构:开发人员将开始专注于他们需要做的最必要的更改,而忽略代码的更大的架构结构以及它是如何相互关联的。 依赖关系将变得混乱,架构将随着时间的推移而分裂,最终创建意大利面条式的代码。
  • 一致性:无论是架构上还是语法上,一致性都会受到影响。 多个不同的开发人员,可能彼此孤立,以最快的方式匆忙地构建东西。 在无意的情况下,他们可能会忽视沟通和标准的建立,导致一致性降低。
  • 测试:编写测试通常需要时间,重构测试也需要时间来适应新的需求。 现有的测试可能会被禁用或删除。 因为没有时间,所以不会编写新的测试。
  • 最佳实践:当他们的时间被拉长时,开发人员将开始在他们的代码中走捷径,而不采取谨慎和注意,以确保他们的软件适合其目的。 他们将绕开最佳实践,转而选择快速和精心设计的解决方案。 在 web 上,这通常会导致用户界面难以访问和使用。

当最后期限开始迫近时,前面的事项是首先要处理的。 如果我们不小心,我们可能会得到以下二级效应:

  • bug:测试和文档缺失,代码的架构基础受到威胁,漏洞不断的代码将开始成为常态。 在 Q&A 过程中可能会发现许多这样的错误,但用户也会发现许多其他错误。 代码及其 api 和 ui 的脆弱性将会增加,给用户带来更多负担。
  • 不开心的用户:由于用户看到的 bug 数量增加,软件的可用性水平降低,他们的工作效率和幸福感会下降。 他们可能会开始避免或放弃这个平台,转而寻找更高质量的替代平台。
  • :疲惫的开发人员,不得不不断放弃他们最好的原则,将开始变得疲惫。 他们可能会对在团队中继续工作的前景感到沮丧。 由于他们的精神健康和整体满足感受到威胁,他们会开始离开。

所有这些影响,如果持续的时间足够长,就会合并在一起,导致项目的失败。 因此,解决造成这种不计后果的高速增长的潜在压力至关重要。 快速交付代码的压力通常是由那些对软件项目中可能出现的缓慢退化没有很强的工作知识的力量所激发的。 这种缺乏知识的部分原因可能是他们不受自己决策的长期影响。 他们可能会假设,当交付的东西满足涉众的批准时,就结束了。 但是正如我们所知道的,仅仅因为快速发布的代码满足了即时需求并不意味着它遵守了良好的质量水平。 质量差的代码可能会产生许多负面的连锁反应,这些影响只有在实现后数周或数月才能完全实现。 几个月后,涉众可能会发现自己对质量的放缓和下降感到恼火,没有意识到这是他们最初施加的压力导致的。

解决这一混乱局面的办法在于在装运时间(T1)和技术债务(T3)之间达成关键的妥协。 技术债务会随着时间累积。 它描述了需要解决的缺陷,以保持代码库健康和良好的工作秩序。 这可能包括修复 bug、编写测试、重构旧模块或集成工具以提高代码质量。 从根本上说,技术债务是所有工作,理想情况下,将是自然开发周期的一部分,但由于时间限制,它被搁置到后面。 还有其他因素决定了技术债务的扩散,但时间是最大的因素。 不偿还我们的技术债务肯定会导致代码萎缩和项目最终失败。

当涉及到项目管理时,你可以利用无数条建议和流程。 我不会在这里详细介绍,但我将分享一些启发式方法,你可以使用它们来确保一个健康的代码库:

  • 在没有测试的情况下,不要发布功能或修复 如果没有测试,回归可以在任何时候发生。 测试是一种防御技术,以确保我们的代码在持续的基础上的正确性。
  • 经常还清技术债 可能每周一次,或者每月两次,尝试让每个人都承担技术债务,即任何被认为可以提高代码库健康程度的工作。
  • 定期与涉众沟通,以表达与代码和项目健康状况相关的约束和成本。 不要过度承诺发货或销售不足的问题。

作为开发人员,我们并不总是能够控制项目的管理方式。 尽管如此,我们应该总是能够轻松地提出关注并提倡能够培养干净代码的过程。 第 18 章沟通与倡导详细介绍了如何开展这一工作。

世界上似乎没有哪个行业能够逃脱这些指标。 这种对衡量事物的疯狂痴迷,既是一种类似邪教的痴迷,也是一种产生必要反省和改变的真正需要。 在软件工程领域,我们对这种需求并不陌生。 作为程序员,我们对能够让我们深入了解代码的指标非常感兴趣:

  • 有多少虫子?
  • 运行这段代码需要多长时间?
  • 我有多少测试覆盖率?

然而,管理人员和其他涉众通常会隐藏他们自己的利益和指标。 在这些参数中,更臭名昭著的是用来衡量开发人员的输出或生产力的参数:

  • 有多少行代码或提交?
  • 我们发布了多少功能?
  • 我们编写了多少行文档?

如果出于正确的原因,这些都是很好的问题。 例如,如果我们在讨论是否重构特定的类/实用程序时,将代码行作为复杂性的代理,那么代码行就是一个有用的指标。 但许多指标与他们试图衡量的东西完全分离。

非技术经理或涉众可能认为编写一定数量的代码应该总是花费相同数量的时间。 当一个曾经在一天内编写了 200 行代码的开发人员最近花了 10 天时间提交了 10 行代码时,他们可能会感到困惑。 当然,他们的困惑表明了对编程过程及其混乱复杂性的严重误解。 但这些误解是普遍存在的,所以我们需要警惕它们。

对于糟糕参数的明确解决方案是推动并创造更好的参数。 为了创造良好的度量标准,我们必须知道我们想要回答的潜在问题是什么,然后集思广益来回答这个问题。 让我们来看一个例子:

| 【t】The question【t】The question【t】 | 不良度量 | why it's badwhy it's bad** | 更好的度量或方法** | | 我们的工作效率高吗? | 行代码/提交 | 程序员可以合理地花费许多天的时间来解决只需要一行更改的关键 bug。 | 询问开发人员并探究是什么拖了他们的生产力; 组织团队回顾以发现需要改进的地方。 | | 我们是否在为用户提供价值? | 提供的特性数量 | 用户可以从少而高质量的特性中获得更多的好处。 | 创建参数或 A/B 实验来判断哪些功能正在被使用和享受。 关注每个功能的质量。 | | 我们是否在编写有用的文档? | 的文档 | 开发人员可能最终只记录他们熟悉的内容,而不是最需要记录的代码库区域。 | 创建一个跟踪文档使用情况的指标。 通过询问开发人员来辨别哪些代码区域没有被充分记录。 | | 我们是否有一个经过良好测试的代码库? | 测试覆盖率 | 如果它只度量某些代码行是否被调用,那么它可能只会被少数非常广泛的集成测试愚弄。 | 将传统的测试覆盖率与其他指标结合使用。 跟踪回归中经常出现 bug 的区域。 | | 我们的代码库有 bug 吗? | 数量的错误 | 代码库可能在应用的某个区域中有许多实际上未使用过的 bug。 某些区域的 bug 可能未被报告。 | 不要计算错误; 相反,应该关注并衡量用户和开发人员的幸福感。 根据 bug 对用户的影响对它们进行优先排序。 |

在组织或团队中固守糟糕的度量标准可能会导致优化错误的东西。 那些更关心编写更多代码行的开发人员将对代码的底层质量不那么感兴趣。 被迫发布更多特性的开发人员将在最佳实践和干净的代码上妥协,优化速度和交付。

重要的是要确保我们追踪的任何参数都符合现实,我们不会纯粹根据这些参数来判断成功。 当您看到指标与我们的干净代码原则背道而驰时,要特别小心。 随着时间的推移,如果一个度量标准被追求得过于雄心勃勃,它可能最终会破坏它试图衡量的东西。 这是通过一个被称为古德哈特定律的效应来实现的:

"When a measure becomes a target, it ceases to be a good measure."                                                                                                        – Marilyn Strathern

所有权是代码库健康的一个关键原则,它依赖于与代码健康相关的个人。 这里的所有权并不意味着一段代码属于一个人,其他人不能使用它。 相反,它意味着一段代码是由一个人或一群人培养的,其持续的健康和可靠性是关键优先事项。

缺乏所有权会导致干净代码的主要原则在以下方面受到影响:

  • 可靠性:代码的正确性和稳定性会随着时间的推移而萎缩,因为新的更改会在不知不觉中产生脆弱性。 代码的持续稳定性没有受到监控或照顾。
  • 效率:代码不被任何人直接测量或观察,潜在的假设是它只是工作。 随着时间的推移,它的效率可能会下降。
  • 可维护性:让许多非所有者快速且考虑不正的更改会导致非内聚架构,使长期的维护更加困难。
  • 可用性:代码的文档和一般可用性不会被任何人考虑或监控,导致其萎缩,最终,一个软件变得复杂和繁琐。

正确运用所有权可以从根本上改变上述原则的萎缩:

  • 可靠性:代码的正确性和持续的稳定性将受到关注和监控
  • 效率:将在持续的基础上衡量和评估代码的效率
  • 可维护性:代码将保持其体系结构和语法的单一愿景
  • 可用性:文档将不断更新,代码的可用性将持续关注

从根本上说,所有权是指对代码具有ongo**ing**关注的个人或团队。 要做到这一点,一定程度的自我或骄傲是必要的。 个人或团队必须与代码的持续运行状况有某种利害关系。 通常是组织或管理文化导致了健康或不健康的所有权水平,因此,再次强调,适当地沟通和提倡流程和动态是至关重要的,这将允许我们,程序员,确保我们的代码的干净和健康。

缺乏所有权还会带来更严重、更难以想象的后果。 由于缺乏自豪感和对工作的监护感,程序员更有可能因为无法实现他们对工作的自豪感和自我价值感的需求而感到倦怠。 由于没有所有权,团队成员可能无法在任何一个领域培养高水平的理解,这意味着团队或组织的一般知识受到影响,每个人只能以非常浅薄或粗略的方式理解代码库。

Beware of too much ego in ownership! Ego is a delicate trait. There is always the risk of too much ownership, which can result in a stubborn and defensive culture where insiders don't let outsiders make changes, and where strong and self-centered opinions run rife. Beware of this. Remember the key tenets of usability and maintainability. These will guide you toward kindness and openness toward those who would wish to use your code or make changes to it.

程序员,作为创造者,永远都在给世界留下深刻印象,所以我们几乎不可能不为我们的工作感到自豪。 如果不加以控制,这很容易导致我们编写代码只是为了给别人留下深刻印象,并提升我们的优越感,而不考虑我们编写的代码是否可维护或可用。 但是,如果我们天性的自我不被允许蓬勃发展,那么我们将不会对我们的工作感到骄傲,也不会倾向于在我们所做的事情中培养卓越。 因此,在编程中,就像在生活的其他领域一样,关键是保持自我的平衡,我们保留它的好部分,而不让它的坏部分过多地影响事情。

Ego, in this context, is our selfhood; the ways in which we identify with ourselves and how we express ourselves in the world. All programmers have an ego and its effects on the code they write are numerous.

作为一名年轻的程序员,我发现我的自我意识经常占据上风。 我不敢说这是一个普遍的真理。 这只是我的经验。 每当我发现一个新的 JavaScript 特性时,我都会尝试在我的下一段代码中利用它。

这方面的一个例子是使用位操作符来实现地板效果。 传统上,取底数——将一个数字四舍五入到最接近的整数——可以使用语言提供的本地方法:

Math.floor(65.7); // => 65

但是,当时我更喜欢使用位操作符来实现相同的结果:

~~65.7; // => 65
0|65.7; // => 65

这里发生了什么? 按位运算符(包括~&|等)用于改变操作数上的位,但作为一个副作用,它们首先将其操作数转换为 32 位整数。 这意味着他们会抛弃小数。 为了利用这种隐式转换到整数而不改变整数的值,我们可以执行,例如,使用双波浪号(~~)执行双位反转。 这实际上是对操作数的所有位进行反运算,然后再对它们进行反运算。 我们也可以用 0(0|...)执行逐位 OR,它总是返回非零操作数的位,因此通过利用副作用(整数转换)而不改变基础值,产生相同的效果。

至关重要的是,需要注意的是,这种副作用在功能上并不匹配Math.floor对负数的地板行为。 注意下面两个表达式的区别:

Math.floor(-25.6); // => 26
~~(-25.6);         // => 25

很容易看出这些神秘技术的诱人之处。 它们的使用似乎表明语言理解水平很高,这对自我很有吸引力。 这类似于使用不必要的长或复杂的词来传达简单的意思:说起来很有趣,但会疏远听者。

这样的技术通常会导致代码的可维护性降低。 我们代码的维护者不应该理解很少使用的操作符的内部工作,应该能够相信我们不会鲁莽地使用语言内部的副作用来实现可以通过更熟悉和明显的方法实现的结果。

复杂或罕见的语法通常是自私自利代码的载体。 另一个例子是误用逻辑操作符来指定控制流:

function showNotification(message) {
  hasUserEnabledNotifications() && (
    new Notification(message).show() &&
    logNotificationShown(message)
  );
}

前面的代码可以用一个IF语句更常规、更清晰地表示:

function showNotification(message) {
  if (hasUserEnabledNotifications()) {
    new Notification(message).show();
    logNotificationShown(message);
  }
}

对于更多人来说,这更清晰,更熟悉,更容易读懂。

有些人认为,我们应该能够自由地使用整个语言的全部功能,利用它的所有特性和副作用来编写更简洁、更高效的代码。 如果我们唯一的目标是编写有效的代码,那么这是一个很好的态度。 但是编写整洁的代码需要采用一种深思熟虑的方法,使用能够提供更多可读性的技术,避免使用适得其反的技术。

记住,从根本上讲,代码是关于沟通意图的。 交流对听者和说话者同样重要。 以自我为中心的代码往往以这种方式失败; 它将您的代码的熟悉程度限制在少数拥有与您相同知识的精英中。 这并不理想。 我们应该始终考虑到那些必须阅读、使用和维护我们代码的人的不同知识和能力。 这种担心应该优先于我们的自我意识。

代码很少是单独编写的; 我们经常和人们一起把项目变成现实。 因此,干净的代码既取决于您的方法,也取决于整个团队的方法。 拥有代码库的团队不断地决定他们用来实现目标的工具、约定和抽象。 因此,团队成员必须能够很好地沟通和分享观点,将这些观点塑造成一个清晰的结果。 有时候,妥协是必要的。 妥协往往会打击自尊心。

JavaScript 及其工具容易受到强烈意见的影响。 随着时间的推移,我们每个人都在使用不同的方法中获得了经验,通常是通过辛苦和痛苦,最终形成了一套我们认为最好的方法的信念。 然而,这些信念可能并不总是与我们同事的一致。 当出现分歧时,解决问题的途径是不明确的。 如果没有解决方案,团队和代码库就会分裂,造成更大的破坏。

想象一下亚当和苏珊之间的情景:

Adam: We should use the Foo testing framework; it's more reliable and simply better. Susan: No, we should definitely use Baz; it's far superior and has a proven track record.

可能有许多不同的方式来解决这个分歧。 例如,我们可以建议,双方都构建自己的案例,并继续讨论每个测试框架的各种优点。 这可能会解决问题。 但同样地,它可能不会。 争论可能会持续下去,在个人之间划了一个楔子,使代码库处于不稳定的状态,而没有一个确定的测试框架。 在这种情况下,解决问题的途径并不总是清晰的,但可以清楚的是,如果涉及到不妥协的自我,解决问题的可能性就更小。 如果亚当和苏珊都能开始看对方的观点,拓宽他们的视野,不再固守自己的观点,那么解决问题的道路就会变得更加清晰。

自我,作为一种微妙的特质,也是我们对自己能力和观点的信念和信念水平的原因。 毫无疑问,一定程度的自信对于编程中的创造和解决问题是至关重要的。 尤其是在科技行业,冒充者综合症似乎是一种普遍现象。 冒名顶替症候群的特征是感觉自己是一个冒名顶替者-,不知怎么的,你不适合或不足够有能力胜任你的角色,而你觉得周围的人似乎比你更有能力。

可以认为,它在软件行业的流行是由于固有的复杂性和专业的财富。 我们最多只能希望在一个相对狭窄的领域拥有高水平的熟练程度,但永远不可能精通所有领域。 当我们在日常工作中四处走动时,我们总是意识到所有我们不知道的事情,这可以理解地造成一定程度的焦虑,并对自己卑微的能力缺乏信心。 这种感觉有时会导致压力、疏远和对自己能力缺乏信心。

这可能会产生下列负面结果:

  • 缺乏决断力:缺乏对自己能力的信心会导致在对代码架构做决定时缺乏信心; 不知道该走哪条路线往往意味着选择了默认路线,这尤其容易受到货运狂热分子的影响。
  • :缺乏自信可能导致较少的冒险和更少的大胆的决定,但有时需要做出这样的决定,以推进项目或代码库。 例如,考虑到重构的成本,选择一个更可靠的 UI 或测试框架可能是一个巨大而大胆的风险,但可以导致代码健康状况的整体改善。
  • :对自己的观点和技能缺乏信心会导致不那么重要的交流发生,例如,程序员和项目的涉众之间。 在这里,沟通并不意味着要外向或健谈,而是要确定关键问题,并对它们有足够的信心来倡导改变。

编程的行为是一种交流我们意图的行为,也就是说,在这个世界上留下印象,也许以一种很小的方式,我们相信一件事情应该运行的方式。 这本身就是一项大胆的行动和技能,我们不应视之为理所当然。 如果你正在阅读这篇文章,并担心自己可能缺乏特定的特质或能力,我提供以下建议:这个星球上没有人完全有能力。 每个人都有自己的优点和缺点。 正是每个人的多样性和他们不同的能力决定了项目和代码库的成功。 即使你有冒名顶替综合症的感觉,也要承认这样或那样的感觉是很自然的,尽管如此,你所提供的可能比你想象的要多。

在 20世纪早期,人们观察到一些美拉尼西亚文化会进行模仿西方技术和行为的仪式,比如用木头和粘土建造跑道和控制塔。 他们这样做是希望食物等物质财富能送到他们手中。 这些奇怪的惯例之所以出现,是因为他们之前曾观察到货物通过西方飞机运送,并错误地认为是跑道本身召唤了货物。

如今,在编程中,我们使用术语cargo cultcargo cult来宽泛地描述复制模式和行为,而没有完全理解它们的真正目的和功能。 当程序员寻找解决方案在线和复制粘贴他们找到的第一段代码不考虑其可靠性或安全性,他们参与货物崇拜行为,试图完成一些任务,利用代码似乎是负责在其他上下文。

货物崇拜通常包括以下过程:

  1. 这个人被嵌入到一个稍微不熟悉的技术环境中
  2. 人们看到了他们想要模仿的效果
  3. 这个人复制似乎能产生预期效果的代码

这种行为可以在组织上和技术上发生。 程序员的任务有时是将他们几乎没有专业知识的完全不同的技术依赖联系在一起,他们通常除了接受 cult 之外别无选择。 而组织往往没有时间去考虑所有的基本原则,最终往往会从其他组织中吸取流行的行为和流程。

为了说明 cargo cult 的行为,让我们想象一个程序员的任务是向他们的 Node.js 服务器添加一个新的 HTTP GET 路由。 他们需要添加/about_us路线。 他们打开routes.js文件,在它的许多行中,找到以下代码:

app.use('/admin', (req, res, next) => {
  const admin = await attemptLoadAdminSection(req, res); 
  if (admin) {
    next();
  } else {
    res.status(403).end('You are not authorized');
  }
});

Express。 然而,不幸的是,程序员并不精通 Express API。 他们看到前面的代码,并寻求模仿它为自己的目的:

app.use('/about_us', (req, res, next) => {
  attemptLoadAboutSection(req, res);
  next();
});

不幸的是,您可能已经知道,这个程序员犯下了货物崇拜的行为。 他们已经复制了用于将流量路由到管理部分的代码,并假设他们应该使用类似的代码将流量路由到关于页面。

在这样做的过程中,他们错过了几件事:

  • admin 路由实际上是一个中间件,用来阻止未授权的用户访问/admin
  • app.use()方法只能用于中间件,不能用于直接 GET 路由
  • 调用next()是只有中间件才有兴趣做的事情

如果程序员花时间阅读 Express 文档,他们会发现正确的方法更类似于下面的:

app.get('/about_us', (req, res) => {
  loadAboutSection(res);
});

这是一个非常简短的例子。 通常情况下,货物崇拜的行为更为复杂。 它可能不涉及代码的直接复制,但可能只涉及模式或语法的微妙复制。 我们可能会对前面的例子摇头,确信我们永远不会做这样的事情,但我们可能已经做了,以不那么明显的方式。

从事项目的程序员通常会正确地继承现有代码库中的命名、语法和空格约定。 他们可以不经过思考就这样做,自然地反映并遵循现有的范例,而不在每个步骤中应用他们的关键技能。 这并不一定是负面的:这是对惯例和表述一致性的合理维护。 这些都是重要的品质。 但同样地,这种盲目的复制往往会导致冗余代码的无意义扩散,或者更糟糕的是,由于代码误解而产生的负面影响。

假设你是第一次做程序员,你想给下面这个有点奇怪的对象添加一个hobby字段:

const person = {
  "name": ("James"),
  "location": ("London")
};

很容易想象,在添加新字段时,您可能倾向于复制现有语法:

const person = {
  "name": ("James"),
  "location": ("London"),
  "hobby": ("kayaking")
};

对于新手来说,这是完全合理的。 他们被嵌入到一个不熟悉的环境中,看到了他们希望模仿的效果,所以他们采用了产生这种效果的模式。 对于有经验的人来说,这甚至是可以理解的行为,他们想要在不干扰环境的情况下对代码进行最小的必要修改。

这段代码中没有任何惊人的错误。 它的功能。 然而,如果我们要编写最具可维护性和效率的代码,那么我们应该采用更广泛接受和常规的约定和语法。 因此,在这种情况下,前面的代码有两个具体的问题:

  • 用双引号括住每个键名(不必要!)
  • 把每个值都用括号括起来(没必要!)

非货物崇拜版本的文件可能是这样的:

const person = {
  name: "James",
  location: "London",
  hobby: "kayaking"
};

然而,这个文件和对象可能会在未来的几个月甚至几年里继续存在。 没有人会质疑它的语法,因为他们会认为它这样一定是有原因的。 遵循既定的做事方式会让人感到舒适和轻松。 不去挑战它往往更容易。 这种形式的货物崇拜是更阴险的类型,并给项目和团队带来许多惯性。 我们盲目地采用实践,而不去质疑它们的持续有效性和适用性。

就像代码可以被无意识地复制一样,工具也可以。 作为 JavaScript 程序员,我们面对的是快速变化的工具和库。 每个月似乎都会发布一个新的实用程序或工具。 围绕着这些工具的兴奋和夸张为货运狂热的爆发创造了肥沃的土壤。 程序员可能会开始使用这些新工具,因为他们确信它们的优点,而没有对它们进行充分的理解或适当地考虑它们对手头项目的适用性。 工具可能是由公司或管理人员规定的,非程序员和程序员可能纯粹根据工具的流行程度或新奇程度来权衡,而不考虑它实际上是如何工作的,或者它与当前的方法有何不同。

货物崇拜往往是一股非常有说服力的力量,它告诉我们,只要我们使用这种方法或工具,我们所有的问题都将得到解决。 当然,这种情况很少发生。 我们可能只会把当前的一系列问题换成一系列新的问题。 因此,在决定一个工具时,无论是框架、库还是任何第三方抽象或服务,我们都应该使用一种经过深思熟虑的方法,问自己以下几个关键问题:

  • 适用性
  • 可靠性:它是否可靠工作,并将继续这样做?
  • 可用性:使用是否简单,是否有良好的文档记录?
  • 兼容性:它与现有代码库集成良好吗?
  • :它能适应我们不断变化的需求吗?

为了避免货物崇拜,我们应该尽量避免轶闻和道听途说,而应该更倾向于详细的比较分析,在其中我们比较和对比各种可能性,以找到最合适的。

在本章中,我们了解了干净代码中最常见的敌人。 我们讨论了 JavaScript 本身是一种语言,当使用不当时,会导致不干净的代码。 我们还探讨了团队和个人的缺陷。 我们认识到,干净的代码不仅是代码的一个特征,而且是一种文化,必须在整个组织和我们自己的头脑中培养这种文化。

在下一章中,我们将探讨一些众所周知和不太为人所知的干净代码原则,并将我们目前所学到的知识集成到一些具体的 JavaScript 抽象中。

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

邱岳的产品实战 -〔邱岳〕

程序员的数学基础课 -〔黄申〕

TensorFlow快速入门与实战 -〔彭靖田〕

TypeScript开发实战 -〔梁宵〕

性能工程高手课 -〔庄振运〕

体验设计案例课 -〔炒炒〕

etcd实战课 -〔唐聪〕

Web 3.0入局攻略 -〔郭大治〕

结构写作力 -〔李忠秋〕