<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ 代码质量 - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ 代码质量 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 19 May 2026 10:03:07 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/code-quality/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 代码整洁实践：敏捷软件开发中的代码优化指南 ]]>
                </title>
                <description>
                    <![CDATA[ 构建可扩展的软件应用程序需要编写干净的代码，这种代码简单到任何开发人员都能理解。 在这篇文章中，我将解释并演示什么是干净的代码。然后，我将分享我最喜欢的代码整洁模式，用于构建现代敏捷应用程序。 我不会使用复杂的术语。我会用简单明了的 JavaScript 示例来展示核心概念。开门见山，这就是我的风格。 让我们开始吧。 目录  1. 坏代码的成本            2. 清洁编码者 vs. 混乱编码者            3. 如果你的代码一团糟，AI 也救不了你 🗑️           ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/the-clean-code-handbook/</link>
                <guid isPermaLink="false">69630570d350d0045e97cbae</guid>
                
                    <category>
                        <![CDATA[ 代码质量 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tsukistar ]]>
                </dc:creator>
                <pubDate>Sun, 11 Jan 2026 02:21:24 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2026/01/----_20260111101926_191_113.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/the-clean-code-handbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">The Clean Code Handbook: How to Write Better Code for Agile Software Development</a>
      </p><!--kg-card-begin: markdown--><p>构建可扩展的软件应用程序需要编写干净的代码，这种代码简单到任何开发人员都能理解。</p>
<p>在这篇文章中，我将解释并演示什么是干净的代码。然后，我将分享我最喜欢的代码整洁模式，用于构建现代敏捷应用程序。</p>
<p>我不会使用复杂的术语。我会用简单明了的 JavaScript 示例来展示核心概念。开门见山，这就是我的风格。</p>
<p>让我们开始吧。</p>
<h2 id="">目录</h2>
<ol>
<li>
<p><a href="#heading-the-cost-of-bad-code">坏代码的成本</a></p>
</li>
<li>
<p><a href="#heading-clean-coder-vs-messy-coder">清洁编码者 vs. 混乱编码者</a></p>
</li>
<li>
<p><a href="#heading-ai-cant-save-you-if-your-code-is-a-mess">如果你的代码一团糟，AI 也救不了你 🗑️</a></p>
</li>
<li>
<p><a href="#heading-12-clean-code-design-patterns-for-building-agile-applications">用于构建敏捷应用的 12 个整洁代码设计模式 ⚖️</a></p>
<ul>
<li>
<p><a href="#heading-use-names-that-mean-something">🌿 使用有意义的名称</a></p>
</li>
<li>
<p><a href="#heading-keep-functions-laser-focused-srp">🔨 保持函数的专注性 (SRP)</a></p>
</li>
<li>
<p><a href="#heading-use-comments-thoughtfully">🚪 谨慎使用注释</a></p>
</li>
<li>
<p><a href="#heading-best-practices-for-writing-good-comments">⚡ 编写优秀注释的最佳实践</a></p>
</li>
<li>
<p><a href="#heading-make-your-code-readable">🧩 让你的代码可读</a></p>
</li>
<li>
<p><a href="#heading-test-everything-you-write">🏌️ 测试你写的所有内容</a></p>
</li>
<li>
<p><a href="#heading-use-dependency-injection">💉 使用依赖注入</a></p>
</li>
<li>
<p><a href="#heading-clean-project-structures">📂 清理项目结构</a></p>
</li>
<li>
<p><a href="#heading-be-consistent-with-formatting">🤹‍♂️ 格式保持一致</a></p>
</li>
<li>
<p><a href="#heading-stop-hardcoding-values">✋ 停止硬编码值</a></p>
</li>
<li>
<p><a href="#heading-keep-functions-short">🤏 保持函数简短</a></p>
</li>
<li>
<p><a href="#heading-follow-the-boy-scout-rule">⛺ 遵循童子军原则</a></p>
</li>
<li>
<p><a href="#heading-follow-the-openclosed-principle">🏟️ 遵循开放/封闭原则</a></p>
</li>
</ul>
</li>
<li>
<p><a href="#heading-modern-best-practices-to-help-you-write-clean-code-a-summary">帮助你编写整洁代码的现代最佳实践：总结 🥷</a></p>
</li>
<li>
<p><a href="#heading-automated-tools-for-maintaining-clean-code">保持代码整洁的自动化工具 ⚓</a></p>
<ul>
<li>
<p><a href="#heading-1-static-analysis">1️⃣ 静态分析</a></p>
</li>
<li>
<p><a href="#heading-2-automated-code-formatting">2️⃣ 自动代码格式化</a></p>
</li>
<li>
<p><a href="#heading-3-continuous-integration-ci-testing">3️⃣ 持续集成 (CI) 测试</a></p>
</li>
<li>
<p><a href="#heading-4-cicd-pipelines">4️⃣ CI/CD 流水线</a></p>
</li>
</ul>
</li>
<li>
<p><a href="#heading-the-role-of-documentation-in-agile-software-development">文档在敏捷软件开发中的角色 🚣</a></p>
</li>
<li>
<p><a href="#heading-conclusion">结论 🏁</a></p>
</li>
<li>
<p><a href="#heading-frequently-asked-questions-about-clean-code">关于代码整洁的常见问题 🧯</a></p>
</li>
</ol>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xh3j6ccn1hc3euc3lfyl.png" alt="敏捷软件开发梗图" width="3125" height="1250" loading="lazy"></p>
<p>在敏捷中，变化是唯一不变的，干净的代码是你的盔甲。它使你具有适应性、敏捷性，最重要的是，它使你能够掌控局面。</p>
<p>事实是：如果你想在软件开发行业中生存，编写整洁代码绝非可有可无，而是必须掌握的能力。幸运的是，我们人类通过努力和练习终能掌握整洁代码的精髓。</p>
<h2 id="heading-the-cost-of-bad-code">坏代码的成本</h2>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdai6npb55j71sguj6kl.png" alt="混乱代码与整洁代码成本对比图表 by shahan" width="500" height="600" loading="lazy"></p>
<p>为了解释这个堆叠条形图，在初始开发阶段，坏代码比整洁代码的变更成本<strong>稍微</strong>高一些。</p>
<p>但是当我们进入维护和重构阶段时，这个差距显著扩大，坏代码的成本几乎是整洁代码的两倍。</p>
<p>到了代码变成遗留代码时，坏代码的变更成本达到100%——现在升级它非常昂贵，而整洁代码仍然更易于管理，仅为45%。</p>
<p>目前，美国关于软件低质量成本的最新分析仍是信息与软件质量联盟（Consortium for Information and Software Quality，CISQ，网站：cisq.org）发布的2022年报告。在这份报告中，经估计，2022年软件低质量给美国经济造成了至少2.41万亿美元的损失，其中技术债务（technical debt）约占1.52万亿美元。</p>
<p>你可以<a href="https://www.it-cisq.org/the-cost-of-poor-quality-software-in-the-us-a-2022-report/">在这里阅读更多内容</a>。</p>
<p>近期的讨论继续强调技术债务对软件质量和业务绩效的显著影响。</p>
<p>例如，<a href="https://vfunction.com/blog/how-to-manage-technical-debt">2024年的一项调查</a>指出，超过50%的公司认为技术债务占其IT总预算的四分之一以上。如果不加以解决，这确实会阻碍创新。</p>
<p>如你所见，毫无疑问，在软件开发中，坏代码是一个代价高昂的问题。</p>
<h2 id="heading-clean-coder-vs-messy-coder">清洁编码者 vs. 混乱编码者</h2>
<p>这是一个展示<strong>两种</strong>编码者历程的图表：</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c6ubf77uwipf4gtucw8q.png" alt="整洁代码 vs 坏代码图表" width="604" height="340" loading="lazy"></p>
<ul>
<li>
<p><strong>⚠️ 混乱编码者（红线）：</strong> 起步快但坠落得更惨。他们写的行数越多，问题也越多。</p>
</li>
<li>
<p><strong>⚡ 清洁编码者（蓝线）：</strong> 开始慢但保持一致。增长不会停止——它会加速。</p>
</li>
</ul>
<p>🫵 现在，你可以决定想走哪条线。</p>
<h2 id="heading-ai-cant-save-you-if-your-code-is-a-mess">如果你的代码一团糟，AI 也救不了你 🗑️</h2>
<p>当你陷入写代码的困境时，你可能会求助于 AI。但让我告诉你：如果你的代码一团糟，AI 也救不了你。</p>
<p>这就像是在沙子上建房子。没错，它会暂时站立，但一旦有强风或大浪，就会倒塌。</p>
<p>请记住：AI 只是一个工具。如果你不知道如何编写清晰、可扩展的应用程序，就是在为失败埋下伏笔。</p>
<p>我反复看到了这样的情况：那些熟悉五种编程语言的开发者，他们能构建应用程序、网站、软件，他们对算法和数据结构了如指掌。</p>
<p>但当面对大型项目或别人的混乱代码时，他们崩溃了。</p>
<p>他们就像一位能设计和建造自己飞机的航天工程师，但却不知道如何驾驶它们。他们在自己的代码中坠毁。</p>
<p>这曾经是我...... 从前的我。我会写上数千行代码，但却发现自己连上周写的东西都无法理解。对我来说，那是一片混乱。</p>
<p>然后我恍然大悟 —— 每个开发者都在为此苦苦挣扎。问题不在于我知道多少，而在于我如何组织和结构化我所知道的东西。换句话说，这取决于对编程艺术本身的理解。</p>
<p>我决定摆脱这个陷阱。经过五个月的密集工作——每天写作、设计和研究四到五个小时——我创造了当初学编程时梦寐以求的工具，一本完整的初学者指南：<strong>《整洁代码从零到一》</strong>。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737731329839/c4c862d9-7fdc-460a-ae2e-18b19468b6ec.png" alt="《整洁代码从零到一》封面图片：从混乱代码到杰作" width="1920" height="1080" loading="lazy"></p>
<p>如果你想了解更多关于这本书的信息，我会在本教程的结尾告诉你所有的细节。所以继续阅读以了解更多关于编写整洁代码的内容。</p>
<h2 id="heading-12-clean-code-design-patterns-for-building-agile-applications">构建敏捷应用程序的 12 种整洁代码设计模式 ⚖️</h2>
<p>如果你的代码不遵循这些现代整洁代码设计模式，你可能正在制造一个定时炸弹。这些模式是你的工具。掌握它们并享受项目成功的乐趣。让我一一展示给你看。</p>
<h3 id=""><strong>🌿 使用有意义的名称</strong></h3>
<p>将你的变量或函数命名为 b 或 x 并没有帮助。将它们称为何物以便于理解。以下是一个坏变量名与好变量名的例子：</p>
<pre><code>// 弱且模糊
let b = 5;

// 强且清晰
let numberOfUsers = 5;
</code></pre>
<p>那些写出不清晰名字的人不愿为自己的错误负责。不要成为那样的人。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736165724746/37b2edc3-3c68-47a8-ab6f-f131a2239a01.png" alt="Shahan创作的关于不良变量名和良好变量名的漫画" width="2000" height="800" loading="lazy"></p>
<h3 id="srp"><strong>🔨 保持函数的专注性 (SRP)</strong></h3>
<p>一个函数应该做到<strong>一件事</strong>——并做到完美。这就是单一职责原则（<strong>SRP</strong>）。</p>
<p>好的代码就像一把锤子。它只打一根钉子，不会打十根。例如，如果你招聘一个人来处理公司里的所有事情——财务、销售、市场营销、清洁工作等等——他们很可能会因为无法专注而惨败。同样的道理适用于你代码中的类。</p>
<p>🚧 当一个类或函数做超过一件事时，就会变成一团乱麻。调试它就像在倒着拼拼图。比如，如果你的类既要处理用户输入又要操作数据库，这不是多任务并行，而是逻辑混乱。将其拆分，遵循一个方法只做一件事的原则。</p>
<p><strong>🔥 我的法则：</strong> 你的代码为你工作。保持它精炼、专注且可控，否则它会控制你。以下是实现这一目标的方法：</p>
<pre><code>// 整洁代码：专注于单一任务
function calculateTotal(a, b) {
    return a + b;
}

function logTotal(user, total) {
    console.log(`User: ${user}, Total: ${total}`);
}

// 混乱代码：尝试做所有事情
function calculateAndLogTotal(a, b, user) {
    let total = a + b;
    console.log(`User: ${user}, Total: ${total}`);
}
</code></pre>
<p>🪧 任务混在一起，混乱也就随之而来。就这么简单。</p>
<h3 id=""><strong>🚪 谨慎使用注释</strong></h3>
<p>职业开发者中有一句名言：</p>
<blockquote>
<p>“代码自有其解释。”</p>
</blockquote>
<p>当有人走进房间时，你不会每次都解释门是做什么用的，对吧？你的代码也该如此。</p>
<p>加注释没错，但如果代码不加注释就看不懂，那代码本身可能就有毛病。</p>
<p>🪧 好的注释要讲“为什么”，而不是“怎么做”或“是什么”。如果开发者连代码是怎么工作的都看不懂，那他们更不可能理解背后的“为什么”。</p>
<p>以下是一些好的注释与坏的注释的简短例子。我还将向你展示一个编写清洁注释的实际项目。</p>
<p><strong>例子1：糟糕的注释 👎</strong></p>
<pre><code>// 将价格乘以数量以计算总数
const total = price * quantity;
</code></pre>
<p>这是一个<strong>糟糕的注释</strong>，因为它只是重复了代码已经说明的内容。代码 <code>price * quantity</code> 本身已经很清晰，因此这个注释没有增加任何有用的信息。</p>
<p><strong>好注释：👍</strong></p>
<p>如果代码清晰简明，<strong>你不需要注释。</strong></p>
<pre><code>const total = price * quantity;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736165891398/6a942ad7-5b09-4990-9c7f-95358dafcbf3.png" alt="Shahan创作的说明不必要注释与“无声注释”的图像" width="2000" height="800" loading="lazy"></p>
<p><strong>例子2：糟糕的注释 👎</strong></p>
<pre><code>// 检查用户是否登录
function isUserLoggedIn(session) {
    return !!session.user;
}
</code></pre>
<p>这个注释不好，因为它没有解释 <code>isUserLoggin()</code> 的存在原因。它只是解释了正在发生的事情。但我们已经知道这是一个身份验证函数。这个注释是浪费时间。</p>
<p><strong>好例子 👍</strong></p>
<pre><code>// 用户在访问受保护的资源之前已经通过认证
function isUserLoggedIn(session) {
    return !!session.user;
}
</code></pre>
<p>这是一个<strong>好的注释</strong>，因为它解释了代码存在的<strong>原因</strong>。它告诉我们函数在允许访问应用程序的敏感部分之前检查用户是否已认证。它关注的是更大的图景。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736166143011/b3ddae3d-41cf-4534-8f1a-af710579922c.png" alt="之前：“检查用户是否已登录”。之后：“用户在访问受保护的资源之前已经通过认证。”作者：Shahan。" width="2000" height="800" loading="lazy"></p>
<h3 id=""><strong>⚡ 编写优秀注释的最佳实践</strong></h3>
<ol>
<li>
<p><strong>解释“为什么”，而不是“什么”：</strong><br>
写注释是为了解释代码的目的或背景，而不是代码正在做什么。</p>
</li>
<li>
<p><strong>避免明显的注释：</strong><br>
不要为代码已经清晰的部分写注释。</p>
</li>
<li>
<p><strong>保持简短和精确：</strong><br>
写简洁的注释，方便阅读，并直接解释目的。</p>
</li>
<li>
<p><strong>定期更新注释：</strong><br>
过时的注释可能误导开发者，所以在代码更改时务必更新注释。</p>
</li>
</ol>
<p><strong>现实世界中的例子（使用好的注释） 🛒</strong></p>
<p>让我们把这些实践应用到一个真实项目中：一个大型电子商务应用程序。一个函数根据订单详情计算运输成本。下面是完整的代码，我将在下面解释每个注释：</p>
<pre><code>// 运输规则：
// - 订单超过 $100 免费运输
// - 低于 $100 的订单标准运输（$10）
// - 国际订单额外 $5

function calculateShipping(order) {
    let shippingCost = 0;

    // 检查订单是否符合免费运输条件
    if (order.total &gt;= 100) {
        shippingCost = 0; // 免费运输
    } else {
        shippingCost = 10; // 标准运输费用
    }

    // 为国际订单增加额外费用
    if (order.isInternational) {
        shippingCost += 5;
    }

    return shippingCost;
}

// 示例使用
const order1 = { total: 120, isInternational: false };
const order2 = { total: 80, isInternational: true };

console.log(calculateShipping(order1)); // 输出：0
console.log(calculateShipping(order2)); // 输出：15
</code></pre>
<p>在函数的开始，我们包含了一个注释来解释运输费用的规则。这使读者在不需要阅读完整代码的情况下获得逻辑概览。</p>
<pre><code>// 运输规则：
// - 订单超过 $100 免费运输
// - 低于 $100 的订单标准运输（$10）
// - 国际订单额外 $5
</code></pre>
<p>然后，第一个条件检查订单总额是否大于或等于 $100。这里的注释明确了为什么会应用免费运输。</p>
<pre><code>// 检查订单是否符合免费运输条件
if (order.total &gt;= 100) {
    shippingCost = 0; // 免费运输
}
</code></pre>
<p>第二个条件为国际运输应用附加收费。注释解释了为何增加额外费用。</p>
<pre><code>// 为国际订单增加额外费用
if (order.isInternational) {
    shippingCost += 5;
}
</code></pre>
<p><strong>为什么这些注释很好？</strong></p>
<p>想象一下你正身处一个 20 人的开发团队。六个月后，有人读到了 <code>calculateShipping</code> 这个函数。如果没有这些注释，他们可能得浪费大把时间去猜测为什么国际订单会有一笔额外费用。好的注释能阐明背后的原因，避免让人陷入数小时的烦躁和困惑中。</p>
<h3 id=""><strong>🧩 让你的代码可读</strong></h3>
<p>如果别人看你的代码感觉像是在“破译密码”，那你已经在给团队“埋坑”了。不信请看：</p>
<pre><code>// 干净：读起来像看故事一样
if (isLoggedIn) {
    console.log("Welcome!");
} else {
    console.log("Please log in.");
}

// 杂乱：感到困惑
if(isLoggedIn){console.log("Welcome!");}else{console.log("Please log in.");}
</code></pre>
<p>如果你的代码杂乱且难以阅读，它会让他人甚至是你自己感到困惑！想象六个月后回到自己写的代码却感觉像在读一种外语。可读性高的代码节省时间，减少错误，使每个人的生活更轻松。</p>
<p><strong>🍵 为什么可读性很重要？</strong></p>
<ol>
<li>
<p><strong>对于你自己：</strong> 当你在几周或几个月后重新访问代码时，整洁的代码能帮助你在不浪费时间弄清楚自己做了什么的情况下继续进行接下来的工作。</p>
</li>
<li>
<p><strong>对于你的团队：</strong> 如果其他人阅读你的代码，他们不应该像是在解谜题。整洁的代码让团队合作更顺畅，防止沟通不畅。</p>
</li>
<li>
<p><strong>更少的错误：</strong> 清晰的代码更易于调试，因为你能快速发现错误。</p>
</li>
</ol>
<p><strong>🧙‍♂️ 如何编写可读的代码</strong></p>
<p>让我们构建一个简单的程序来管理图书馆中的书籍。我们将使其整洁和可读，接下来我将分解这段代码：</p>
<pre><code>// 一个表示书籍的类
class Book {
    constructor(title, author, isAvailable) {
        this.title = title;
        this.author = author;
        this.isAvailable = isAvailable;
    }

    borrow() {
        if (this.isAvailable) {
            this.isAvailable = false;
            console.log(`You borrowed "${this.title}".`);
        } else {
            console.log(`Sorry, "${this.title}" is not available.`);
        }
    }

    returnBook() {
        this.isAvailable = true;
        console.log(`You returned "${this.title}".`);
    }
}

// 一个用于展示可用书籍的函数
function displayAvailableBooks(books) {
    console.log("Available books:");
    books.forEach((book) =&gt; {
        if (book.isAvailable) {
            console.log(`- ${book.title} by ${book.author}`);
        }
    });
}

// 示例使用
const book1 = new Book("The Clean Coder", "Robert Martin", true);
const book2 = new Book("You Don’t Know JS", "Kyle Simpson", false);
const book3 = new Book("Eloquent JavaScript", "Marijn Haverbeke", true);

const library = [book1, book2, book3];

displayAvailableBooks(library); // 显示可用的书籍
book1.borrow(); // 借阅一本书
displayAvailableBooks(library); // 再次显示可用的书籍
book1.returnBook(); // 归还这本书
displayAvailableBooks(library); // 最终列表
</code></pre>
<p>我们创建了一个 <code>Book</code> 类来表示每本书。它具有像 <code>title</code>、<code>author</code> 和 <code>isAvailable</code> 这样的属性来追踪其状态。</p>
<ul>
<li><code>borrow</code> 方法检查书籍是否可用。如果是，则将其标记为不可用并打印一条消息。</li>
<li><code>returnBook</code> 方法使书籍再次可用。</li>
<li><code>displayAvailableBooks</code> 函数循环遍历图书馆，只打印可用的书籍。</li>
<li>我们创建了三本书（<code>book1</code>、<code>book2</code>、<code>book3</code>）并将它们存储在一个 <code>library</code> 数组中。</li>
<li>我们借阅和归还书籍，展示了可用书籍列表如何变化。</li>
</ul>
<p>如你所见，可读代码不仅仅关乎风格。它能节省时间、防止错误，并确保你的代码在未来多年仍能发挥作用。</p>
<h3 id=""><strong>🏌️ 测试你写的所有内容</strong></h3>
<p>如果你不花时间编写测试，那么代码出故障时就别感到意外。如果你打算编写测试，请遵循这套单元测试策略，以便及早发现问题。</p>
<p><strong>什么是单元测试？</strong></p>
<p>具体来说，单元测试检查代码的各个部分（如函数或类）以确保其正常工作。就像在建造房子的墙之前检查每一块砖是否完好一样。</p>
<p>让我给你一个单元测试工作原理的例子：</p>
<pre><code class="language-javascript">class Calculator {
    add(a, b) { return a + b; }
    subtract(a, b) { return a - b; }
}

// 测试它（单元测试）
const calculator = new Calculator();
console.assert(calculator.add(2, 3) === 5, "加法失败");
console.assert(calculator.subtract(5, 3) === 2, "减法失败");
</code></pre>
<p>让我们来看看这段代码是怎么运行的：</p>
<p>首先，我们要先构建一个计算器类：</p>
<pre><code class="language-javascript">class Calculator {
    add(a, b) { return a + b; }
    subtract(a, b) { return a - b; }
}
</code></pre>
<p><code>Calculator</code> 类有两个方法：<code>add</code> 和 <code>subtract</code>。</p>
<ul>
<li><code>add(a, b)</code> 接受两个数字并返回它们的和。</li>
<li><code>subtract(a, b)</code> 接受两个数字并返回它们的差。</li>
</ul>
<p>接下来，我们设置测试：</p>
<pre><code class="language-javascript">const calculator = new Calculator();
</code></pre>
<p>在这里，我们创建一个 <code>Calculator</code> 类的实例以测试其方法。</p>
<p>然后我们编写测试用例：</p>
<pre><code class="language-javascript">console.assert(calculator.add(2, 3) === 5, "加法失败");
console.assert(calculator.subtract(5, 3) === 2, "减法失败");
</code></pre>
<p><code>console.assert(condition, message)</code> 检查条件是否为 <code>true</code>。如果是 <code>false</code>，则消息（“加法失败”或“减法失败”）将显示在控制台中。</p>
<ul>
<li>
<p><strong>第一次测试</strong>：<code>calculator.add(2, 3) === 5</code></p>
<ul>
<li>调用 <code>add</code> 方法，给出 <code>2</code> 和 <code>3</code>。</li>
<li>检查结果是否为 <code>5</code>。</li>
</ul>
</li>
<li>
<p><strong>第二次测试</strong>：<code>calculator.subtract(5, 3) === 2</code></p>
<ul>
<li>调用 <code>subtract</code> 方法，给出 <code>5</code> 和 <code>3</code>。</li>
<li>检查结果是否为 <code>2</code>。</li>
</ul>
</li>
</ul>
<p>那么如果出错了会发生什么呢？在这里解决出现的问题是相当简单的。在这种情况下，如果 <code>add</code> 或 <code>subtract</code> 方法不能正常工作，测试将失败。例如：</p>
<pre><code class="language-javascript">console.assert(calculator.add(2, 3) === 6, "加法失败");
</code></pre>
<ul>
<li>条件 <code>calculator.add(2, 3) === 6</code> 是 <code>false</code>。</li>
<li>控制台将显示：“加法失败”。</li>
</ul>
<p><strong>实际例子：测试一个登录系统 👥</strong></p>
<p>让我们测试一个简单的登录系统，看看单元测试在实际场景中如何工作。</p>
<pre><code class="language-javascript">class Auth {
    login(username, password) {
        return username === "admin" &amp;&amp; password === "1234";
    }
}

// 测试 Auth 类
const auth = new Auth();
console.assert(auth.login("admin", "et5t45#@") === true, "有效凭证登录失败");
console.assert(auth.login("user", "wrongpassword") === false, "无效凭证登录成功");
</code></pre>
<p>首先，创建 <code>Auth</code> 类：</p>
<pre><code class="language-javascript">class Auth {
    login(username, password) {
        return username === "admin" &amp;&amp; password === "1234";
    }
}
</code></pre>
<p><code>login</code> 方法检查用户名是否为 <code>"admin"</code> 且密码是否为 <code>"1234"</code>。如果两者都匹配，则返回 <code>true</code>，否则返回 <code>false</code>。</p>
<p>接下来，设置测试：</p>
<pre><code class="language-javascript">const auth = new Auth();
</code></pre>
<p>创建一个 <code>Auth</code> 类的实例。然后编写测试用例：</p>
<pre><code class="language-javascript">console.assert(auth.login("admin", "1234") === true, "有效凭证登录失败");
console.assert(auth.login("user", "wrongpassword") === false, "无效凭证登录成功");
</code></pre>
<ul>
<li><strong>第一次测试</strong>：检查有效凭证（<code>"admin"</code>，<code>"1234"</code>）是否成功。如果不成功，就会显示 “有效凭证登录失败”。</li>
<li><strong>第二次测试</strong>：检查无效凭证（<code>"user"</code>，<code>"wrongpassword"</code>）是否失败。如果没有失败，就会显示 “无效凭证登录成功”。</li>
</ul>
<p><strong>🌱 为什么测试会导致干净的代码：</strong></p>
<ol>
<li>
<p>为了提高代码的可测试性，你会自然而然地编写更短小、更专注的函数。</p>
</li>
<li>
<p>测试能验证你的代码在不同场景下的表现是否符合预期。</p>
</li>
<li>
<p>有了测试作为保障，你可以放心大胆地更新代码，因为你知道任何错误都会被测试及时发现。</p>
</li>
</ol>
<h3 id=""><strong>💉 使用依赖注入</strong></h3>
<p>将依赖硬编码就像在额头上纹上某人的名字——它是永久性的，可能是磨蚀性的，并且限制了你的选择。</p>
<p>那么，依赖注入做了什么？它通过将依赖关系作为参数传递来管理代码的关系。它是灵活、可适应且易于维护的。</p>
<p>为了演示其工作原理，我这里使用 Nodemailer 依赖项向用户发送电子邮件：</p>
<pre><code>// 依赖：使用 Nodemailer 发送电子邮件
const nodemailer = require('nodemailer');
function sendEmail(to, subject, message) {
    const transporter = nodemailer.createTransport({ /* 配置 */ });
    return transporter.sendMail({ from: "programmingwithshahan@gmail.com", to, subject, text: message });
}
</code></pre>
<p>⚠️ 为了避免风险，请确保避免将依赖关系<strong>硬编码</strong>。使用抽象或配置文件进行安全维护。</p>
<p>这只是一个例子。作为开发人员，你可能会使用数百个库或依赖项。</p>
<p>我并不是说你绝对不应该依赖依赖项/库，因为如今很难避免它们。但在将它们安装到你的编码项目中之前，你应该非常小心。</p>
<p>你应该检查组织的软件系统的安全性、性能、质量或功能。因为它们有时可能包含会毁掉你整个项目的风险。</p>
<p>🚧 永远要控制你的工具，不要让它们控制你。</p>
<h3 id=""><strong>📂 清理项目结构</strong></h3>
<p>一个组织良好的项目就像一座高端<strong>精品店</strong>与一堆<strong>垃圾堆</strong>的区别。</p>
<p>以下是每个文件夹的组织方式：</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xwyg9iqqcybz21lsgxz.png" alt="Shahan 的整洁代码项目结构图" width="1563" height="1250" loading="lazy"></p>
<p>如果你的代码库像个杂物抽屉，那你已经给未来的自己制造了麻烦。</p>
<p>让我们浏览上面的干净项目结构，以更好地理解它：</p>
<p><strong>1.</strong> <code>myProjet/src</code></p>
<p>这是整个应用程序的主容器。应用所需的一切都存储在这个文件夹中。它有子文件夹，可以保持整洁并在一个地方管理。</p>
<p><strong>2.</strong> <code>components</code></p>
<p>这是存放应用所有可重用部分的地方。你可以在多个地方使用这些组件，而无需再次构建它们。</p>
<p><strong>3.</strong> <code>services</code></p>
<p>这是应用的“大脑”。它在前端和后端为幕后工作提供支持。<code>emailService.js</code>、<code>userService.js</code> 和 <code>productService.js</code> 是 <code>services</code> 文件夹的一些示例文件。</p>
<p><strong>4.</strong> <code>utils</code></p>
<p>这包含了所有运行应用程序所需的小工具，使你的生活更轻松。例如，<code>formatedate.js</code>、<code>validateEmail.js</code> 和 <code>generateId.js</code> 是一些常见的 utils 文件，用于为整个项目制作可重用的组件。</p>
<p><strong>5.</strong> <code>tests</code></p>
<p>根据惯例，测试文件通常位于项目根级别的 <code>src</code> 文件夹<strong>之外</strong>。这样可以将生产代码（<code>src</code>）与测试代码（<code>tests</code>）分隔开，使其更清晰易于管理。查看下面结构：</p>
<pre><code>myProject/
├── src/              # 生产代码
│   ├── components/
│   ├── services/
│   └── utils/
├── tests/            # 测试文件
│   ├── components/
│   ├── services/
│   └── utils/
├── package.json      # 项目配置
└── README.md         # 文档
</code></pre>
<p>一些开发人员可能更喜欢在 <code>test</code> 文件夹中创建一个测试文件来测试所有内容。遗憾的是，一开始它看起来很整洁，但随着项目的发展，你将不得不寻找和搜索特定代码块。这样显得杂乱无章，并可能产生意外的测试结果。因此，强烈建议在 <code>tests</code> 文件夹中将它们分解成多个测试文件。</p>
<p><strong>一个实际的例子 📧</strong></p>
<p>让我为你创建一个干净、耐用的项目结构，以便在任何将来可能从事的项目中应用。不用说，干净的项目结构是构建可维护项目的基础。</p>
<p>根据我们之前的发送电子邮件应用程序的示例，我们将为该应用程序编写一个干净的项目结构。我们希望构建一个向用户发送电子邮件的应用程序。此应用的干净项目结构应如下所示：</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v6rlc5qiplgxz1h4dps.png" alt="Shahan 的邮箱应用整洁代码项目结构图" width="1563" height="1250" loading="lazy"></p>
<p>如你所见，我将每个子文件夹和文件都打包在应用的主容器 <code>src</code> 文件夹中。在 <code>src</code> 文件夹中，我们创建了 <code>components</code>、<code>services</code>、<code>utiles</code>。最后，我们在 <code>src</code> 文件夹外有一个可管理的 <code>test</code> 文件夹，以独立测试每个组件。这个测试文件夹与位于 <code>src</code> 文件夹中的生产代码没有任何关系。</p>
<h3 id=""><strong>🤹‍♂️ 保持格式一致性</strong></h3>
<p>别让你的代码看起来像是出自不同的十个人一样。请务必保持格式风格的一致性。</p>
<p>使用 <a href="https://prettier.io/">Prettier</a> 或 <a href="https://eslint.org/">ESLint</a> 等工具强制执行一致的风格。如果每个文件都看起来不同，你只是在制造没人愿意解决的混乱。</p>
<p>我认为格式一致性是编写整洁代码的最重要方面之一。</p>
<pre><code>// 始终使用2个空格进行缩进
function calculateArea(width, height) {
  if (width &lt;= 0 || height &lt;= 0) {
    throw new Error("维度必须是正数。");
  }
  return width * height;
}

// 添加有意义的空白以提高可读性
const rectangle = {
  width: 10,
  height: 20,
};

// 清晰的逻辑分离
try {
  const area = calculateArea(rectangle.width, rectangle.height);
  console.log(`面积: ${area}`);
} catch (error) {
  console.error(error.message);
}
</code></pre>
<p>让我们来分析一下，这段代码在哪些方面体现了“整洁之道”：</p>
<h4 id="1">1️⃣ 一致的缩进</h4>
<p>为什么用2个或4个空格？这很简洁、最小化，并且被许多JavaScript风格指南普遍接受。它不会让眼睛感到负担，并且代码结构清晰。如果你在这里用2个空格，那里用4个空格，就会让人感到困惑——而困惑时容易犯错。</p>
<h4 id="2">2️⃣ 有意义的空白：给代码留出呼吸空间</h4>
<p>在<code>rectangle</code>定义和<code>try</code>块之间的额外换行就像句子中的停顿——它让读者有时间去处理信息。</p>
<h4 id="3">3️⃣ 清晰的逻辑分离：模块化思维</h4>
<pre><code>try {
  const area = calculateArea(rectangle.width, rectangle.height);
  console.log(`Area: ${area}`);
} catch (error) {
  console.error(error.message);
}
</code></pre>
<p>看看代码逻辑是如何被分成清晰的部分：</p>
<ul>
<li>
<p>首先是计算（<code>calculateArea</code>函数）。</p>
</li>
<li>
<p>然后是输出（<code>console.log</code>）。</p>
</li>
<li>
<p>最后是错误处理（<code>catch</code>块）。</p>
</li>
</ul>
<p>每个任务都有其独立的空间和明确的职责。</p>
<h4 id="4">4️⃣ 可读的错误处理</h4>
<p>当你抛出错误或记录消息时，请整洁地格式化它们。不要使用模糊或隐晦的信息。看到这样的代码，开发者立刻就能知道出了什么问题。</p>
<pre><code>throw new Error("维度必须是正数。");
</code></pre>
<p><strong>🐦‍⬛ 常规的格式一致性建议：</strong></p>
<ul>
<li>
<p>在整个代码库中一致地使用2个或4个空格进行缩进。避免使用制表符，以维护在不同编辑器间的一致性。</p>
</li>
<li>
<p>将每行的字符数限制在最多100-120个以内，以防止水平滚动并提高可读性。</p>
</li>
<li>
<p>将相关逻辑集中在一起，用空行分隔代码块以突出强调其用途。</p>
</li>
<li>
<p>最后，避免过度对齐代码。相反，让缩进自然引导逻辑流程。</p>
</li>
</ul>
<h3 id=""><strong>✋ 停止硬编码值</strong></h3>
<p>硬编码值是一种懒惰的编码方式。以下是证明：</p>
<pre><code>// 糟糕：硬编码且僵化
function createUser() {
    const maxUsers = 100;
    if (currentUsers &gt;= maxUsers) throw "用户太多！";
}

// 简洁：动态且灵活
const MAX_USERS = 100;
function createUser() {
    if (currentUsers &gt;= MAX_USERS) throw "用户太多！";
}
</code></pre>
<p>你看，改变这个变量将来不会让你感到意外。你确切地知道在哪里可以找到它来改变不确定的值。</p>
<p>最好将固定值存储在全局配置（配置）文件中。</p>
<p>🪧 因此，无论如何避免硬编码。硬编码看似捷径，却可能让未来的自己（或他人）抓狂。</p>
<h3 id=""><strong>🤏 保持函数简洁</strong></h3>
<p>如果你的函数超过20行，可能它试图做得太多了。</p>
<p>短小精悍的函数总是能命中目标。</p>
<p>冗长的函数又乱又难读，而短小的函数则清晰且集中。以下是你应如何将大型函数拆解：</p>
<pre><code>function updateCart(cart, item) {
    addItemToCart(cart, item);
    let total = calculateTotal(cart);
    logTransaction(item, total);
    return total;
}

function addItemToCart(cart, item) {
    cart.items.push(item);
}
</code></pre>
<p>让我解释一下这段代码，以便你理解为何将大型函数拆分是一个明智的策略。</p>
<ol>
<li>
<p><strong>主函数：</strong> <code>updateCart()</code> 调用较小的辅助函数来处理特定的任务，如：</p>
<ul>
<li>
<p>将物品添加到购物车。</p>
</li>
<li>
<p>计算总价格。</p>
</li>
<li>
<p>记录交易细节。</p>
</li>
<li>
<p>最后，返回总价格。</p>
</li>
</ul>
</li>
</ol>
<p>这并不是一个试图做所有事情的长块代码，而是将任务委派给辅助函数。</p>
<ol start="2">
<li><strong>辅助函数：</strong> <code>addItemToCart()</code> 这个函数<strong>只</strong>负责将物品添加到购物车。如果你需要更改添加物品的方式（例如，检查重复项）。你只需编辑这个小函数，而不必在<code>updateCart</code>中一大块代码中寻找更改。这就是编写整洁代码函数的方式，令人愉悦和易于维护。</li>
</ol>
<p><strong>函数过长会怎样？ 💤</strong></p>
<p>假设你没有拆分<code>updateCart</code>函数。它可能看起来像这样：</p>
<pre><code>function updateCart(cart, item) {
    cart.items.push(item);
    let total = 0;
    for (let i = 0; i &lt; cart.items.length; i++) {
        total += cart.items[i].price;
    }
    console.log(`添加了${item.name}。总价现在是$${total}。`);
    return total;
}
</code></pre>
<p>这里的问题是什么？</p>
<ul>
<li>
<p>它试图做所有事情。</p>
</li>
<li>
<p>特别是当它变得更庞大时，难以阅读。</p>
</li>
<li>
<p>如果出了问题，你将浪费时间去找出问题的所在部分。</p>
</li>
</ul>
<p>现在，选择权在你手中：是继续坚持那种混乱的“全能一把抓”写法，还是开始实践“一个函数只做一件事”的整洁心态？</p>
<h3 id=""><strong>⛺ 遵循童子军规则</strong></h3>
<blockquote>
<p>离开营地时，要比你发现它时更整洁。</p>
</blockquote>
<p>让我来详细解释一下：你不能只顾着自己使用，却留下一个比之前更烂的摊子。那是极不负责任的行为。真正的专业人士总会想办法让事物变得比原来更好。</p>
<p>在编程中，这意味着 <strong>每当你触碰代码库，都要让它变得更好一点。</strong> 去清理冗余、重构混乱的部分、提升可读性。如果你不这么做，你只是在不断堆积垃圾，而这些“屎山”最终会崩塌并砸到你自己头上。</p>
<p>来看这个例子。在这里，我们没有选择优化，而是仅仅增加了一层又一层的复杂度：</p>
<pre><code>// 原始代码：难以阅读，变量命名不好
function calc(a, b) {
  let x = a + b;
  let y = x * 0.2;
  return y;
}

// 我们只是不断添加而未进行清理
function calcDiscount(a, b, discountRate) {
  let total = calc(a, b);
  let final = total - discountRate;
  return final;
}
</code></pre>
<p>之后：每次都有所改善。这是一个有纪律的程序员的工作方式——他们在改进中前行：</p>
<pre><code>// 改进后的代码：清晰的命名，重构以提高清晰度
function calculateSubtotal(price, quantity) {
  return price * quantity;
}

function calculateDiscountedTotal(price, quantity, discountRate) {
  const subtotal = calculateSubtotal(price, quantity);
  const discount = subtotal * discountRate;
  return subtotal - discount;
}
</code></pre>
<p>现在，任何人一目了然地看出发生了什么。因为我们将代码拆解为更小、更集中的函数。因此，增加新功能不会破坏现有功能。🏕️</p>
<h3 id=""><strong>🏟️ 遵循开放/封闭原则</strong></h3>
<p>这项设计原则建议：代码应当设计成允许扩展，而不必改变现有的基础。</p>
<p>你肯定希望是“增加功能”，而不是每升级一次就把代码“拆了重建”。为了适配新需求而去修改旧代码，简直就像每次买新家具都要把房子推倒重盖一样。这显然是不可持续的。</p>
<p>让我们来看看如何构建更聪明、更具扩展性的代码，让你在增加功能的同时，不会把其他部分搞坏。</p>
<h4 id="">重构前：违反原则的写法</h4>
<p>你写了一个处理支付的类——挺简单的，起初它只支持信用卡支付。</p>
<p>结果你老板突然冒出来说：“嘿，我们现在得接个 PayPal。”</p>
<p>因为你之前没心思学什么代码整洁之道，你的代码现在看起来就像一个从 1995 年过时企业级系统里爬出来的“意面怪物”。来，欣赏一下你亲手打造的这件“杰作”：</p>
<pre><code>class PaymentProcessor {
  processPayment(paymentType, amount) {
    if (paymentType === "creditCard") {
      console.log(`Processing credit card payment of $${amount}`);
    } else if (paymentType === "paypal") {
      console.log(`Processing PayPal payment of $${amount}`);
    } else {
      throw new Error("Unsupported payment type");
    }
  }
}

const paymentProcessor = new PaymentProcessor();
paymentProcessor.processPayment("creditCard", 100);
paymentProcessor.processPayment("paypal", 200);
</code></pre>
<p>唉！每新增一种支付方式（例如 Apple Pay、Google Pay 等），都需要修改 <code>processPayment</code> 方法。不用说，你在添加新功能时有可能破坏现有功能。如果你学过这个原则，你就不会陷入这个困境。</p>
<p>不要担心：我会帮你解决这个问题。首先，我们需要重构代码。我们将通过使用<a href="https://stackify.com/oop-concept-polymorphism/">多态</a>来扩展其功能，而不是修改现有的类：</p>
<pre><code>javascriptCopy code// 基础类
class PaymentProcessor {
  processPayment(amount) {
    throw new Error("processPayment() must be implemented");
  }
}

// 信用卡支付
class CreditCardPayment extends PaymentProcessor {
  processPayment(amount) {
    console.log(`Processing credit card payment of $${amount}`);
  }
}

// PayPal 支付
class PayPalPayment extends PaymentProcessor {
  processPayment(amount) {
    console.log(`Processing PayPal payment of $${amount}`);
  }
}

// 添加新的支付类型？只需扩展类！
class ApplePayPayment extends PaymentProcessor {
  processPayment(amount) {
    console.log(`Processing Apple Pay payment of $${amount}`);
  }
}

// 使用方法
const payments = [
  new CreditCardPayment(),
  new PayPalPayment(),
  new ApplePayPayment(),
];

payments.forEach((payment) =&gt; payment.processPayment(100));
</code></pre>
<p>现在，添加新的支付方式不再需要修改现有的 <code>PaymentProcessor</code> 类了，你只需要创建一个新的子类。这样一来，原有代码保持原封不动，意味着完全没有破坏现有功能的风险。</p>
<p>每种支付类型都有独立的类。比如添加 PayPal 支持，完全不会影响到老代码。现在你可以自信地回复老板：“没问题，我 5 分钟就能把这功能加上。” 升职加薪的机会正等着你呢！</p>
<p>我在我的书 <a href="https://codewithshahan.gumroad.com/l/cleancode-zero-to-one">从零到一的整洁代码</a> 中分享了更多的技巧。</p>
<h2 id="heading-modern-best-practices-to-help-you-write-clean-code-a-summary">帮助你编写整洁代码的现代最佳实践：总结 🥷</h2>
<p>现在，让我为你展示最佳实践，并总结这 12 条整洁代码设计原则，助你在敏捷应用开发中游刃有余。</p>
<h3 id="">🔎 常见代码异味及其修复方法</h3>
<ul>
<li>
<p>💊 重复代码： 如果你在复制粘贴代码，那你是在给自己挖坑。把代码抽离成函数，一次性把事做对。</p>
</li>
<li>
<p>🛤️ 过长方法： 如果一个方法长到需要滚动条，那它承载的逻辑就太多了。拆分它，让功能保持专注。</p>
</li>
<li>
<p>👑 万能对象： 没有任何一个类应该包揽所有活儿。简化职责，否则你的代码库迟早会变成一团乱麻。</p>
</li>
</ul>
<h3 id="">💬 高效注释实践</h3>
<ul>
<li>
<p>💭 何时注释： 只有在代码逻辑不够清晰时才写注释。如果代码已经很直观了，注释就是噪音。</p>
</li>
<li>
<p>🫗 清晰度： 注释应该解释 <strong>“为什么”</strong> 而不是“是什么”。如果你的代码非得靠注释才能读懂，那可能它写得太复杂了。</p>
</li>
<li>
<p>🌴 避免冗余： 别给显而易见的代码写注释。如果函数名是 <code>addNumbers</code>，就别再注释说它是用来“加数”的。</p>
</li>
</ul>
<h3 id="">🧼 整洁代码的重构技巧</h3>
<ul>
<li>
<p>🏭 提取方法：方法太臃肿？拆掉它。这不仅仅是为了整洁，更是为了掌控感。</p>
</li>
<li>
<p>🫕 重命名变量：如果变量名不能一眼看出用途，就改掉它。命名的精准度反映了思维的严谨度。</p>
</li>
<li>
<p>🍃 如果你的判断语句写得像代数题一样复杂，简化它。如果是 if (a == true)，直接写成 if (a)。</p>
</li>
</ul>
<h3 id="">🧪 测试与清洁代码</h3>
<ul>
<li>
<p>🧙 单元测试：像审讯嫌疑人一样测试每一行代码。不放过任何死角。</p>
</li>
<li>
<p>🏇 TDD（测试驱动开发）：先写测试。这不只是为了抓 Bug，更是为了在动笔写代码前，就搞清楚它到底该干什么。</p>
</li>
<li>
<p>🧽 清洁测试：测试代码也要写得和业务代码一样整洁。如果测试代码本身就乱七八糟，它就毫无参考价值。</p>
</li>
</ul>
<h3 id="">🐛 错误处理与整洁代码</h3>
<ul>
<li>
<p>⁉️ 异常处理：大胆使用异常。它们不仅能处理错误，还能让你的主逻辑免受错误处理代码的干扰。</p>
</li>
<li>
<p>🖍️ 快速失败：一旦发现异常，立即停止。别让错误雪球越滚越大，当场解决。</p>
</li>
<li>
<p>🚨 日志记录：记录日志要像记录犯罪现场一样。清晰、精准，且只记录必要的信息。</p>
</li>
</ul>
<h3 id="">🌱 代码审查及整洁代码</h3>
<ul>
<li>
<p>🚢 流程化：建立一套系统。拒绝“牛仔式”野路子开发。审查、评判、改进。</p>
</li>
<li>
<p>🔪 工具化：利用工具让审查变得轻松。工具不仅是为了纠错，更是为了培养规范性。</p>
</li>
<li>
<p>🧦 文化建设：营造一种“反馈是金”的文化。帮助团队学会如何给出和接受有建设性的批评。</p>
</li>
</ul>
<h2 id="heading-automated-tools-for-maintaining-clean-code">保持代码整洁的自动化工具 ⚓</h2>
<p>工具和自动化技术对于编写清洁代码非常有帮助。如果你没有使用合适的工具和自动化来节省时间，那就错过了机会。</p>
<p>你认为可以"凭眼力"判断代码质量？再想想吧。没有自动化，这会发生：</p>
<ol>
<li>
<p>👎 因为“太忙”而错过显而易见的错误。</p>
</li>
<li>
<p>🤕 你的代码在每个文件中看起来都不同，协作起来头疼。</p>
</li>
<li>
<p>🪦 部署失败，因为你跳过了关键测试。</p>
</li>
</ol>
<p>成功的开发者使用合适的工具来自动化代码和完成任务。以下是使用现代工具维护清洁代码的四种策略。</p>
<h3 id="1"><strong>1️⃣ 静态分析</strong></h3>
<p>静态分析实际上是一个代码检查器，它在早期找出潜在问题。最棒的是，它在<strong>运行前</strong>工作，捕获可能导致崩溃、停机或令人尴尬的错误。</p>
<h4 id=""><strong>它是如何工作的？</strong></h4>
<ol>
<li>
<p><strong>语法检查</strong>：检查代码中编写的语法是否正确。如果拼写错误或遗漏了闭合括号，它会立即指出。</p>
</li>
<li>
<p><strong>代码质量规则</strong>：像 ESLint 这样的工具强制执行规则，如一致的缩进、避免未使用的变量，以及遵循最佳实践。</p>
</li>
<li>
<p><strong>错误预防</strong>：识别逻辑错误，比如使用未定义的变量或进行无意义的比较。</p>
</li>
</ol>
<p>以下是静态分析的实际操作：</p>
<h4 id="">🚨 在静态分析之前：</h4>
<pre><code class="language-javascript">let sum = (a, b) =&gt; { return a + b; }
console.log(sume(2, 3)); // Typo, unnoticed until runtime
</code></pre>
<ul>
<li><strong>问题</strong>：<code>sume</code>中的拼写错误只有在运行代码时才会导致错误，这可能会导致令人沮丧的调试会话，或者更糟糕的是，在生产环境中中断应用程序。</li>
</ul>
<h4 id="eslint">🚑 使用 ESLint 之后的静态分析：</h4>
<pre><code>codeError: 'sume' is not defined.
</code></pre>
<ul>
<li><strong>解决方案</strong>：ESLint 立即标记拼写错误，你甚至在运行代码之前就发现了问题。早期捕获错误，节省了时间和麻烦。</li>
</ul>
<h3 id="2"><strong>2️⃣ 自动代码格式化</strong></h3>
<p>格式化前：</p>
<pre><code class="language-javascript">function calculate ( x , y ){ return x+ y;}
console.log( calculate (2,3 ) )
</code></pre>
<ul>
<li><strong>问题</strong>：不一致的空格和格式化使代码难以阅读。</li>
</ul>
<h4 id="prettier">使用 Prettier 之后：</h4>
<pre><code class="language-javascript">function calculate(x, y) {
  return x + y;
}
console.log(calculate(2, 3));
</code></pre>
<ul>
<li><strong>解决方案</strong>：自动应用干净、一致、专业的格式。不再挑剔空格或对齐问题。</li>
</ul>
<p>当然了，这些都是基本操作。我讲这些是以防万一，比如你得用记事本手撕代码，或者是在面试这种没有 IDE 助力的环境。</p>
<h3 id="3ci"><strong>3️⃣ 持续集成（CI）测试</strong></h3>
<p>CI 测试确保你对代码的每一次更改都得到自动验证。它就像一个安全网，可以捕捉开发过程中引入的错误。CI 工具在每次推送代码时运行测试，因此在部署后没有任何中断。</p>
<h4 id="ci"><strong>CI 测试如何工作？</strong></h4>
<ol>
<li>
<p><strong>变化触发</strong>：每次提交代码时，CI 工具（如 GitHub Actions、Jenkins）会运行自动测试。</p>
</li>
<li>
<p><strong>反馈</strong>：如果出现问题，它会立即提供反馈。</p>
</li>
<li>
<p><strong>防止代码破坏</strong>：只有干净且工作的代码会被合并到主分支。</p>
</li>
</ol>
<h3 id="4cicd">4️⃣ CI/CD 流水线</h3>
<p>我们也使用 CI/CD 流水线作为一个持续的过程，其中包括代码构建、测试和部署，而 CI 测试是这个过程中专注于自动化代码变更测试的部分。</p>
<p><strong>CI/CD 流水线与 CI 测试的区别：</strong></p>
<ul>
<li>
<p><strong>CI/CD 流水线：</strong> CI/CD 流水线将代码构建、测试和部署整合为一个单一过程。这个过程确保对主分支代码的所有更改都可以发布到生产环境。CI/CD 流水线可以减少部署时间、降低成本并改善团队协作。</p>
</li>
<li>
<p><strong>CI 测试：</strong> CI 测试是自动测试集成到中央代码库中的代码变更的过程。CI 测试专注于确保代码库的稳定性以及解决集成问题。CI 测试帮助开发者构建稳定的、无错误的软件并满足功能需求。</p>
</li>
</ul>
<p>🚧 这些就是 CI 测试和 CI/CD 流水线概念的真正含义。并不像想象中那么复杂。所以让我详细讲解一下使用 GitHub Actions 进行的 CI 测试，因为我们现在通常通过自动化工具运行测试。</p>
<h3 id="githubactionsci"><strong>⚡ 使用 GitHub Actions 进行持续集成（CI）测试</strong></h3>
<p>正如我之前所说，CI 工具会在每次您推送代码或打开拉取请求时运行自动化测试。这确保只有正常工作的、无错误的代码才会被合并到主分支。</p>
<h4 id="githubactionsci">如何使用 GitHub Actions 设置 CI 测试</h4>
<p><strong>步骤 1：创建您的代码库</strong></p>
<p>为您的项目设置一个 GitHub 代码库。然后，使用以下命令将代码推送到 GitHub：</p>
<pre><code>git init
git add .
git commit -m "Initial commit for CI Testing"
git branch -M main
git remote add origin https://github.com/codewithshahan/codewithshahan.git
git push -u origin main
</code></pre>
<p>或者您可以在不使用命令的情况下从 GitHub 账户创建一个新的代码库。只需登录到您的 GitHub 账户并访问仪表板。您会发现一个“New”按钮来创建一个全新的代码库：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737618697327/dcef8be8-0d08-45d7-8000-34c4c65df425.png" alt="在 GitHub 上创建新代码库的图片由 Shahan 提供" width="1371" height="553" loading="lazy"></p>
<p><strong>步骤 2：添加一个 GitHub Actions 工作流</strong></p>
<p>导航到您代码库的 <strong>Actions</strong> 选项卡。要执行此操作，首先您必须访问 GitHub 上的代码库（创建代码库后您会找到链接）。在此示例中，我创建了一个名为“codewithshahan”的新代码库。在这里，看一下导航栏右侧的 <strong>Actions</strong> 选项卡。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737618879398/7c5aa37a-72be-4701-a8f8-9ea9e05c0d5d.png" alt="GitHub Actions 导航选项卡的图片由 Shahan 提供" width="1885" height="724" loading="lazy"></p>
<p>进入 Actions 选项卡后，向下滚动一点，您会找到 <strong>continuous integration</strong> 部分：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737619002674/60003e57-f2b2-48f1-bef8-9bde39149faf.png" alt="GitHub Actions 页面上的 CI（持续集成）测试图片由 Shahan 提供" width="1496" height="508" loading="lazy"></p>
<p>选择一个适合您的工作流设置。我将在这个项目中使用 Node.js。</p>
<p>点击配置按钮后，会自动创建一个 <code>node.js.yml</code> 文件，您可以根据目标调整代码。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737619475568/74da6d46-c105-42c8-8662-fc72e9410bda.png" alt="GitHub 自动测试工作流代码段的图片由 Shahan 提供" width="1879" height="854" loading="lazy"></p>
<p>我不会详细说明如何修改 <code>.yml</code> 文件。这取决于您的项目目标和个人偏好。此外，这是一个完全不同的更广泛的话题，鉴于这篇文章已经相当长，我将在以后的文章中再做解释。目前，请坚持这些基础知识。</p>
<p>这个 CI 测试工作流非常适合现代应用程序开发。您的应用程序在包含关键功能（例如：暗模式）、在 GitHub 代码库中直接构建和部署应用程序时保持稳定。通过这种方式，您可以自信地推送代码，确保您的代码始终清晰并准备好投入生产。</p>
<h2 id="heading-the-role-of-documentation-in-agile-software-development">文档在敏捷软件开发中的角色 🚣</h2>
<p>如果您希望您的编码能力达到顶级水准，您需要了解如何编写好的文档。如果您认为文档只是匆忙写下代码如何工作，那么您错了。这是关于解释<strong>为什么</strong>它可以工作，而不仅仅是如何工作。这是大多数人忽视的一点。</p>
<h3 id="1">1. 🚡 创建<strong>有用的文档（解释为什么，而不仅仅是如何）</strong></h3>
<p>当您编写文档时，您不仅仅是写下一些关于如何使用代码的说明。您是在告诉下一个人（或者未来的自己）为什么这个代码段最初存在。这就是好文档和差文档的区别。</p>
<p>糟糕的文档会让人困惑。它们太模糊、太简单，并且没有回答重要的问题。如果您的文档不清晰，这可能意味着您的思考不清晰。您基本上是在说，“我不在乎你是否理解这个，它管用，直接用就行。”这没有帮助。</p>
<p>优秀的文档回答了棘手的问题：</p>
<ul>
<li>
<p>✅ 为什么您选择了这种方法而不是其他方法？</p>
</li>
<li>
<p>✅ 为什么这个函数存在？它解决了什么问题？</p>
</li>
<li>
<p>✅ 为什么您这样编写代码？</p>
</li>
</ul>
<h3 id="2">2. ⏳ <strong>保持文档更新（过时的文档比没有文档更糟）</strong></h3>
<p>过时的文档是最糟糕的。事实上，它可能比没有文档更糟。当你的文档与代码不同步时，你是在给未来的自己（或下一个必须处理它的人）带来巨大的麻烦。</p>
<p>每当代码发生变化，你的文档也必须随之更新。它必须能够反映代码的当前状态。千万不要留下那些过时的信息去误导未来的开发者（甚至是未来的你自己），那只会让他们感到困惑并浪费时间。如果某些内容不再相关，直接删掉它。过时的文档就像凌乱的大脑——它只会拖你的后腿。</p>
<p>要养成定期检查和更新文档的习惯。代码改动的那一刻，文档也必须同步更新。就这么简单。</p>
<h3 id="3">3. 🚆 <strong>整合注释（代码中的良好注释是文档的一部分）</strong></h3>
<p>听好了——代码里的注释应当与文档融为一体。优秀的注释不该是开发者的“拐杖”，仅仅因为他们在别处解释不清楚代码才拿来凑合。注释的作用是升华文档，而不是取而代之。</p>
<p>注释是文档的补充。你应该编写那种清晰易懂、几乎不需要解释的高质量代码；但当某些地方无法做到“一眼看透”时，再加入注释。记住注释的准则：解释 <strong>“为什么（Why）”</strong>，而不是 <strong>“怎么做（How）”</strong>。文档也是同理。别说废话，让代码自己“说话”。注释应当服务于文档的宏观大局，而不是给烂代码打补丁。</p>
<p>🪧 优秀的代码应该是自解释的。先去优化代码逻辑，如果仍有必要，再添加注释进行澄清。保持注释整洁、简短、直击要害。</p>
<p>如果你想写出整洁、高效且易维护的代码，文档就是关键。别再把文档当成事后才补的作业，或者填补空间的废话。它是代码的延伸——是你进行清晰高效沟通的方式。它是留给后人的路线图，更是你思维过程的体现。</p>
<h2 id="heading-conclusion">结论 🏁</h2>
<p>整洁代码并非可有可无的“加分项”，而是那些志在成为领导者的人的“必选项”。它关乎掌控力、效率，以及长期的持续改进。归根结底，它将助你在敏捷软件开发的博弈中脱颖而出。</p>
<p>🪧 如果你想真正精进自己的技艺，请编写整洁的代码，让效率自己证明一切。</p>
<h2 id="heading-frequently-asked-questions-about-clean-code">关于整洁代码的常见问题 🧯</h2>
<ol>
<li>
<p><strong>什么是干净的代码？</strong> 是指不会让你想把电脑扔出窗外的代码。</p>
</li>
<li>
<p><strong>为什么干净的代码在敏捷中很重要？</strong> 因为敏捷注重速度和变化，凌乱的环境中你不可能快速行动。</p>
</li>
<li>
<p><strong>什么是代码异味？</strong> 是你即将失去对代码库控制的迹象。</p>
</li>
<li>
<p><strong>我该如何改善注释？</strong> 只对必要的部分进行注释，并确保每个注释都增加价值而不是噪音。</p>
</li>
</ol>
<p>感谢您的陪伴。您可以访问我的 <a href="https://x.com/shahancd">Twitter 账号</a> 或 <a href="https://www.codewithshahan.com">我的网站</a> 阅读更多关于整洁代码和敏捷应用程序开发的文章。下次再见……继续改进你的代码库。</p>
<p>如果你认真想掌握整洁代码并提升你的编程职业生涯，我的书就是为你而写的：<a href="https://codewithshahan.gumroad.com/l/cleancode-zero-to-one"><strong>从零到一的整洁代码</strong></a>。这本书是你从零到一掌握整洁代码的完整指南，从杂乱到杰作。我正在提供50%的折扣，使用代码“earlybird”——仅限前50本。此外，还有30天的退款保证——无风险，纯奖励。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何写出整洁的代码——技巧与最佳实践 ]]>
                </title>
                <description>
                    <![CDATA[ 大家好！这篇文章讲的主要是如何写出“整洁”的代码。我刚开始成为一个程序员的时候，这个概念常常会有点模糊，后来我发现它有很多细微差别的地方和一些可能的解释。 所以在这篇文章中，我们将会讨论“整洁代码”指的是什么，它为什么这么重要，我们如何评估一个代码库整洁与否。同时你也会学习一些你能遵循的最佳实践和惯例，让你的代码更加整洁。 冲！ 目录  * 写出“整洁代码”意味着什么以及我为什么要注意它？  * 我如何评估一个代码库是否整洁？  * 让代码更整洁的技巧和惯例 * 有效、效率和简单      * 格式和语法     * 命名     * 简洁VS清晰     * 复用性     * 清晰的执行流程     ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-write-clean-code/</link>
                <guid isPermaLink="false">64775064932aa206e5722edd</guid>
                
                    <category>
                        <![CDATA[ 代码质量 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ songyp0505 ]]>
                </dc:creator>
                <pubDate>Wed, 31 May 2023 03:05:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/05/pexels-ken-tomita-389819.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-write-clean-code/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Write Clean Code – Tips and Best Practices (Full Handbook)</a>
      </p><!--kg-card-begin: markdown--><p>大家好！这篇文章讲的主要是如何写出“整洁”的代码。我刚开始成为一个程序员的时候，这个概念常常会有点模糊，后来我发现它有很多细微差别的地方和一些可能的解释。</p>
<p>所以在这篇文章中，我们将会讨论“整洁代码”指的是什么，它为什么这么重要，我们如何评估一个代码库整洁与否。同时你也会学习一些你能遵循的最佳实践和惯例，让你的代码更加整洁。</p>
<p>冲！</p>
<h2 id="">目录</h2>
<ul>
<li><a href="#what-does-it-mean-to-write-clean-code-and-why-should-i-care">写出“整洁代码”意味着什么以及我为什么要注意它？</a></li>
<li><a href="#how-can-i-assess-whether-a-codebase-is-clean-or-not">我如何评估一个代码库是否整洁？</a></li>
<li><a href="#tips-and-conventions-to-write-cleaner-code">让代码更整洁的技巧和惯例</a>
<ul>
<li><a href="#effectiveness-efficiency-and-simplicity">有效、效率和简单 </a></li>
<li><a href="#format-and-syntax">格式和语法</a></li>
<li><a href="#naming">命名</a></li>
<li><a href="#conciseness-versus-clarity">简洁VS清晰</a></li>
<li><a href="#re-usability">复用性</a></li>
<li><a href="#clear-flow-of-execution">清晰的执行流程</a></li>
<li><a href="#single-responsibility-principle">单一职责原则</a></li>
<li><a href="#having-a-single-source-of-truth">拥有“单一事实来源”</a></li>
<li><a href="#only-expose-and-consume-data-you-need">只暴露和使用你需要的数据</a></li>
<li><a href="#modularization">模块化</a></li>
<li><a href="#folder-structures">文件夹结构</a></li>
<li><a href="#documentation">文档化</a></li>
</ul>
</li>
<li><a href="#wrapping-up">总结</a></li>
</ul>
<h2 id="what-does-it-mean-to-write-clean-code-and-why-should-i-care">写出“整洁代码”意味着什么以及我为什么要注意它？</h2>
<p>整洁代码是一个术语，用来形容计算机代码容易阅读、理解和维护。整洁的代码需要用一种，能让代码简单、简洁且形象生动的方式进行书写。它遵循一套惯例、标准来使得代码容易阅读和理解。</p>
<p>整洁的代码脱离了复杂性、冗余以及一些“代码味”和反面模式，因为这些东西会让代码变得难以维护、调试以及修改。</p>
<p>我并不能夸大代码整洁的重要性。当代码很容易阅读和理解的时候，开发者们就能很容易地在代码库中工作。这就可以提高工作效率以及降低产生的错误。</p>
<p>并且，当代码是易于维护的，随着时间的推移，代码库也能不断提高和更新。这对于长期项目来说十分重要，尤其是代码在未来几年需要不断更新和维护的时候。</p>
<h2 id="how-can-i-assess-whether-a-codebase-is-clean-or-not">我如何评估一个代码库是否整洁？</h2>
<p>这里可以用很多方法。好的文档、前后一致的格式以及组织良好的代码库都是需要考虑的因素。</p>
<p>代码审查也可以帮助识别潜在的问题，确保代码遵循最佳实践和惯例。</p>
<p>测试也是一个很重要的方面，它可以帮助确保代码运行的情况跟预期的保持一致，而且能尽早地发现运行中的问题和错误。</p>
<p>你有很多工具、实践以及惯例可以直接引入，从而确保一个代码库是整洁的。</p>
<p>通过引入这些工具和实践，开发者们可以创建一个容易阅读、理解和维护的代码库。</p>
<p>还有一件事情也很重要，就是一定要记住在代码整洁这件事情上，不可避免地会粘连很多主观性，并且对于这件事情也有许许多多不同的观点和见解。一个代码库对一个人来说可能看起来很整洁、厉害，但是对另一个人或者另一个项目来说可能就不是这样了。</p>
<p>但是在这个问题上依然还是有很多惯例是我们可以遵守的，下面就说一下这些。</p>
<h2 id="tips-and-conventions-to-write-cleaner-code">让代码更整洁的技巧和惯例</h2>
<h3 id="effectiveness-efficiency-and-simplicity">有效、效率和简单</h3>
<p>当我需要思考如何向已有的代码库引入新的功能、或者如何找到某个特殊问题的答案的时候，我总会优先考虑这三个东西。</p>
<h4 id="">有效</h4>
<p>首先，我们的代码必须有效，这就意味着它必须能够解决它应该解决的问题。当然这是我们对我们自己的代码最基本的期望，但是如果我们实施的东西没有起到作用的话，此时考虑其他事情是毫无意义的。</p>
<h4 id="">效率</h4>
<p>第二，一旦我们的代码解决了问题，我们就应该检查它是否有效率。就时间和空间而言，程序运行时是否使用了合理的资源？它能不能运行得更快一些或者占用空间更少一些。</p>
<p>为了评估这一点，算法复杂度是一个你应该注意的事情。如果你对这个词不熟悉，你可以去我写的<a href="https://chinese.freecodecamp.org/news/introduction-to-algorithms-with-javascript-examples/#algorithmic-complexity">这一篇文章</a>看一下。</p>
<p>在效率这部分展开的话，下面有两个例子，都是计算一个数组中所有数字的总和。</p>
<pre><code class="language-javascript">// 不高效的示例
function sumArrayInefficient(array) {
  let sum = 0;
  for (let i = 0; i &lt; array.length; i++) {
    sum += array[i];
  }
  return sum;
}
</code></pre>
<p><code>sumArrayInefficient</code>函数使用<code>for</code>循环迭代数组，并将每个元素添加到<code>sum</code>变量中。虽然这是一个可行解，但是这样就没太有效率了，因为不管数组有多长，它都需要迭代整个数组。</p>
<pre><code class="language-javascript">// 高效的示例
function sumArrayEfficient(array) {
  return array.reduce((a, b) =&gt; a + b, 0);
}
</code></pre>
<p>在这里，<code>sumArrayEfficient</code> 函数使用<code>reduce</code> 方法去计算数组里的数字的总和。 <code>reduce</code> 方法将函数应用于数组的每个元素，并累加结果。在这种情况下，函数只需将每个元素添加到从0开始的累加器中。</p>
<p>这是一个更有效的解决方案，因为它只需要对数组进行一次迭代，并在进行时对每个元素执行求和操作。</p>
<h4 id="">简单</h4>
<p>最后是<strong>简单</strong>。这是最难评估的一个方面，因为它很主观，它取决于读代码的人。但是也有一些我们可以遵循的指南：</p>
<ol>
<li>你能简单地理解程序的每行代码在干什么吗？</li>
<li>函数和变量的定义是否都清晰地代表它们需要代表的内容？</li>
<li>在整个代码库中，代码的缩进和空格是否遵循正确的代码格式？</li>
<li>代码有可用的文档吗？评论是否用于解释程序的复杂部分？</li>
<li>你能以多快的速度识别出代码库的哪个部分是程序的某些功能？你能在不修改代码其他部分的前提下删除/增加新功能吗？</li>
<li>代码是否遵循模块化方法，在组件中分离不同的功能？</li>
<li>代码在可能的情况下会重复使用吗？</li>
<li>在整个代码库中，是否同样遵循相同的架构、设计和实现决策？</li>
</ol>
<p>通过遵循和优先考虑有效、效率和简单这三个概念，在考虑如何实施解决方案时，我们总是可以遵循一个指导方针。现在，让我们扩展一些可以帮助我们简化代码的指南。</p>
<h3 id="format-and-syntax">格式和语法</h3>
<p>在整个代码库中使用一致的格式和语法也是写出整洁代码的一个重要因素。这是因为一致的结构和语法能让代码可读性更强，并且能够更加容易理解。</p>
<p>当代码一致时，开发者们可以轻松地识别模式、理解代码的工作流程，这也就使得未来调试、维护和更新代码库变得更加容易。一致性也能帮助降低错误，因为它确保所有的开发者都遵循相同的标准和惯例。</p>
<p>我们应该考虑的一些格式和语法如下：</p>
<ul>
<li><strong>缩进和间距</strong></li>
</ul>
<pre><code class="language-javascript">// 缩进和间距不对
const myFunc=(number1,number2)=&gt;{
const result=number1+number2;
return result;
}

// 好的缩进和间距
const myFunc = (number1, number2) =&gt; {
    const result = number1 + number2
    return result
}
</code></pre>
<p>这里是同一个函数的例子，一个是没有缩进和空格，另一个是有合适的空格和缩进，我们可以发现第二个更容易阅读。</p>
<ul>
<li><strong>一致的语法</strong></li>
</ul>
<pre><code class="language-javascript">// 箭头函数，没有括号和返回
const multiplyByTwo = number =&gt; number * 2

// 函数，括号，返回
function multiplyByThree(number) {
    return number * 3;
}
</code></pre>
<p>同样，这里是用两种语法写出来的非常相似的函数。第一个用的是一个箭头定义的函数，没有括号和返回，而另一个是一个使用了括号和返回的相同函数。</p>
<p>两个都可以顺利的实现功能，但是我们应该力求在类似的操作中始终使用相同的语法，这样的话可以使得代码库更加可读，且代码量也更加均匀。</p>
<p>Linterns 和代码格式化程序是我们可以在项目中使用的很棒的工具，可以在我们的代码库中自动化语法和格式约定。如果你不熟悉这些工具，<a href="https://www.freecodecamp.org/news/using-prettier-and-jslint/">查看我的另一篇文章</a>。</p>
<ul>
<li><strong>一致的案例惯例</strong></li>
</ul>
<pre><code class="language-javascript">// camelCase
const myName = 'John'
// PascalCase
const MyName = 'John'
// snake_case
const my_name = 'John'
</code></pre>
<p>我们选择的案例遵循的惯例也是如此。上述三种情况都可以，但我们应该在整个项目中始终使用相同的用法。</p>
<h3 id="naming">命名</h3>
<p>清晰而有描述性地命名变量和函数，对于书写clean code也是十分钟重要的一个方面。它可以帮助提高代码的可读性和维护能力。当命名选择良好时，其他开发人员可以快速理解变量或函数在做什么，以及它与代码的其余部分有何关系。</p>
<p>下面是JavaScript中的两个例子，用来展示清晰且有描述性地命名的重要性：</p>
<pre><code class="language-javascript">// 示例 1：命名不规范
function ab(a, b) {
  let x = 10;
  let y = a + b + x;
  console.log(y);
}

ab(5, 3);
</code></pre>
<p>在本例中，我们有一个函数，它接受两个参数，将它们与常量值10相加，并将结果记录到控制台。函数名称和变量名选择不当，没有给出任何指示函数的作用或变量代表什么。</p>
<pre><code class="language-javascript">// 示例 1：命名规范
function calculateTotalWithTax(basePrice, taxRate) {
  const BASE_TAX = 10;
  const totalWithTax = basePrice + (basePrice * (taxRate / 100)) + BASE_TAX;
  console.log(totalWithTax);
}

calculateTotalWithTax(50, 20);
</code></pre>
<p>在这个例子中，我们有一个函数来计算产品的总价格，包括收取的税值。函数名称和变量名选择得很好，可以清楚地表明函数的作用以及变量代表什么。</p>
<p>这使得代码更容易阅读和理解，特别是对于未来可能使用代码库的其他开发人员来说。</p>
<h3 id="conciseness-versus-clarity">简洁 VS 清晰</h3>
<p>当涉及到编写整洁的代码时，在简洁和清晰之间取得平衡很重要。虽然保持代码简洁以提高其可读性和可维护性很重要，但同样重要的是确保代码清晰且易于理解。编写过于简洁的代码可能会导致混乱和错误，并可能使其他开发人员难以使用代码。</p>
<p>以下是两个例子，证明了简洁和清晰的重要性：</p>
<pre><code class="language-javascript">// 示例 1：清晰的函数
const countVowels = s =&gt; (s.match(/[aeiou]/gi) || []).length;
console.log(countVowels("hello world"));
</code></pre>
<p>本示例使用简洁的箭头函数和正则表达式来计算给定字符串中的元音数量。虽然代码非常简短且易于编写，但其他开发人员可能无法立即清楚正则表达式模式的工作原理，特别是如果他们不熟悉正则表达式语法。</p>
<pre><code class="language-javascript">// 例子 2：更省略、更清晰的函数
function countVowels(s) {
  const vowelRegex = /[aeiou]/gi;
  const matches = s.match(vowelRegex) || [];
  return matches.length;
}

console.log(countVowels("hello world"));
</code></pre>
<p>此示例使用传统函数和正则表达式来计算给定字符串中的元音数量，但以清晰易懂的方式进行计算。函数名称和变量名是描述性的，正则表达式模式存储在具有清晰名称的变量中。这使得很容易看到该功能在做什么以及它是如何工作的。</p>
<p>在编写代码时，在简洁和清晰之间取得平衡很重要。虽然简洁的代码可以提高可读性和可维护性，但对于将来可能使用代码库的其他开发人员来说，确保代码仍然清晰且易于理解很重要。</p>
<p>通过使用描述性函数和变量名，以及使用清晰可读的代码格式和注释，可以编写易于理解和处理的整洁的代码。</p>
<h3 id="re-usability">复用性</h3>
<p>代码可重用性是软件工程的一个基本概念，指的是代码无需修改即可多次使用的能力。</p>
<p>代码可重用性的重要性在于，它可以通过减少需要编写和测试的代码量来大大提高软件开发的效率和生产力。</p>
<p>通过重复使用现有代码，开发人员可以节省时间和精力，提高代码质量和一致性，并最大限度地降低引入错误和错误的风险。可重用代码还允许更多模块化和可扩展的软件架构，使其更容易随着时间的推移维护和更新代码库。</p>
<pre><code class="language-javascript">// 示例 1：没有复用
function calculateCircleArea(radius) {
  const PI = 3.14;
  return PI * radius * radius;
}

function calculateRectangleArea(length, width) {
  return length * width;
}

function calculateTriangleArea(base, height) {
  return (base * height) / 2;
}

const circleArea = calculateCircleArea(5);
const rectangleArea = calculateRectangleArea(4, 6);
const triangleArea = calculateTriangleArea(3, 7);

console.log(circleArea, rectangleArea, triangleArea);
</code></pre>
<p>这个例子定义了三个函数，分别计算圆、矩形和三角形的面积。每个函数执行一个特定的任务，但它们都不能重复用于其他类似任务。</p>
<p>此外，如果将来需要更改值，使用直接赋值的PI值可能会导致错误。该代码效率低下，因为它多次重复相同的逻辑。</p>
<pre><code class="language-javascript">// 示例 2：有复用
function calculateArea(shape, ...args) {
  if (shape === 'circle') {
    const [radius] = args;
    const PI = 3.14;
    return PI * radius * radius;
  } else if (shape === 'rectangle') {
    const [length, width] = args;
    return length * width;
  } else if (shape === 'triangle') {
    const [base, height] = args;
    return (base * height) / 2;
  } else {
    throw new Error(`Shape "${shape}" not supported.`);
  }
}

const circleArea = calculateArea('circle', 5);
const rectangleArea = calculateArea('rectangle', 4, 6);
const triangleArea = calculateArea('triangle', 3, 7);

console.log(circleArea, rectangleArea, triangleArea);
</code></pre>
<p>此示例定义了单个函数 calculateArea，该函数采用形状参数和可变数量的参数。基于形状参数，函数执行适当的计算并返回结果。</p>
<p>这种方法效率要高得多，因为它消除了为类似任务重复代码的需要。它也更加灵活和可扩展，因为将来可以很容易地添加额外的形状。</p>
<h3 id="clear-flow-of-execution">清晰的执行流程</h3>
<p>清晰的执行流程对于编写整洁的代码至关重要，因为它使代码更易于阅读、理解和维护。遵循清晰和逻辑结构的代码不容易出错，更容易修改和扩展，并且在时间和资源方面更有效率。</p>
<p>另一方面，意大利面条代码是一个术语，用于描述复杂且难以理解的代码，通常以长、混乱和无组织的代码块为特征。意大利面条代码可能是糟糕的设计决策、过度耦合或缺乏适当的文档和注释的结果。</p>
<p>下面是执行相同任务的两个 JavaScript 代码示例，一个执行流程清晰，另一个代码很杂乱：</p>
<pre><code class="language-javascript">// 示例 1：清晰的执行流程
function calculateDiscount(price, discountPercentage) {
  const discountAmount = price * (discountPercentage / 100);
  const discountedPrice = price - discountAmount;
  return discountedPrice;
}

const originalPrice = 100;
const discountPercentage = 20;
const finalPrice = calculateDiscount(originalPrice, discountPercentage);

console.log(finalPrice);

// 示例 2：意大利面条代码
const originalPrice = 100;
const discountPercentage = 20;

let discountedPrice;
let discountAmount;
if (originalPrice &amp;&amp; discountPercentage) {
  discountAmount = originalPrice * (discountPercentage / 100);
  discountedPrice = originalPrice - discountAmount;
}

if (discountedPrice) {
  console.log(discountedPrice);
}
</code></pre>
<p>正如我们所见，示例 1 遵循清晰且合乎逻辑的结构，其中包含一个函数，该函数接受必要的参数并返回计算结果。另一方面，示例 2 更加复杂，在任何函数之外声明了变量，并且使用多个 if 语句来检查代码块是否已成功执行。</p>
<h3 id="single-responsibility-principle">单一职责原则</h3>
<p>单一职责原则（SRP）是软件开发中的一项原则，它指出每个类或模块应该只有一个更改原因，或者换句话说，我们代码库中的每个实体都应该承担单一职责。</p>
<p>此原则有助于创建易于理解、维护和扩展的代码。</p>
<p>通过应用 SRP，我们可以创建更易于测试、重用和重构的代码，因为每个模块只处理单一职责。这使得它不太可能有副作用或依赖性，这些副作用或依赖性会使代码更难使用。</p>
<pre><code class="language-javascript">// 示例 1：没有 SRP
function processOrder(order) {
  // validate order
  if (order.items.length === 0) {
    console.log("Error: Order has no items");
    return;
  }
  
  // calculate total
  let total = 0;
  order.items.forEach(item =&gt; {
    total += item.price * item.quantity;
  });
  
  // apply discounts
  if (order.customer === "vip") {
    total *= 0.9;
  }
  
  // save order
  const db = new Database();
  db.connect();
  db.saveOrder(order, total);
}
</code></pre>
<p>在此示例中，<code>processOrder</code>函数处理多项职责：它验证订单、计算总额、应用折扣以及将订单保存到数据库中。这使得该功能冗长且难以理解，并且对一项职责的任何更改都可能影响其他职责，从而使其更难维护。</p>
<pre><code class="language-javascript">// 示例 2：有 SRP
class OrderProcessor {
  constructor(order) {
    this.order = order;
  }
  
  validate() {
    if (this.order.items.length === 0) {
      console.log("Error: Order has no items");
      return false;
    }
    return true;
  }
  
  calculateTotal() {
    let total = 0;
    this.order.items.forEach(item =&gt; {
      total += item.price * item.quantity;
    });
    return total;
  }
  
  applyDiscounts(total) {
    if (this.order.customer === "vip") {
      total *= 0.9;
    }
    return total;
  }
}

class OrderSaver {
  constructor(order, total) {
    this.order = order;
    this.total = total;
  }
  
  save() {
    const db = new Database();
    db.connect();
    db.saveOrder(this.order, this.total);
  }
}

const order = new Order();
const processor = new OrderProcessor(order);

if (processor.validate()) {
  const total = processor.calculateTotal();
  const totalWithDiscounts = processor.applyDiscounts(total);
  const saver = new OrderSaver(order, totalWithDiscounts);
  saver.save();
}
</code></pre>
<p>在此示例中，<code>processOrder</code> 函数已拆分为遵循 SRP 的两个类：<code>OrderProcessor</code>和<code>OrderSaver</code>。</p>
<p><code>OrderProcessor</code> 类负责验证订单、计算总额和应用折扣，而 <code>OrderSaver</code> 类负责将订单保存到数据库。</p>
<p>这使得代码更易于理解、测试和维护，因为每个类都有明确的职责，并且可以在不影响其他类的情况下进行修改或替换。</p>
<h3 id="having-a-single-source-of-truth">拥有“单一事实来源”</h3>
<p>拥有“单一事实来源”意味着代码库中只有一个地方存储特定的数据或配置，代码中对它的任何其他引用都指向那个来源。 这很重要，因为它确保数据一致并避免重复和不一致。</p>
<p>这里有一个例子来说明这个概念。 假设我们有一个应用程序需要显示一个城市的当前天气状况。我们可以通过两种不同的方式实现此功能：</p>
<pre><code class="language-javascript">// 操作 1：没有“单一事实来源”

// file 1: weatherAPI.js
const apiKey = '12345abcde';

function getCurrentWeather(city) {
  return fetch(`https://api.weather.com/conditions/v1/${city}?apiKey=${apiKey}`)
    .then(response =&gt; response.json());
}

// file 2: weatherComponent.js
const apiKey = '12345abcde';

function displayCurrentWeather(city) {
  getCurrentWeather(city)
    .then(weatherData =&gt; {
      // display weatherData on the UI
    });
}
</code></pre>
<p>在此选项中，API 密钥在两个不同的文件中重复，使其更难维护和更新。如果我们需要更改 API 密钥，我们必须记住在两个地方都更新它。</p>
<pre><code class="language-javascript">// 操作 2：有“单一事实来源”

// file 1: weatherAPI.js
const apiKey = '12345abcde';

function getCurrentWeather(city) {
  return fetch(`https://api.weather.com/conditions/v1/${city}?apiKey=${apiKey}`)
    .then(response =&gt; response.json());
}

export { getCurrentWeather, apiKey };


// file 2: weatherComponent.js
import { getCurrentWeather } from './weatherAPI';

function displayCurrentWeather(city) {
  getCurrentWeather(city)
    .then(weatherData =&gt; {
      // display weatherData on the UI
    });
}
</code></pre>
<p>在此选项中，API 密钥存储在一个位置（在 <code>weatherAPI.js</code> 文件中）并导出以供其他模块使用。这可确保 API 密钥只有一个真实来源，并避免重复和不一致。</p>
<p>如果我们需要更新 API 密钥，我们可以在一个地方进行，所有其他使用它的模块将自动获得更新后的值。</p>
<h3 id="only-expose-and-consume-data-you-need">只暴露和使用你需要的数据</h3>
<p>编写整洁代码的一个重要原则是只公开和使用特定任务所需的信息。这有助于降低复杂性、提高效率并避免因使用不必要的数据而导致的错误。</p>
<p>当不需要的数据被暴露或消耗时，可能会导致性能问题并使代码更难以理解和维护。</p>
<p>假设您有一个具有多个属性的对象，但您只需要使用其中的几个。一种方法是在每次需要时引用对象和特定属性。 但这可能会变得冗长且容易出错，尤其是当对象嵌套很深时。一种更整洁、更高效的解决方案是使用对象解构来仅公开和使用您需要的信息。</p>
<pre><code class="language-javascript">// 原始对象
const user = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  age: 25,
  address: {
    street: '123 Main St',
    city: 'Anytown',
    state: 'CA',
    zip: '12345'
  }
};

// 只暴露和使用 name 和 email 属性
const { name, email } = user;

console.log(name); // 'Alice'
console.log(email); // 'alice@example.com'
</code></pre>
<h3 id="modularization">模块化</h3>
<p>模块化是编写整洁代码的基本概念。它指的是将大型复杂代码分解为更小、更易于管理的模块或功能的做法。 这使代码更易于理解、测试和维护。</p>
<p>使用模块化提供了几个好处，例如：</p>
<ol>
<li>复用性：模块可以在应用程序的不同部分或其他应用程序中重复使用，节省开发时间和精力。</li>
<li>封装：模块允许您隐藏函数或对象的内部细节，只将基本接口暴露给外界。这有助于减少代码不同部分之间的耦合并提高整体代码质量。</li>
<li>可扩展性：通过将大代码分解成更小的模块化片段，您可以轻松添加或删除功能，而不会影响整个代码库。</li>
</ol>
<p>下面是一段 JavaScript 代码示例，它执行一个简单的任务，一个不使用模块化，另一个实现模块化。</p>
<pre><code class="language-javascript">// 没有模块化
function calculatePrice(quantity, price, tax) {
  let subtotal = quantity * price;
  let total = subtotal + (subtotal * tax);
  return total;
}

// 没有模块化
let quantity = parseInt(prompt("Enter quantity: "));
let price = parseFloat(prompt("Enter price: "));
let tax = parseFloat(prompt("Enter tax rate: "));

let total = calculatePrice(quantity, price, tax);
console.log("Total: $" + total.toFixed(2));
</code></pre>
<p>在上面的示例中，<code>calculatePrice</code> 函数用于根据商品的数量、价格和税率计算商品的总价。 但是，此功能并未模块化，并且与用户输入和输出逻辑紧密耦合。这会使测试和维护变得困难。</p>
<p>现在，让我们看一下使用模块化的相同代码的示例：</p>
<pre><code class="language-javascript">// 模块化
function calculateSubtotal(quantity, price) {
  return quantity * price;
}

function calculateTotal(subtotal, tax) {
  return subtotal + (subtotal * tax);
}

// 模块化
let quantity = parseInt(prompt("Enter quantity: "));
let price = parseFloat(prompt("Enter price: "));
let tax = parseFloat(prompt("Enter tax rate: "));

let subtotal = calculateSubtotal(quantity, price);
let total = calculateTotal(subtotal, tax);
console.log("Total: $" + total.toFixed(2));
</code></pre>
<p>在上面的示例中，<code>calculatePrice</code> 函数被分解为两个较小的函数：<code>calculateSubtotal</code> 和 <code>calculateTotal</code>。 这些函数现在已经模块化，分别负责计算小计和总计。 这使代码更易于理解、测试和维护，也使其在应用程序的其他部分更易于重用。</p>
<p>模块化也可以指将单个代码文件分成许多较小的文件，这些文件之后会编译回单个（或更少的文件）的做法。 这种做法与我们刚才谈到的好处相同。</p>
<p>如果您想知道如何使用模块在 JavaScript 中实现这一点，<a href="https://chinese.freecodecamp.org/news/modules-in-javascript/">查看我的另一篇文章</a>。</p>
<h3 id="folder-structures">文件夹结构</h3>
<p>选择一个好的文件夹结构是编写整洁代码的重要部分。组织良好的项目结构有助于开发人员轻松查找和修改代码，降低代码复杂度，并提高项目的可扩展性和可维护性。</p>
<p>另一方面，糟糕的文件夹结构会使理解项目架构、浏览代码库变得困难，并导致混乱和错误。</p>
<p>以下是使用 React 项目作为示例的好的和坏的文件夹结构示例：</p>
<pre><code class="language-javascript">// 文件夹结构不规范
my-app/
├── App.js
├── index.js
├── components/
│   ├── Button.js
│   ├── Card.js
│   └── Navbar.js
├── containers/
│   ├── Home.js
│   ├── Login.js
│   └── Profile.js
├── pages/
│   ├── Home.js
│   ├── Login.js
│   └── Profile.js
└── utilities/
    ├── api.js
    └── helpers.js
</code></pre>
<p>在此示例中，项目结构是围绕文件类型组织的，例如组件、容器和页面。</p>
<p>但是这种方法会导致混淆和重复，因为不清楚哪些文件属于哪里。例如，<code>Home</code>组件存在于<code>containers</code>和<code>pages</code>文件夹中。它还可能使查找和修改代码变得困难，因为开发人员可能需要导航多个文件夹才能找到他们需要的代码。</p>
<pre><code class="language-javascript">// 规范的文件夹结构
my-app/
├── src/
│   ├── components/
│   │   ├── Button/
│   │   │   ├── Button.js
│   │   │   ├── Button.module.css
│   │   │   └── index.js
│   │   ├── Card/
│   │   │   ├── Card.js
│   │   │   ├── Card.module.css
│   │   │   └── index.js
│   │   └── Navbar/
│   │       ├── Navbar.js
│   │       ├── Navbar.module.css
│   │       └── index.js
│   ├── pages/
│   │   ├── Home/
│   │   │   ├── Home.js
│   │   │   ├── Home.module.css
│   │   │   └── index.js
│   │   ├── Login/
│   │   │   ├── Login.js
│   │   │   ├── Login.module.css
│   │   │   └── index.js
│   │   └── Profile/
│   │       ├── Profile.js
│   │       ├── Profile.module.css
│   │       └── index.js
│   ├── utils/
│   │   ├── api.js
│   │   └── helpers.js
│   ├── App.js
│   └── index.js
└── public/
    ├── index.html
    └── favicon.ico
</code></pre>
<p>在此示例中，项目结构是围绕组件、页面和实用程序等功能进行组织的。每个功能都有自己的文件夹，其中包含与该功能相关的所有文件。</p>
<p>这种方法使得查找和修改代码变得容易，因为与功能相关的所有文件都位于同一个文件夹中。它还减少了代码重复和复杂性，因为功能是分开的，并且它们的相关文件被组织在一起。</p>
<p>总的来说，一个好的文件夹结构应该围绕功能而不是文件类型来组织，并且应该便于查找和修改代码。 清晰且合乎逻辑的结构可以使项目更易于维护、理解和扩展，而混乱和不一致的结构会导致错误和混乱。</p>
<p>如果您有兴趣了解更多相关信息，[在本文中我写了关于软件架构的文章](/news/an-introduction-to-software-architecture-patterns/#different-folder -structures-to-know)我扩展了文件夹结构和您可以遵循的众所周知的模式的主题。</p>
<h3 id="documentation">文档化</h3>
<p>文档是编写整洁代码的重要组成部分。适当的文档不仅可以帮助编写代码的开发人员将来更好地理解代码，还可以让其他开发人员更容易阅读和理解代码库。当代码有良好的文档记录时，可以节省调试和维护代码的时间和精力。</p>
<p>在无法实施简单易懂的解决方案、业务逻辑相当复杂的情况以及必须与不熟悉代码库的人交互的情况下，文档化尤为重要。</p>
<p>记录代码的一种方法是使用注释。 注释可以提供上下文并解释代码的作用。 但重要的是要明智地使用注释，只在必要时评论并避免多余或不必要的注释。</p>
<p>另一种记录代码的方法是使用内联文档。 内联文档嵌入在代码本身中，可用于解释特定功能或代码片段的作用。内联文档通常与 <a href="https://jsdoc.app/">JSDoc</a> 等工具结合使用，它提供了在 JavaScript 中记录代码的标准。</p>
<p>像 Typescript 这样的工具还可以为我们的代码库提供自动文档，这非常有帮助。</p>
<p>如果您想了解更多关于 Typescript 的信息，我之前写了一篇<a href="https://www.freecodecamp.org/news/an-introduction-to-typescript/#otherfunctionalitiesoftypescript">初学者友好指南</a>。</p>
<p>最后，Swagger 和 Postman 等工具可用于记录 API，提供一种轻松理解如何与它们交互的方法</p>
<p>如果您有兴趣了解如何完全实施、测试、使用和记录 API，可以查看我最近为 <a href="https://www.freecodecamp.org/news/build-consume-and-document-a-rest-api/">REST API</a> 和 <a href="https://www.freecodecamp.org/news/building-consuming-and-documenting-a-graphql-api/">GraphQL API</a> 写的两份指南。</p>
<h2 id="wrapping-up">总结</h2>
<p>最后，还是像之前那样，我希望你喜欢这篇文章并学到新东西。</p>
<p>如果需要，你也可以在 <a href="https://www.linkedin.com/in/germancocca/">LinkedIn</a> 或 <a href="https://twitter.com/CoccaGerman">Twitter</a> 上关注我。下篇文章见！</p>
<figure class="kg-card kg0card0image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/05/giphy.gif " class="kg-image" width="600" height="400" alt="giphy" loading="lazy"></figure><!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
