<?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[ Chengjun.L - 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[ Chengjun.L - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 08:49:25 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/chengjun/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Git 拉取远程分支 ]]>
                </title>
                <description>
                    <![CDATA[ Git 是一种流行的版本控制系统，数百万开发人员使用它来管理自己的代码库。Git [https://kinsta.com/knowledgebase/install-git/]最强大的功能之一是通过远程仓库协同工作的能力。 在一个项目中与多人合作时，你必须能够从远程仓库中获取变更并将其与本地仓库合并。本文将教你如何在 Git 中获取远程分支。 什么是远程分支？ 在了解如何获取远程分支之前，让我们先定义一下远程分支。 远程分支是指存在于 GitHub [https://kinsta.com/knowledgebase/git-vs-github/]、GitLab 或 Bitbucket 等远程仓库中的分支。 克隆一个仓库时，Git 会自动创建一个指向原始仓库的 “remote”分支。然后，你就可以用这个远程分支来获取项目中其他协作者所做的修改。 如何在 Git 中获取远程分支 当你克隆一个仓库时，你可以访问它的所有远程分支。你可以使用git branch命令和-r选项来验证这一点： git branch -r 你可以使用git checkout命令签出到这些分支中的任何一 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/git-pull-remote-branch-how-to-fetch-remote-branches-in-git/</link>
                <guid isPermaLink="false">661367403fc36503d53aca02</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 28 Aug 2024 10:29:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/04/roman-synkevych-wX2L8L-fGeA-unsplash--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/git-pull-remote-branch-how-to-fetch-remote-branches-in-git/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Git Pull Remote Branch – How To Fetch Remote Branches in Git</a>
      </p><p>Git 是一种流行的版本控制系统，数百万开发人员使用它来管理自己的代码库。<a href="https://kinsta.com/knowledgebase/install-git/">Git</a>最强大的功能之一是通过远程仓库协同工作的能力。</p><p>在一个项目中与多人合作时，你必须能够从远程仓库中获取变更并将其与本地仓库合并。本文将教你如何在 Git 中获取远程分支。</p><h2 id="-"><strong>什么是远程分支？</strong></h2><p>在了解如何获取远程分支之前，让我们先定义一下远程分支。</p><p>远程分支是指存在于 <a href="https://kinsta.com/knowledgebase/git-vs-github/">GitHub</a>、GitLab 或 Bitbucket 等远程仓库中的分支。</p><p>克隆一个仓库时，Git 会自动创建一个指向原始仓库的 “remote”分支。然后，你就可以用这个远程分支来获取项目中其他协作者所做的修改。</p><h2 id="-git-"><strong>如何在 Git 中获取远程分支</strong></h2><p>当你克隆一个仓库时，你可以访问它的所有远程分支。你可以使用<code>git branch</code>命令和<code>-r</code>选项来验证这一点：</p><pre><code class="language-bash">git branch -r
</code></pre><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropboxusercontent.com/s_4A23CAD3B56D51AD7DA85730E428F7A2E6F6289B6BB197975176BE233B3F0EA9_1682869187912_image.png" class="kg-image" alt="s_4A23CAD3B56D51AD7DA85730E428F7A2E6F6289B6BB197975176BE233B3F0EA9_1682869187912_image" width="2074" height="188" loading="lazy"></figure><p>你可以使用<code>git checkout</code>命令签出到这些分支中的任何一个。</p><p>当你与一群人一起工作时，其中一个贡献者会远程创建一个新的分支。你可能需要将这个远程分支拉取到你的项目中。你可以使用<code>git fetch</code>命令来完成这项工作。</p><p><code>git fetch</code>命令会转向你的远程项目，并从该远程项目中拉取你还没有的所有数据。接着，你就可以获得该远程分支的所有引用，随时可以合并或检查这些引用。</p><pre><code class="language-bash">git fetch
</code></pre><p>你可以写上远程仓库库名称，默认为<code>origin</code>：</p><pre><code class="language-bash">git fetch origin
</code></pre><p>需要注意的是，当你使用<code>git fetch</code>命令时，它只会将远程仓库中的更改下载到本地仓库，而不会自动与你的工作合并，也不会修改你当前的工作。你需要在准备就绪后手动合并更改。</p><p>要访问获取的内容，需要使用<code>git checkout</code>命令。这样就能确保在将提交整合到本地仓库之前，对提交进行审核。</p><p>如果想获取远程分支并将其与自己的工作合并或修改当前工作，可以使用<code>git pull</code>命令。为此，请使用以下命令：</p><pre><code class="language-bash">git pull --all
</code></pre><p>然后就可以运行<code>git branch -r</code>来验证远程仓库是否已被添加。</p><h2 id="--1"><strong>总结</strong></h2><p>在 Git 中获取远程分支是开发环境中协作的一个重要方面。</p><p>按照本文概述的步骤，你就能获取其他协作者在远程分支上所做的改动，并将它们与本地仓库合并。这样，你就能在 Git 仓库的不同分支上工作，并与其他开发人员有效协作。</p><p>开启学习之旅，<a href="https://joelolawanle.com/contents">浏览 200 多篇关于 web 开发的专业文章</a>。查看<a href="https://joelolawanle.com/posts">我的博客</a>，获取更多精彩内容。</p><p>祝你编码愉快！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 适合初学者的简单正则表达式技巧 ]]>
                </title>
                <description>
                    <![CDATA[ 一直想学习正则表达式但被它们的复杂性劝退了？在本文中，我将向你展示五个易于学习的正则技巧，你可以立即在你最喜欢的文本编辑器中开始使用它们。 文本编辑器设置 虽然现在几乎所有文本编辑器都支持正则表达式，但我将在本教程中使用 Visual Studio Code。你可以使用任何你喜欢的编辑器。另外，请注意，你通常需要在搜索输入附近的某处打开 RegEx。这是在 VS Code 中执行此操作的方法： You need to enable RegEx by checking this option1) . — 匹配任何字符 让我们从简单的开始。点符号 . 匹配任何字符： b.t 上面的 RegEx 匹配 "bot”、"bat” 和任何其他以 b 开头并以 t 结尾的三个字符的单词。但是如果你想搜索点符号，你需要用 \  对其进行转义，所以这个 RegEx 只会匹配精确的文本 "b.t"： b\.t 2) .* — 匹配任何字符 这里 . 表示“任何字符”，* 表示“此符号之前的任何内容重复任意次数”。它们一起（.* ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/simple-regex-tricks-for-beginners/</link>
                <guid isPermaLink="false">62fdc03b60480505ded7a71f</guid>
                
                    <category>
                        <![CDATA[ 正则表达式 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Mon, 19 Aug 2024 04:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/08/pexels-dmitry-demidov-515774-3852577.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/simple-regex-tricks-for-beginners-3acb3fa257cb/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Simple RegEx tricks for beginners</a>
      </p><p>一直想学习正则表达式但被它们的复杂性劝退了？在本文中，我将向你展示五个易于学习的正则技巧，你可以立即在你最喜欢的文本编辑器中开始使用它们。</p><h3 id="-">文本编辑器设置</h3><p>虽然现在几乎所有文本编辑器都支持正则表达式，但我将在本教程中使用 Visual Studio Code。你可以使用任何你喜欢的编辑器。另外，请注意，你通常需要在搜索输入附近的某处打开 RegEx。这是在 VS Code 中执行此操作的方法：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/GjEq59Gj7Io7MWY4OuayOCmqZo0f5ezXyvOS" class="kg-image" alt="GjEq59Gj7Io7MWY4OuayOCmqZo0f5ezXyvOS" width="309" height="64" loading="lazy"><figcaption>You need to enable RegEx by checking this option</figcaption></figure><h3 id="1-"><strong>1) <code>. </code>— </strong>匹配任何字符</h3><p>让我们从简单的开始。点符号 <code>.</code> 匹配任何字符：</p><pre><code>b.t</code></pre><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/tMEDSKS2mHfOfqYTQP-elCZI5OnGUObC484v" class="kg-image" alt="tMEDSKS2mHfOfqYTQP-elCZI5OnGUObC484v" width="794" height="148" loading="lazy"></figure><p>上面的 RegEx 匹配 <code>"bot”</code>、<code>"bat”</code> 和任何其他以 <code>b</code> 开头并以 <code>t</code> 结尾的三个字符的单词。但是如果你想搜索点符号，你需要用 <code>\</code> 对其进行转义，所以这个 RegEx 只会匹配精确的文本 <code>"b.t"</code>：</p><pre><code>b\.t</code></pre><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/anNgoajLGzpFhPWYRnVGlowi0bL4Z4xni59R" class="kg-image" alt="anNgoajLGzpFhPWYRnVGlowi0bL4Z4xni59R" width="631" height="183" loading="lazy"></figure><h3 id="2-"><strong>2) .* — </strong>匹配任何字符</h3><p>这里 <code>.</code> 表示“任何字符”，<code>*</code> 表示“此符号之前的任何内容重复任意次数”。它们一起（<code>.*</code>）表示“任何符号任意次数”。例如，你可以使用它来查找以某些文本开头或结尾的匹配项。假设我们有一个带有以下签名的 JavaScript 方法：</p><pre><code>loadScript(scriptName: string, pathToFile: string)</code></pre><p>我们想要找到这个方法的所有调用，其中 <code>pathToFile</code> 指向文件夹 <code>“lua”</code> 中的任何文件。你可以为此使用以下正则表达式：</p><pre><code>loadScript.*lua</code></pre><p>这意味着，匹配所有以 <code>“loadScript”</code> 开头的文本，然后是最后一次出现的 <code>“lua”</code>。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/wCWC964KLZKxEHdW0fZlr4Z-X-vdcbYX-ogk" class="kg-image" alt="wCWC964KLZKxEHdW0fZlr4Z-X-vdcbYX-ogk" width="800" height="321" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">loadScript.*lua: matches everything starting with "loadScript" and ending in "lua"</code></figcaption></figure><h3 id="3-"><strong>3) ? — </strong>非贪婪匹配</h3><p><code>.*</code> 后面的 <code>?</code> 和其他一些 RegEx 序列之后的符号表示“尽可能少地匹配”。如果你查看上一张图片，你会看到文本 <code>“lua”</code> 在每次匹配中出现两次，并且匹配到第二个 <code>“lua”</code> 的所有内容。如果你想将所有内容匹配到第一次出现的 <code>"lua"</code>，你将使用以下正则表达式：</p><pre><code>loadScript.*?lua</code></pre><p>这意味着，匹配以 <code>"loadScript"</code> 开头的所有内容，然后是第一次出现的 <code>"lua"</code>。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2022/08/1660810429783.jpg" class="kg-image" alt="1660810429783" width="800" height="352" loading="lazy"></figure><h3 id="4-"><strong>4) ( ) $ — </strong>捕获组和反向引用</h3><p>好的，现在我们可以匹配一些文本。但是如果我们想改变我们找到的部分文本呢？为此，我们经常不得不使用捕获组。</p><p>假设我们更改了 <code>loadScript</code> 方法，现在它突然需要在两个参数之间插入另一个参数。让我们将这个新参数命名为 <code>id</code>，因此新函数签名应该是 <code>loadScript(scriptName, id, pathToFile)</code>。我们不能在这里使用文本编辑器的普通替换功能，但正则表达式正是我们所需要的。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/hRdlYnNzYuX64kcVoXrvtH2RfwaY3FzNZedD" class="kg-image" alt="hRdlYnNzYuX64kcVoXrvtH2RfwaY3FzNZedD" width="800" height="365" loading="lazy"><figcaption>loadScript\(.*?,.*?\)</figcaption></figure><p>上面你可以看到运行以下正则表达式的结果：</p><pre><code>loadScript\(.*?,.*?\)</code></pre><p>这意味着：匹配以 <em><em><code>"loadScript("</code></em></em> 开头的所有内容，然后是第一个 <em><em><code>,</code></em></em> 之前的任何内容，然后是第一个 <em><em><code>)</code></em></em> 之前的任何内容。</p><p>唯一对你来说可能看起来很奇怪的是 <code>\</code> 符号。它们用于转义括号。</p><p>我们需要对符号 <code>(</code> 和 <code>)</code> 进行转义，因为它们是 RegEx 用来捕获部分匹配文本的特殊字符。但是我们需要匹配实际的括号字符。</p><p>在前面的 RegEx 中，我们用 .*? 定义了方法调用的两个参数。符号。让我们通过在它们周围添加 ( 和 ) 符号来使我们的每个参数成为一个单独的捕获组：</p><pre><code>loadScript\((.*?),(.*?)\)</code></pre><p>如果你运行这个正则表达式，你会发现没有任何改变。这是因为它匹配相同的文本。但是现在我们可以将第一个参数称为 <code>$1</code>，将第二个参数称为 <code>$2</code>。这可被称为反向引用，它将帮助我们做我们想做的事情：在调用中间添加另一个参数。</p><p>搜索输入：</p><pre><code>loadScript\((.*?),(.*?)\)</code></pre><p>这意味着与之前的正则表达式相同，但将参数分别映射到捕获组 1 和 2。</p><p>替换输入：</p><pre><code>loadScript($1,id,$2)</code></pre><p>这意味着将每个匹配的文本替换为文本 <code><em><em>“loadScript(“</em></em></code>，后跟捕获组 1、<code><em><em>“id”</em></em></code>、捕获组 2 和 <code><em><em>)</em></em></code>。请注意，你不需要在替换输入中转义括号。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/w27UNrc7N2hkWAO1DmU6p0gulIYiwU-oYjpT" class="kg-image" alt="w27UNrc7N2hkWAO1DmU6p0gulIYiwU-oYjpT" width="800" height="354" loading="lazy"><figcaption>替换结果</figcaption></figure><h3 id="5-"><strong>5) [ ] — </strong>字符类</h3><p>你可以通过在这些字符周围放置 <code>[</code> 和 <code>]</code> 符号来列出要在特定位置匹配的字符。例如，class <code>[0-9]</code> 匹配从 0 到 9 的所有数字。你也可以显式列出所有数字：<code>[0123456789]</code> ——含义相同。你也可以将破折​​号与字母一起使用，<code>[a-z]</code> 将匹配任何小写拉丁字符，<code>[A-Z]</code> 将匹配任何大写拉丁字符，而 <code>[a-zA-Z]</code> 将匹配两者。</p><p>你也可以在字符类之后使用 <code>*</code>，就像在 <code>.</code> 之后一样，在这种情况下，这意味着：匹配该类中出现的任意数量的字符。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/2aTqw0lDyht0cE1gqoF3O5eYcemyzhIBhSzU" class="kg-image" alt="2aTqw0lDyht0cE1gqoF3O5eYcemyzhIBhSzU" width="800" height="463" loading="lazy"><figcaption>expect.*to.equal\([0–9]*\)：仅匹配我们期望被测变量等于数字的那些行</figcaption></figure><h3 id="--1"><strong>总结</strong></h3><p>你应该知道有几种正则表达式风格。我在这里讨论的是 JavaScript RegEx 引擎。大多数现代引擎都是相似的，但可能存在一些差异。通常，这些差异包括转义字符和反向引用标记。</p><p>我建议你立即打开文本编辑器并开始使用其中的一些技巧。你将看到自己现在可以比以前更快地完成许多重构任务。如果你对这些技巧感到满意，<a href="https://www.regular-expressions.info/">你就可以开始更多地研究正则表达式</a>。</p><p>感谢你阅读这篇文章。如果你觉得它有帮助，请将它分享给更多人。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 面向对象编程的四大支柱 ]]>
                </title>
                <description>
                    <![CDATA[ JavaScript 是一种多范式语言，可以按照不同的编程范式进行编写。编程范式本质上就是你在写代码时遵循一些规则，以帮助你解决一个特定的问题。 这就是四大支柱的含义。它们是软件设计原则，帮助你编写整洁的面向对象的代码。 面向对象编程的四大支柱是：  * 抽象  * 封装  * 继承  * 多态 让我们仔细看看这些支柱。 面向对象编程中的抽象 抽象的意思是把实现的细节隐藏在某个东西里面——有时是一个原型，有时是一个函数。因此，当你调用这个函数时，你不需要确切地了解它在做什么。 如果你必须了解一个大代码库中的每一个函数，你将永远无法编码。要花几个月的时间才能读完它。 你可以通过抽象化某些细节来创建一个可重用的、简单易懂的、容易修改的代码库。让我给你举个例子： function hitAPI(type){ 	if (type instanceof InitialLoad) { 		// 实施示例 	} else if (type instanceof NavBar) { 		// 实施示例 	} else { 		// 实施示例 	} } 这完全没有被抽象你能从这个例子中 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/four-pillars-of-object-oriented-programming/</link>
                <guid isPermaLink="false">63f43440cf34b8063af88f03</guid>
                
                    <category>
                        <![CDATA[ 面向对象 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 01 May 2024 03:22:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/02/The-four-pillars-of-object-orientation.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/four-pillars-of-object-oriented-programming/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">The Four Pillars of Object-Oriented Programming</a>
      </p><p>JavaScript 是一种多范式语言，可以按照不同的编程范式进行编写。编程范式本质上就是你在写代码时遵循一些规则，以帮助你解决一个特定的问题。</p><p>这就是四大支柱的含义。它们是软件设计原则，帮助你编写整洁的面向对象的代码。</p><p>面向对象编程的四大支柱是：</p><ul><li>抽象</li><li>封装</li><li>继承</li><li>多态</li></ul><p>让我们仔细看看这些支柱。</p><h1 id="-">面向对象编程中的抽象</h1><p>抽象的意思是把实现的细节隐藏在某个东西里面——有时是一个原型，有时是一个函数。因此，当你调用这个函数时，你不需要确切地了解它在做什么。</p><p>如果你必须了解一个大代码库中的每一个函数，你将永远无法编码。要花几个月的时间才能读完它。</p><p>你可以通过抽象化某些细节来创建一个可重用的、简单易懂的、容易修改的代码库。让我给你举个例子：</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">function hitAPI(type){
	if (type instanceof InitialLoad) {
		// 实施示例
	} else if (type instanceof NavBar) {
		// 实施示例
	} else {
		// 实施示例
	}
}</code></pre><figcaption>这完全没有被抽象</figcaption></figure><p>你能从这个例子中看到，你如何准确地实现你的自定义用例所需要的东西吗？</p><p>你需要的每一个新的 API 都需要一个新的 <code>if</code> 块，以及它自己的自定义代码。这不是抽象的，因为你需要为你添加的每一个新类型担心实现。这不是可重用的，而且是一个维护的恶梦。</p><p>像下面这样的方法怎么样？</p><pre><code class="language-javascript">hitApi('www.kealanparr.com', HTTPMethod.Get)</code></pre><p>你现在只需向你的函数传递一个 URL 和你想使用的 HTTP 方法就可以了。</p><p>你不必担心这个函数如何工作。它已经被解决了。这极大地有利于代码重用！同时也使你的代码更容易维护。</p><p>这就是抽象的意义所在。在你的代码中找到相似的东西，并提供一个通用的函数或对象来服务于多个地方/多个关注点。</p><p>这里还有一个很好的<strong>抽象</strong>的例子：想象一下，如果你正在创建一台机器，为你的用户制作咖啡，可能有两种方法：</p><h2 id="--1">如何在有抽象的情况下创建按钮</h2><ul><li>有一个标题为“制作咖啡”的按钮</li></ul><h2 id="--2">如何在没有抽象的情况下创建按钮</h2><ul><li>有一个标题为“烧水”的按钮</li><li>有一个标题为“向水壶中加入冷水”的按钮</li><li>有一个标题为“向干净的杯子中加入一勺咖啡粉”的按钮</li><li>有一个标题为“清洁任何脏杯子”的按钮</li><li>以及所有其他的按钮</li></ul><p>这是一个非常简单的例子，但第一种方法将逻辑抽象到机器中去了，第二种方法迫使用户了解如何制作咖啡，并且基本上是自己制作。</p><p>下一个支柱向我们展示了一种我们可以实现<strong>抽象</strong>的方法，即使用<strong>封装</strong>。</p><h1 id="--3">面向对象编程中的封装</h1><p>封装的定义是“将某物封闭在或像封闭在一个胶囊中的行为”。移除对部分代码的访问并使之成为私有的东西正是<strong>封装</strong>的意义所在（很多时候，人们把它称为数据隐藏）。</p><p>封装意味着你代码中的每个对象都应该控制自己的状态。状态是你的对象的当前“快照”，例如键、你的对象上的方法、布尔属性等等。如果你要重置一个布尔值或从对象中删除一个键，它们都是对你状态的改变。</p><p>限制你的代码中哪些部分可以访问。如果不需要的话，让更多的东西无法访问。</p><p>在 JavaScript 中，私有属性是通过使用闭包来实现的。下面是一个例子：</p><pre><code class="language-javascript">var Dog = (function () {

	// 私有
	var play = function () {
		// 执行代码
	};
    
	// 私有
	var breed = "Dalmatian"
    
	// 公有
	var name = "Rex";

	// 公有
	var makeNoise = function () {
 		return 'Bark bark!';
	};

 	return {
		makeNoise: makeNoise,
		name: name
 	};
})();

</code></pre><p>我们做的第一件事是创建一个<strong>立即被调用的函数</strong>（Immediately Invoked Function<strong><strong> </strong></strong>Expression，简称 IIFE）。这创建了一个任何人都可以访问的对象，但隐藏了一些细节。你不能调用 <code>play</code>，也不能访问 <code>breed</code>，因为我们没有在最后的对象中用 return 暴露它。</p><p>上面这个特殊的模式被称为<strong>揭示模块模式</strong>（Revealing Module Pattern），但它只是一个你如何实现封装的例子。</p><p>我想更多地关注封装的思想（因为它比仅仅学习一个模式更重要）。</p><p>反思一下，多想想如何能把你的数据和代码藏起来，把它分开来。模块化和明确责任是<strong>面向对象</strong>的关键。</p><p>我们为什么要喜欢私有？为什么不把所有的东西都变成全局的呢？</p><ul><li>很多不相关的代码将通过全局变量成为相互依赖/耦合的对象。</li><li>如果名字被重复使用，你很可能会覆盖这些变量，这可能会导致错误或不可预测的行为。</li><li>你很可能最终会产生<strong>意大利面条式的代码</strong>——很难推理出是什么在读写你的变量和改变状态。</li></ul><p>封装可以通过将长行代码分离成较小的独立函数来应用。将这些函数分离成模块。我们把数据隐藏在一个没有其他需要访问的地方，并显示出需要的东西。</p><p>这就是<strong>封装</strong>。将你的数据绑定到某个东西上，无论是类、对象、模块还是函数，并尽你所能保持它的私有性。</p><h1 id="--4">面向对象编程中的继承</h1><p>继承让一个对象获得另一个对象的属性和方法。在 JavaScript 中，这是通过<strong>原型继承</strong>完成的。</p><p>主要的好处是可重用性。我们知道，有时多个地方需要做同样的事情，而且除了一个小部分之外，它们需要做的事情都是一样的。这就是继承可以解决的问题。</p><p>每当我们使用继承时，我们都会努力使父代和子代的代码具有高关联度。例如， <code>Bird</code> 类型是否从 <code>DieselEngine</code> 类型中延伸出来？</p><p>保持你的继承简单易懂，可以预测。不要因为有一个你需要的方法或属性而从完全不相关的地方继承。继承并不能很好地解决这个特殊问题。</p><p>使用继承时，你应该需要大部分的功能（你不一定需要所有的功能）。</p><p>开发人员有一个原则，叫做<strong>里氏替换原则</strong>（Liskov Substitution principle）。它指出，如果你能在使用子类（我们称之为 <code>ChildType</code>）的任何地方使用父类（我们称之为 <code>ParentType</code>）——并且 <code>ChildType</code> 继承自 <code>ParentType</code>——那么你就通过了测试。</p><p>你不能通过这个测试的主要原因是，如果 <code>ChildType</code> 从父类移除了一些东西。如果 <code>ChildType</code> 删除了它从父类继承的方法，就会导致 <code>TypeError</code>，即出现未定义的东西，不符合你的期望。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-146.png" class="kg-image" alt="image-146" width="600" height="400" loading="lazy"><figcaption>箭头看起来好像走错了方向，但 Animal 是基础--父代</figcaption></figure><p>继承链是一个术语，用来描述从基础对象的原型（其他东西都是从它继承的）到继承链的“终点”（最后继承的类型——上面例子中的 <strong><strong>Dog</strong></strong>）的继承流。</p><p>尽力保持你的继承链清晰且合理。在使用<strong>继承</strong>时，你很容易出现<strong>编程反模式</strong>（Fragile base anti-pattern）——当你的基础原型被认为是“脆弱的”，因为你对基础对象做了一个“安全”的改变，然后开始破坏你所有的子对象。</p><h1 id="--5">面向对象编程中的多态</h1><p>多态意味着“以多种不同形式出现的条件”。这正是第四个也是最后一个支柱所关注的——同一继承链中的类型能够做不同的事情。</p><p>从上一个图中，我们可能有一个叫做 <code>Animal</code> 的基本原型，它定义了 <code>makeNoise</code>。然后，从这个原型延伸出来的每个类型都可以覆盖，做他们自己的自定义工作。就像这样。</p><pre><code class="language-javascript">// 让我们创建一个 Animal 和 Dog 的例子
function Animal(){}
function Dog(){}

Animal.prototype.makeNoise = function(){
	console.log("Base noise");
};

// 我们编码的大多数动物有 4 个，如果需要的话，这可以被重写
Animal.prototype.legs = 4;

Dog.prototype = new Animal();

Dog.prototype.makeNoise = function(){
	console.log("Woof woof");  
};

var animal = new Animal();
var dog = new Dog();

animal.makeNoise(); // Base noise
dog.makeNoise();    // Woof woof- 这个被覆盖了
dog.legs;           // 4！被继承了</code></pre><p><code>Dog</code> 扩展自 <code>Animal</code>，可以利用默认的 <code>legs</code> 属性。但它也能自己实现制造自己的噪音。</p><p>多态的真正力量在于共享行为，并允许自定义重写。</p><h1 id="--6"><strong>总结</strong></h1><p>我希望这篇文章解释清楚了什么是面向对象编程的四大支柱，以及它们是如何帮助我们写出更整洁更高效的代码的。</p><p>如果你喜欢这篇文章并想看到更多文章，可以在 <a href="https://twitter.com/kealanparr">Twitter</a> 上关注我。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 开发人员面试准备——如何通过协作来解决问题 ]]>
                </title>
                <description>
                    <![CDATA[ 无论你是一个多么有经验的开发者，为一份新工作参加面试总是很有压力。从我自己的经验来看，确实是这样。 我从事专业软件开发工作已经两年半了，经历过五次开发者面试。 我看到人们过多地关注学习具体的算法问题，或者在专门提供面试挑战的在线平台上进行培训。但是，面试还有另一面，我认为它同样重要——而人们对它的关注度较低。 因此，如果你想知道我认为你应该如何关注你的面试训练，请冲一杯咖啡，舒服地坐下来。不要担心，我会通过 Python 示例来帮助你理解。 这是我们要讨论的内容：  1. 算法和数据结构对面试重要吗  2. 专注于协作方式  3. 尝试做模拟面试     – 第一个面试问题示例     – 第二个面试问题示例     – 最难的面试问题示例  4. 总结 让我们深入了解一下。 算法和数据结构对面试重要吗 简短的回答是重要。 了解数据结构和算法往往能帮助你找到工作。此外，拥有扎实的算法背景的一个好处是在面对挑战性问题时能够做出更好的决定。 在现实生活中，算法问题不会像你可能研究的学术练习中的语句那样。不过，你经历的训练越多，你就越有可能识别出平衡二叉树或最短路径算法的应用 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/collaborative-problem-solving-with-python/</link>
                <guid isPermaLink="false">64546734f70dcb066852ad05</guid>
                
                    <category>
                        <![CDATA[ 面试技巧 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Tue, 05 Mar 2024 03:52:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/05/michal-czyz-ALM7RNZuDH8-unsplash.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/collaborative-problem-solving-with-python/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Developer Interview Prep – How to Use a Collaborative Approach to Problem-Solving</a>
      </p><p>无论你是一个多么有经验的开发者，为一份新工作参加面试总是很有压力。从我自己的经验来看，确实是这样。</p><p>我从事专业软件开发工作已经两年半了，经历过五次开发者面试。</p><p>我看到人们过多地关注学习具体的算法问题，或者在专门提供面试挑战的在线平台上进行培训。但是，面试还有另一面，我认为它同样重要——而人们对它的关注度较低。</p><p>因此，如果你想知道我认为你应该如何关注你的面试训练，请冲一杯咖啡，舒服地坐下来。不要担心，我会通过 Python 示例来帮助你理解。</p><h2 id="-"><strong>这</strong>是我们要讨论的内容：</h2><ol><li>算法和数据结构对面试重要吗</li><li>专注于协作方式</li><li>尝试做模拟面试<br>– 第一个面试问题示例<br>– 第二个面试问题示例<br>– 最难的面试问题示例</li><li>总结</li></ol><p>让我们深入了解一下。</p><h2 id="--1">算法和数据结构对面试重要吗</h2><p>简短的回答是<strong>重要</strong>。</p><p>了解数据结构和算法往往能帮助你找到工作。此外，拥有扎实的算法背景的一个好处是在面对挑战性问题时能够做出更好的决定。</p><p>在现实生活中，算法问题不会像你可能研究的学术练习中的语句那样。不过，你经历的训练越多，你就越有可能识别出平衡二叉树或最短路径算法的应用。</p><p>考虑到这一点，我建议不要仅仅为了你想擅长的面试而训练数据结构和算法。即使仅仅为了学习的乐趣，算法的设计和如何使用正确的数据结构也是非常有意思的话题。</p><p>面试的范围是非常有限的。要把重点放在解决问题的部分。试着了解如何使用一种算法（或其变体）来完成类似的任务。研究所有的变体，并学会在不同情况下识别它们，这将有助于你以开放的心态面对新任务。</p><p>另外，不要背诵解决方案。它不会给你带来任何提升。如果你只针对非常具体的主题进行训练，有经验的开发人员很容易提出正确的问题，让你“大显身手”。</p><p>针对一个问题，尝试用不同的方法来解决它。每次都尝试解决更难的问题。走出你的舒适区。</p><p>这将会给你回报。</p><h2 id="--2">专注于协作方式</h2><p>很多人在 <a href="https://leetcode.com/">Leetcode</a> 等网站上进行面试训练。这并没有什么问题。这些网站对训练你解决问题的能力很有帮助。</p><p>我至少在过去八年里一直在参加竞争性编程竞赛。我一直从这些在线资源中受益。</p><p>但是，对于大多数想要在面试中表现出色的软件工程师来说，有一件事很难理解。他们没有对软件开发中最重要的方面进行训练：<strong>协作</strong>。</p><p>通常情况下，当你在一个在线平台上处理问题时，它对一些输入的最大值有一些限制。它也可能有一些时间和内存的限制，要求你调整你的解决方案，以提高或降低效率。但这不是现实生活中的情况。</p><p>将这些约束条件映射到现实生活中的场景并不明显。它们通常是以来自客户的非常具体的要求或团队的非常具体的特征的形式出现。</p><p>在一个真实的项目中，团队将合作决定这些约束条件是什么。你必须分析你的用例、你有多少时间来完成任务、谁是最终的用户、有多少人将从事这项工作，等等......</p><p>经过一系列的讨论，你们会达成共识，最后开始实施适合你们的解决方案。而这个解决方案在某些情况下甚至不一定是更有效的，但可能是实施速度最快的。</p><p>这也是面试官在面试时希望从候选人那里看到的。</p><p>你不要直接跳到实施你所知道的针对你所面临的问题的最佳解决方案。相反，你应该把面试官当作一种有价值的资源，把他们当作你的队友（最终，你希望他们是）。问一些关于团队更倾向于如何实施该问题的解决方案的问题。</p><p>这将引发非常富有成效的讨论，你可以展示你的编码能力和你的协作技能。你可以先提出简单的解决方案，并带领你的面试官了解如何使用你的锦囊妙计来改进解决方案。</p><p>对于在线训练，我再说最后一点。我建议做个人和团队培训。使用诸如 <a href="https://codeforces.com/">Codeforces</a> 或 <a href="https://atcoder.jp/">AtCoder</a> 这样的网站，这些网站有大量有趣（和具有挑战性）的问题，并尝试每天与自己比赛。</p><p>不要专注于你的评级。我以前也这么做过，这只会让你退步。</p><h2 id="--3">尝试做模拟面试</h2><p>如果你读到了这里，我想提出一个小小的练习。让我们进行一次模拟面试，我将扮演面试官的角色。我会告诉你我认为候选人（你）应该如何回答每一个问题和执行每一项任务。</p><p>当然，我们将只关注编程挑战部分。面试的其他方面，如谈论以前的经验，也很重要，但我们将尝试在另一篇文章中介绍。</p><p>所以，如果你觉得准备得足够充分了，就开始吧！</p><h3 id="--4">第一个面试问题示例</h3><p>让我们从你要解决的任务开始。请记住，如果你和我真的有机会面试，我不会使用这个相同的例子，不过我当然将使用相同的方法 😉。</p><p>这个问题的陈述如下：</p><blockquote>“给出一个整数 <code>X</code>，确定它是否是一个质数。”</blockquote><p>你可能很想去找你知道的最好的解决方案来解决这个问题。我认为这种方法，正如我之前解释的，并不总是正确的。</p><p>相反，我想知道的是解决这个问题的不同方法。另外，我希望你能问一下这个任务的要求。比如说：</p><ul><li>我们应该以性能为目标，还是以更快的速度来解决它？</li><li>我们应该采取一个对其他开发者来说容易理解的解决方案吗？</li></ul><p>通常，在为一个问题创建第一个解决方案时要考虑的是：</p><ul><li>简单或困难：我们应该制定一个容易实施的解决方案，即使它并不完美，还是应该选择一个更强大但却难以实施的解决方案？</li><li>马虎与高效：我们应该先提供一个可行的、马虎的解决方案，然后再提供一个更有效的解决方案，还是应该直接选取高效的解决方案？</li></ul><p>评估你的团队想要优化什么。达成一个共识，并实施商定的解决方案。</p><p>就我而言，如果你提出你能想到的最简单的、最快的、正确的解决方案，然后引导我改进这个方案，我会很高兴。</p><p>对于这个问题，一个非常好的初步解决方案的例子是像下面这样的：</p><pre><code class="language-python"># naive.py

def is_prime(x: int) -&gt; bool:
    if x in [0, 1]:
        return False
    for i in range(2, x):
        if x % i == 0:
            return False
    return True
</code></pre><p>正如你所看到的，这个函数是正确的。由于质数是一个只能被数字 <code>1</code> 和它本身整除的整数，所以从 <code>2</code> 到 <code>x-1</code> 迭代寻找 <code>x</code> 的除数是有意义的，如果我们找到一个，我们可以立即返回 <code>False</code>。否则，我们返回 <code>True</code>。作为边缘案例，数字 <code>0</code> 和 <code>1</code> 根据定义不是质数。</p><p>这是一个很好的出发点！</p><p>现在，在深入优化这个方法之前，你可以讨论一下代码的风格。它够 Pythonic 吗？我们关心它吗？它可读吗？</p><p>所有这些问题一开始可能并不重要，但是，由于我们作为一个团队工作，它们确实很重要。有一个编码风格是很重要的，因为它使每个团队成员更容易理解对方的代码，而且会加快审核和重构的速度。另外，代码更多的是被阅读而不是被书写，所以可读性也很重要！</p><p>你可能很想炫耀一下你的 Python 技能，把前面的函数改写成如下：</p><pre><code class="language-python"># pythonic_naive.py

def is_prime(x: int) -&gt; bool:
    return False if x in {0, 1} else all(x % i != 0 for i in range(2, x))
</code></pre><p>我认为这是一个很好的、符合 Pythonic 的方法来写这个函数。但是，由于团队是最重要的，所以这种修改有待商榷。</p><p>可能有些团队成员对 Python 不是那么熟练。也许，在这种情况下，我们应该对可读性进行优化，保留我们最初写的那个函数。</p><p>在讨论性能之前，一个更有趣的补充是为该函数添加文档字符串（docstring）。正如我之前所说，这段代码很可能在将来被你的团队成员阅读。那么，重要的是，让他们（和你未来的自己）更容易理解这个函数在做什么。</p><p>也许把这个函数改成下面这样会更好：</p><pre><code class="language-python"># naive.py

def is_prime(x: int) -&gt; bool:
    """这个函数以一个整数 `x` 为参数，检查它是否是质数。

    Args:
        x (int): 判断是否为质数的整数。

    Returns:
        bool: 如果数字 `x` 是质数，返回 True，否则返回 False。
    """
    if x in [0, 1]:
        return False
    for i in range(2, x):
        if x % i == 0:
            return False
    return True
</code></pre><h4 id="--5">第一次优化</h4><p>到此为止，我们有了一个初步的解决方案，它是可行的。我们已经讨论了一些重要的话题，如代码的样式、可读性和开发人员的文档。这些都是作为一个团队工作时需要考虑的重要事情。</p><p>但是我们仍然没有谈及解决方案的性能问题。所以，现在可能是时候了。</p><p>在这个时候，你也许应该提出，这个函数可以被改进，这样它的运行速度就会更快。现在是你展示你的所有算法知识的时候了。</p><p>之前的解决方案的时间复杂度为 <code>O(x)</code>，其中 <code>x</code> 是函数作为参数的输入整数。这可以通过以下代码优化为 <code>O(sqrt(x))</code>：</p><pre><code class="language-python"># sqrt.py

import math

def is_prime(x: int) -&gt; bool:
    if x in [0, 1]:
        return False
    for i in range(2, int(math.sqrt(x)) + 1):
        if x % i == 0:
            return False
    return True
</code></pre><p>或者甚至像这样，不导入 <code>math</code> 库：</p><pre><code class="language-python"># sqrt.py

def is_prime(x: int) -&gt; bool:
    if x in [0, 1]:
        return False
    i = 2
    while i**2 &lt;= x:
        if x % i == 0:
            return False
        i += 1
    return True
</code></pre><p>两种方法对我来说都是可以的。</p><p>我在之前的实现中省略了文档字符串，是为了让实际的代码变化更加清晰。但是，如果能将函数的时间复杂度包含在文档中，对开发者来说是非常好的。你能提供的关于你的代码的信息越多，对你的团队成员和你自己来说就越好。</p><p>到目前为止，你做得很好。让我们继续吧!</p><h3 id="--6">第二个面试问题示例</h3><p>到目前为止，我已经能够评估出你具有必要的协作能力，能够与团队一起承担简单的任务。现在，是时候让事情变得复杂一点了。</p><p>这是我要提出的第二个问题的陈述：</p><blockquote>“给出两个整数 <code>L</code> 和 <code>R</code>，计算在区间 <code>[L, R]</code> 内有多少个质数。”</blockquote><p>再说一遍，我建议讨论一下团队关于这项任务的优先级是什么。从简单的开始，逐步讨论改进初始解决方案。强调过早的优化并不是一个好的实践。</p><p>同时，讨论使用你为前一个任务实施的解决方案来解决这个任务的可能性。如果我们有一个函数可以告诉我们一个整数是否是质数，我们就可以把它用于一个范围内的每个数字。</p><p>而这是你在现实生活中必须要做的事情。通常情况下，当一个新的任务出现时，团队应该研究一下所维护的项目，看看哪些东西可以被重用，以加快实施过程的速度。如果你不这样做，你可能最终要编码重复的功能。</p><p>因此，这个任务的一个好的初步解决方案可以是这样的：</p><pre><code class="language-python"># range_primes.py

from sqrt import is_prime

def count_primes(l: int, r: int) -&gt; int:
    primes = 0
    for i in range(l, r + 1):
        if is_prime(i):
            primes += 1
    return primes
</code></pre><p>这完全没问题，你已经表明你可以重用以前的功能来构建新的功能。就像第一个例子一样，你可能很想炫耀你的 Python 技能，把这个函数写成这样：</p><pre><code class="language-python"># range_primes.py

from sqrt import is_prime

def count_primes(l: int, r: int) -&gt; int:
    return sum(bool(is_prime(i)) for i in range(l, r + 1))
</code></pre><p>我建议不要把这个 Pythonic 实现作为你的第一选择。留待讨论，评估代码的可读性，也许还可以分析一下性能的差异。不要忘了 docstrings！</p><p>下一节是事情变得有趣的时候。继续阅读。我们正在进行最后一步......</p><h3 id="--7">最难的面试问题示例</h3><p>还记得我之前说过，竞争性编程网站中的约束条件很难映射到现实生活中的需求吗？</p><p>下面我将为你提出一个困难的挑战，让你确定这些约束条件，并实现你能做到的最佳解决方案：</p><blockquote>“假设一个客户希望我们把计算一个范围内的质数的功能作为一项服务来提供。他们希望把重点放在性能上，因为他们计划经常使用这项服务。你将如何实现它？”</blockquote><p>如果你一直在为面试练习你的算法技能，你可能已经解决了几次与此类似的问题。这里的主要区别是背景的变化。</p><p>我没有给你精确的指令、数字约束和输入或输出格式，而是给你一个更宽泛的任务描述。我在这里的目标和以前一样：让你和我互动，就好像我们是队友，从我们知道的一点信息中找出如何解决这个问题。</p><p>希望在交流了一些问题和答案之后，我们可以把之前的、比较模糊的说法翻译成更熟悉的东西：</p><blockquote>“给出一组形式为 <code>[L, R]</code> 的查询，对于每个查询，回答该范围内有多少个整数是质数。”</blockquote><p>这是有道理的，因为正如描述中所说，客户希望非常频繁地使用这项服务。</p><p>我们想把重点放在性能上。这应该是我们在实施解决方案时的主要关注点。但是，要达到一个最佳的解决方案，最好的办法还是从一个简单的解决方案开始，分析是否满足我们的性能要求，并不断地逐步改进。让我们来看看整个例子。</p><p>我们可以先用上一步实现的解决方案。它够好吗？</p><p>让我们假设，作为函数参数的最大数字范围是 <code>[1, 10^6]</code>。另外，现实中，让我们假设服务每分钟要回答的查询数量大约为 <code>10^5</code>。</p><p>我们之前的解决方案的时间复杂度为 <code>O(sqrt(n))</code>，以确定一个数字是否是质数。如果我们对范围内的每个数字都这样做，复杂度就会上升到 <code>O(n * sqrt(n))</code>。除此之外，如果我们对每个查询都这样做，我们最终会得到更高的时间复杂度 <code>O(q * n * sqrt(n))</code>。</p><p>把前面的变量替换为它们可能具有的最高值，你会得到这个解决方案将需要大约 <code>10^14</code> 次操作来回答所有的查询。假设一台计算机每秒可以进行大约 <code>10^8</code> 次基本操作，那么完成所有操作大约需要 <code>10^6</code> 秒。</p><blockquote>注：将 10^6 秒转换为天，你会感到惊讶的 🤯。</blockquote><p>如果目标是优先考虑我们的解决方案的性能，这个解决方案是不可行的。让我们看看如何改进它。</p><p>在这一点上，我所期望的是你利用自己通过所有在线平台的训练获得的最好经验，向我展示一个令人印象深刻的解决方案。这是展示你所有分析和算法技能的时候。</p><p>但只是现在，因为我知道你是一个团队型开发者。</p><h4 id="--8">最终解决方案</h4><p>由于这是一次模拟面试，我很想知道你是如何有效地解决最后这个问题的。让我知道你将如何实现这个解决方案，或者在 GitHub 上分享你的代码——不要害羞。</p><p>我向你保证一件事：如果你能在真正的面试中做到这一点，可能你不知道这个问题的最佳解决方案也不会太重要。这并不意味着你不应该尽力去解决它，但请放心，你已经做得非常好了。</p><p>就是这些啦，现在我想看看你的代码。</p><h2 id="--9"><strong><strong><strong>总结</strong></strong></strong></h2><p>在这篇文章中，我尝试总结一些我认为在编程面试中最重要的方面。我特别强调协作部分，因为我认为大多数人都低估了这种技能的重要性。这是必须的，特别是如果你想和其他开发者在一个团队中工作。</p><p>我尝试通过一个模拟面试来引导你，我解释了在面试中我对于一个标准的编程任务将遵循的思考过程。我希望这个练习是有用的，它可以在下一次面试中帮助你（作为候选人或面试官）。</p><p>欢迎分享你对这次模拟面试的想法，让我们开始一场富有成效的讨论。</p><p>回头见！👋</p><h2 id="--10"><strong><strong><strong>资源</strong></strong></strong></h2><ul><li>这篇文章里的代码示例在<a href="https://github.com/albexl/a-problem-solving-oriented-approach">这里</a>。</li><li>对于最后一个问题的提示：素数筛（<a href="https://github.com/albexl/data-structures-for-teaching/blob/dev/algorithms/number_theory/eratosthenes_sieve.py">Sieve of Eratosthenes</a>）算法的实现。</li></ul><p>👋 我是 Alberto，<a href="https://dowhile.se/">doWhile</a> 的软件工程师、竞争性程序员、老师和健身爱好者。</p><p>🧡 如果你喜欢这篇文章，请考虑分享它。</p><p>🔗 <a href="https://bio.link/albexl">All links</a> | <a href="https://twitter.com/albe_xl">Twitter</a> | <a href="https://www.linkedin.com/in/albexl/">LinkedIn</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 字符串转为布尔值 ]]>
                </title>
                <description>
                    <![CDATA[ 当你操作数据、从表单中接收数值以及以其他方式处理数据时，这些数值可能是不正确的数据类型。 假设你希望你的值是一个布尔值，要么是 true，要么是 false，但它被存储为一个字符串——"true" 或 "false"。这样一来，要想达到你的预期目的就变得很有挑战性，所以你必须首先将这些布尔字符串值转换成实际的布尔值。 在这篇文章中，你将学习如何使用 JavaScript 中的不同方法将字符串转换为布尔值。如果你着急了解，下面是操作方法： // 使用恒等运算符 console.log((boolString === "true")); // true / false // 使用正则 console.log((/true/).test(boolString)); // true 如果你不着急，让我们来了解每一种方法和更多的内容。 如何用恒等运算符（===）将一个字符串解析为布尔值 严格运算符是恒等运算符的另一个名字。只有当被比较的两个值是相同的时候，它才会返回 true 。这意味着它们的字母大小写——以及其他一切——也必须是相同的。否则，它将返回 false。 在这种情况下 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-string-to-boolean/</link>
                <guid isPermaLink="false">63b2dc9f836d320782be15d7</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 29 Nov 2023 10:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/01/string-to-boolean.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/javascript-string-to-boolean/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript String to Boolean – How to Parse a Boolean in JS</a>
      </p><p>当你操作数据、从表单中接收数值以及以其他方式处理数据时，这些数值可能是不正确的数据类型。</p><p>假设你希望你的值是一个布尔值，要么是 <code>true</code>，要么是 <code>false</code>，但它被存储为一个字符串——"true" 或 "false"。这样一来，要想达到你的预期目的就变得很有挑战性，所以你必须首先将这些布尔字符串值转换成实际的布尔值。</p><p>在这篇文章中，你将学习如何使用 JavaScript 中的不同方法将字符串转换为布尔值。如果你着急了解，下面是操作方法：</p><pre><code class="language-js">// 使用恒等运算符
console.log((boolString === "true")); // true / false

// 使用正则
console.log((/true/).test(boolString)); // true
</code></pre><p>如果你不着急，让我们来了解每一种方法和更多的内容。</p><h2 id="-">如何用恒等运算符（===）将一个字符串解析为布尔值</h2><p>严格运算符是恒等运算符的另一个名字。只有当被比较的两个值是相同的时候，它才会返回 <code>true</code>。这意味着它们的字母大小写——以及其他一切——也必须是相同的。否则，它将返回 <code>false</code>。</p><p>在这种情况下，你想把一个字符串转换为布尔值，这意味着你要把它与字符串 "true " 进行比较。如果两个值相同，它将返回布尔值 <code>true</code>，否则，它将返回布尔值 <code>false</code>。</p><pre><code class="language-js">let boolString = "true"; 
let boolValue = (boolString === "true"); 
console.log(boolValue); // true
</code></pre><p>这是一个严格相等运算符，将对字母大小写进行严格比较：</p><pre><code class="language-js">let boolString = "True"; 
let boolValue = (boolString === "true"); 
console.log(boolValue); // false
</code></pre><p>你可以使用 <code>toLowerCase()</code> 方法来解决这个问题。它首先将字符串值转换为适合比较的字母大小写，然后再进行比较。</p><pre><code class="language-js">let boolString = "True"; 
let boolValue = (boolString.toLowerCase() === "true"); 
console.log(boolValue); // true
</code></pre><p>另一个与恒等运算符非常相似的方法是正则方法，你可以测试两个值是否匹配。</p><h2 id="--1">如何用正则将一个字符串解析为布尔值</h2><p>Regex 正则是 Regular Expressions 的缩写。这一个庞大的编程主题，你可以用 regex 作为模式来匹配和测试字符串的字符组合。</p><p>一个非常简单的 regex 指南会告诉你，表达式要放在两个斜线（<code>/</code>）之间。例如，如果你想测试 "true" 字符串值，你会这样做：</p><pre><code class="language-js">let boolString = "true"; 
let boolValue = (/true/).test(boolString);
console.log(boolValue); // true
</code></pre><p>这也是区分大小写的：</p><pre><code class="language-js">let boolString = "True"; 
let boolValue = (/true/).test(boolString);
console.log(boolValue); // false
</code></pre><p>你必须在正则表达式的末尾添加 <code>i</code> 标志，以便进行不区分大小写的匹配。</p><pre><code class="language-js">let boolString = "True"; 
let boolValue = (/true/i).test(boolString);
console.log(boolValue); // true
</code></pre><h2 id="-not-">如何用双 NOT 运算符（<strong><code>*!!*</code></strong>）将一个字符串解析为布尔值</h2><p>你还应该知道如何使用单NOT运算符，你可以用它来反转一个结果。</p><p>当你在一个字符串前面加上 NOT 运算符时，它将返回 <code>true</code> 或 <code>false</code>。如果它是一个空字符串，它将返回 <code>true</code>，否则返回 <code>false</code>：</p><pre><code class="language-js">let stringValue1 = !'true';
let stringValue2 = !'';

console.log(stringValue1); // false
console.log(stringValue2); // true
</code></pre><p>这不是你想要的。相反，你想把一个字符串转换为布尔值，也就是说，当字符串为空时，它应该返回 <code>false</code>，而在其他情况下，它应该返回 <code>true</code>。</p><p>这时你可以使用双非逻辑运算符。你用它来反转单个 NOT 运算符的结果：</p><pre><code class="language-js">let stringValue1 = !!'true';
let stringValue2 = !!'';

console.log(stringValue1); // true
console.log(stringValue2); // false
</code></pre><p>你用这个方法将任何字符串值转换为布尔值。当它是空的，它返回 <code>false</code>。否则，它返回 <code>true</code>。</p><p>这个方法的一个缺点是，你不能把一个 <code>"false"</code> 的字符串转换成一个 <code>false</code> 的布尔值。只有当它是一个空字符串时才会返回 <code>false</code>。</p><h2 id="--2">如何用布尔封装器将一个字符串解析为布尔值</h2><p>JavaScript 的布尔对象代表一个布尔值。这个方法就像双非运算符一样。</p><pre><code class="language-js">// 语法
Boolean() 
</code></pre><p>当你向布尔对象传递一个字符串值时，它将评估为 <code>true</code>，但当你传递一个空字符串时，它将评估为 <code>false</code>。</p><pre><code class="language-js">let stringValue1 = Boolean('true');
let stringValue2 = Boolean('');

console.log(stringValue1); // true
console.log(stringValue2); // false
</code></pre><p>这个方法的唯一限制是，如果你在引号之间添加空格代表字符串，它将返回 <code>true</code>——意味着它将其视为一个字符串。</p><pre><code class="language-js">let stringValue = Boolean(' ');

console.log(stringValue); // true
</code></pre><p><strong>注意：</strong>当你转换一个 <code>"false"</code> 的字符串时，你会期望它返回一个布尔值为 <code>false</code>。但这将返回 <code>true</code>，因为它只在是空字符串时返回 <code>false</code>。</p><h2 id="--3">总结</h2><p>在这篇文章中，你已经学会了如何将一个字符串值转换为布尔值。涵盖所有情况的最佳方法是恒等运算符，而布尔对象和双非运算符有更好的语法。</p><p>祝你编码愉快！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 正则匹配实例 ]]>
                </title>
                <description>
                    <![CDATA[ 正则表达式 Regular expressions，缩写为 regex，或有时为 regexp，是你可能知道的真正强大和有用的概念之一。但它们可能令人生畏，特别是对初学的程序员来说，更是如此。 其实不一定是这样的。JavaScript 包括几个有用的方法，使正则表达式的使用更容易。在这些方法中，.match()、.matchAll() 和  .replace() 方法可能是你最经常使用的。 在本教程中，我们将介绍这些方法，并看看为什么你会使用这些方法而不是其他的 JS 方法。 对正则表达式的快速介绍 根据 MDN，正则表达式是“用于匹配字符串中字符组合的模式”。 这些模式有时可以包括特殊字符（*，+），断言（\W，^），组和范围（(abc)，[123]），以及其他使正则如此强大但难以掌握的东西。 就其核心而言，正则就是在字符串中寻找模式——从测试一个字符串的单个字符到验证一个电话号码是否有效，都可以用正则表达式完成。 如果你对正则表达式是个新手，想在继续阅读之前做一些练习，可以看看我们的交互式编码挑战 [https://www.freecodecamp.org/chines ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-regex-match-example-how-to-use-the-js-replace-method-on-a-string/</link>
                <guid isPermaLink="false">63b19d4a836d320782be13fa</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 15 Nov 2023 11:26:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/01/6020e04e0a2838549dcc0c26-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/javascript-regex-match-example-how-to-use-the-js-replace-method-on-a-string/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Regex Match Example – How to Use JS Replace on a String</a>
      </p><p>正则表达式 Regular expressions，缩写为 regex，或有时为 regexp，是你可能知道的真正强大和有用的概念之一。但它们可能令人生畏，特别是对初学的程序员来说，更是如此。</p><p>其实不一定是这样的。JavaScript 包括几个有用的方法，使正则表达式的使用更容易。在这些方法中，<code>.match()</code>、<code>.matchAll()</code> 和 <code>.replace()</code> 方法可能是你最经常使用的。</p><p>在本教程中，我们将介绍这些方法，并看看为什么你会使用这些方法而不是其他的 JS 方法。</p><h2 id="-">对正则表达式的快速介绍</h2><p>根据 MDN，正则表达式是“用于匹配字符串中字符组合的模式”。</p><p>这些模式有时可以包括特殊字符（<code>*</code>，<code>+</code>），断言（<code>\W</code>，<code>^</code>），组和范围（<code>(abc)</code>，<code>[123]</code>），以及其他使正则如此强大但难以掌握的东西。</p><p>就其核心而言，正则就是在字符串中寻找模式——从测试一个字符串的单个字符到验证一个电话号码是否有效，都可以用正则表达式完成。</p><p>如果你对正则表达式是个新手，想在继续阅读之前做一些练习，可以看看我们的<a href="https://www.freecodecamp.org/chinese/learn/javascript-algorithms-and-data-structures/regular-expressions/using-the-test-method">交互式编码挑战</a>。</p><h2 id="-match-"><strong>如何使用 <code>.match()</code> 方法</strong></h2><p>因此，如果正则是关于在字符串中寻找模式，你可能会问自己是什么使 <code>.match()</code> 方法如此有用？</p><p>不像 <code>.test()</code> 方法只是返回 <code>true</code> 或者 <code>false</code>，<code>.match()</code> 将实际返回与你测试的字符串的匹配，例如：</p><pre><code class="language-js">const csLewisQuote = 'We are what we believe we are.';
const regex1 = /are/;
const regex2 = /eat/;

csLewisQuote.match(regex1); // ["are", index: 3, input: "We are what we believe we are.", groups: undefined]

csLewisQuote.match(regex2); // null
</code></pre><p>这对一些项目来说真的很有帮助，特别是当你想在不改变原始字符串的情况下提取和操作你所匹配的数据时。</p><p>如果你只想知道一个搜索模式是否被找到，请使用 <code>.test()</code> 方法——它要快得多。</p><p>从 <code>.match()</code> 方法中你可以期待两个主要的返回值：</p><ul><li>如果有一个匹配，<code>.match()</code> 方法将返回一个包含匹配内容的数组。我们稍后会详细讨论这个问题。</li><li>如果没有匹配，<code>.match()</code> 方法将返回 <code>null</code>。</li></ul><p>你可能已经注意到了这一点，但如果你看上面的例子，<code>.match()</code> 只匹配 “are” 这个词的第一个实例。</p><p>很多时候，你会想知道一个模式与你所测试的字符串的匹配频率，所以让我们看看如何用 <code>.match()</code> 来实现。</p><h3 id="--1">不同的匹配模式</h3><p>如果有一个匹配，<code>.match()</code> 返回的数组有两种不同的模式，因为缺乏更好的术语。</p><p>第一种模式是不使用全局标志（<code>g</code>）时，例如在上面的例子中：</p><pre><code class="language-js">const csLewisQuote = 'We are what we believe we are.';
const regex = /are/;

csLewisQuote.match(regex); // ["are", index: 3, input: "We are what we believe we are.", groups: undefined]
</code></pre><p>在这种情况下，我们 <code>.match()</code> 一个数组，和包含第一个匹配项以及原始字符串中匹配项的索引、原始字符串本身，以及任何使用过的匹配组。</p><p>但是，假设你想看看 “are” 这个词在一个字符串中出现了多少次。要做到这一点，只需在你的正则表达式中添加全局搜索标志：</p><pre><code class="language-js">const csLewisQuote = 'We are what we believe we are.';
const regex = /are/g;

csLewisQuote.match(regex); // ["are", "are"]
</code></pre><p>你不会得到非全局模式所包含的其他信息，但你会得到一个包含你正在测试的字符串中所有匹配的数组。</p><h3 id="--2">大小写敏感</h3><p>要记住的一件重要事情是，regex 是区分大小写的。例如，假设你想看看 “we” 这个词在你的字符串中出现了多少次：</p><pre><code class="language-js">const csLewisQuote = 'We are what we believe we are.';
const regex = /we/g;

csLewisQuote.match(regex); // ["we", "we"]
</code></pre><p>在这种情况下，你要匹配的是一个小写的 “w” 和一个小写的 “e”，而后者只出现两次。</p><p>如果你想要 “we” 这个词的所有实例，无论它是大写还是小写，你有几个选择。</p><p>首先，你可以在用 <code>.match()</code> 方法测试之前对字符串使用 <code>.toLowercase()</code> 方法：</p><pre><code class="language-js">const csLewisQuote = 'We are what we believe we are.'.toLowerCase();
const regex = /we/g;

csLewisQuote.match(regex); // ["we", "we", "we"]
</code></pre><p>或者如果你想保留原来的大小写，你可以在你的正则表达式中添加不区分大小写的搜索标志（<code>i</code>）：</p><pre><code class="language-js">const csLewisQuote = 'We are what we believe we are.';
const regex = /we/gi;

csLewisQuote.match(regex); // ["We", "we", "we"]
</code></pre><h2 id="-matchall-"><strong>新的 </strong><code><strong>.matchAll()</strong></code><strong> 方法</strong></h2><p>现在你已经知道了关于 <code>.match()</code> 方法的所有情况，值得指出的是，最近引入了 <code>.matchAll()</code> 方法。</p><p>与 <code>.match()</code> 方法不同的是，<code>.matchAll()</code> 需要全局搜索标志（<code>g</code>），并返回一个迭代器或一个空数组：</p><pre><code class="language-js">const csLewisQuote = 'We are what we believe we are.';
const regex1 = /we/gi;
const regex2 = /eat/gi;

[...csLewisQuote.matchAll(regex1)]; 
// [
//   ["We", index: 0, input: "We are what we believe we are.", groups: undefined],
//   ["we", index: 12, input: "We are what we believe we are.", groups: undefined]
//   ["we", index: 23, input: "We are what we believe we are.", groups: undefined]
// ]

[...csLewisQuote.matchAll(regex2)]; // []
</code></pre><p>虽然它看起来只是一个更复杂的 <code>.match()</code> 方法，但 <code>.matchAll()</code> 的主要优势在于它能更好地处理捕获组。</p><p>下面是一个简单的例子：</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const csLewisRepeat = "We We are are";
const repeatRegex = /(\w+)\s\1/g;

csLewisRepeat.match(repeatRegex); // ["We We", "are are"]
</code></pre><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">.match()</code></figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-js">const csLewisRepeat = "We We are are";
const repeatRegex = /(\w+)\s\1/g;

[...repeatStr.matchAll(repeatRegex)];

// [
//   ["We We", "We", index: 0, input: "We We are are", groups: undefined],
//   ["are are", "are", index: 6, input: "We We are are", groups: undefined],
// ]
</code></pre><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">.matchAll()</code></figcaption></figure><p>虽然这只是勉强触及表面，但请记住，如果你使用 <code>g</code> 标志并希望得到 <code>.match()</code> 为单个匹配提供的所有额外信息（索引、原始字符串等），使用 <code>.matchAll()</code> 可能更好。</p><h2 id="-replace-">如何使用 .replace() 方法</h2><p>现在你知道了如何在字符串中匹配模式，你可能想对这些匹配做一些有用的事情。</p><p>一旦你找到一个匹配的模式，你最常做的事情之一就是用其他东西替换该模式。例如，你可能想把 “paidCodeCamp” 中的 “paid” 替换成 “free”。Regex 将是一个很好的方法来实现这一点。</p><p>由于 <code>.match()</code> 和 <code>.matchAll()</code> 返回每个匹配模式的索引信息，取决于你如何使用它，你可以用它来做一些花哨的字符串操作。但是有一个更简单的方法——通过使用 <code>.replace()</code> 方法。</p><p>使用 <code>.replace()</code>，你所需要做的就是把你想匹配的字符串或正则表达式作为第一个参数传给它，把要替换的字符串作为第二个参数：</p><pre><code class="language-js">const campString = 'paidCodeCamp';
const fCCString1 = campString.replace('paid', 'free');
const fCCString2 = campString.replace(/paid/, 'free');

console.log(campString); // "paidCodeCamp"
console.log(fCCString1); // "freeCodeCamp"
console.log(fCCString2); // "freeCodeCamp"
</code></pre><p>最重要的是，<code>.replace()</code> 返回一个新的字符串，而原来的则保持不变。</p><p>与 <code>.match()</code> 方法类似，<code>.replace()</code> 只会替换它找到的第一个匹配模式，除非你使用带有 <code>g</code> 标志的 regex：</p><pre><code class="language-js">const campString = 'paidCodeCamp is awesome. You should check out paidCodeCamp.';
const fCCString1 = campString.replace('paid', 'free');
const fCCString2 = campString.replace(/paid/g, 'free');

console.log(fCCString1); // "freeCodeCamp is awesome. You should check out paidCodeCamp."
console.log(fCCString2); // "freeCodeCamp is awesome. You should check out freeCodeCamp."
</code></pre><p>与之前类似，无论你传递的是字符串还是正则表达式作为第一个参数，都要记住匹配模式是区分大小写的：</p><pre><code class="language-js">const campString = 'PaidCodeCamp is awesome. You should check out PaidCodeCamp.';
const fCCString1 = campString.replace('Paid', 'free');
const fCCString2 = campString.replace(/paid/gi, 'free');

console.log(fCCString1); // "freeCodeCamp is awesome. You should check out PaidCodeCamp."
console.log(fCCString2); // "freeCodeCamp is awesome. You should check out freeCodeCamp."
</code></pre><h2 id="-replaceall-">如何使用 .replaceAll() 方法</h2><p>就像 <code>.match()</code> 有一个更新的 <code>.matchAll()</code> 方法一样，<code>.replace()</code> 也有一个更新的 <code>.replaceAll()</code> 方法。</p><p><code>.replace()</code> 和 <code>.replaceAll()</code> 之间唯一真正的区别是，如果你用 <code>.replaceAll()</code> 使用正则表达式，你需要使用全局搜索标志：</p><pre><code class="language-js">const campString = 'paidCodeCamp is awesome. You should check out paidCodeCamp.';
const fCCString1 = campString.replaceAll('paid', 'free');
const fCCString2 = campString.replaceAll(/paid/g, 'free');

console.log(fCCString1); // "freeCodeCamp is awesome. You should check out freeCodeCamp."
console.log(fCCString2); // "freeCodeCamp is awesome. You should check out freeCodeCamp."
</code></pre><p><code>.replaceAll()</code> 的真正好处是它的可读性更强一些，当你把一个字符串作为第一个参数传给它时，它将替换所有匹配的模式。</p><p>现在你知道了用 regex 和一些内置的 JS 方法匹配和替换部分字符串的基本知识。这些都是非常简单的例子，但我希望它仍然显示了即使是一点点的 regex 也可以是多么强大。</p><p>这对你有帮助吗？你是如何使用 <code>.match()</code>、<code>.matchAll()</code>、<code>.replace()</code> 和 <code>.replaceAll()</code> 方法的？请在 <a href="https://twitter.com/kriskoishigawa">Twitter</a> 上告诉我。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Python range() 函数示例 ]]>
                </title>
                <description>
                    <![CDATA[ 在这篇文章中，你将通过一些代码实例学习如何在 Python 中使用 range() 函数。 Python 中的 range() 函数是什么？range() 函数语法详解 Python 内置的 range() 函数主要在处理 for 循环时使用——你可以用它来循环某些代码块指定次数。 range() 函数接受三个参数——一个是必需的，两个是可选的。 默认情况下，range() 函数的语法就像下面这样： range(stop) stop 参数是必需的。 range() 函数返回一个从 0 开始、增量为 1、以你指定的 stop 为结束值的数字序列（不包括结束值）。 但是如果你想遍历你指定的两个数字的范围，而不想从 0 开始计数，怎么办？ 你可以传递第二个可选的起始值，start，来指定起始数字。语法是这样的： range(start, stop) 这种语法在起始值（包括）和结束值（不包括）的基础上生成一个数字序列，以 1 为增量递增。 最后，如果你不希望默认的增量是 1，你可以指定第三个可选的参数，即步长 step。语法是这样的： range(start, st ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/python-range-function-example/</link>
                <guid isPermaLink="false">64212316e32a7606487d58d3</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Mon, 06 Nov 2023 05:02:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/03/pexels-christina-morillo-1181671.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/python-range-function-example/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Python range() Function Example</a>
      </p><p>在这篇文章中，你将通过一些代码实例学习如何在 Python 中使用 <code>range()</code> 函数。</p><h2 id="python-range-range-">Python 中的 range() 函数是什么？range() 函数语法详解</h2><p>Python 内置的 <code>range()</code> 函数主要在处理 <code>for</code> 循环时使用——你可以用它来循环某些代码块指定次数。</p><p><code>range()</code> 函数接受三个参数——一个是必需的，两个是可选的。</p><p>默认情况下，<code>range()</code> 函数的语法就像下面这样：</p><pre><code>range(stop)
</code></pre><p><code>stop</code> 参数是<strong>必需的</strong>。</p><p><code>range()</code> 函数返回一个从 <code>0</code> 开始、增量为 <code>1</code>、以你指定的 <code>stop</code> 为结束值的数字序列（不包括结束值）。</p><p>但是如果你想遍历你指定的两个数字的范围，而不想从 <code>0</code> 开始计数，怎么办？</p><p>你可以传递第二个<strong>可选的</strong>起始值，<code>start</code>，来指定起始数字。语法是这样的：</p><pre><code>range(start, stop)
</code></pre><p>这种语法在起始值（包括）和结束值（不包括）的基础上生成一个数字序列，以 <code>1</code> 为增量递增。</p><p>最后，如果你不希望默认的增量是 <code>1</code>，你可以指定第三个<strong>可选的</strong>参数，即步长 <code>step</code>。语法是这样的：</p><pre><code>range(start, stop, step)
</code></pre><p>这种语法产生一个数字序列，从 <code>start</code>（包括）开始计数，并根据 <code>step</code> 递增，直到到达 <code>stop</code>（不包括）。</p><h2 id="-stop-range-">如何使用只有 stop 参数的 range() 函数</h2><p>当在 <code>range()</code> 中只使用 <code>stop</code> 参数时，计数从 <code>0</code> 开始，按 <code>1</code> 递增，当达到你指定的 <code>stop</code> 值时计数停止。</p><p>请记住，你指定的结束值并不包括在内！</p><p>如果你指定的 <code>stop</code> 参数是 <code>5</code>，那么范围包括 <code>0-4</code>，而不是 <code>0-5</code>——计数将停止在 <code>4</code> 而不是 <code>5</code>。</p><p>让我们看看下面的例子：</p><pre><code class="language-python">for num in range(5):
    print(num)
    
# output 

# 0
# 1
# 2
# 3
# 4
</code></pre><p>在这个例子中，我指定了一个 <code>range(5)</code>。</p><p>该函数从 <code>0</code> 开始计数，每次迭代都递增 <code>1</code>，最后结束于 <code>4</code>。</p><h2 id="-start-stop-range-">如何使用带有 start 和 stop 参数的 range() 函数</h2><p>如果你想有一个两个数字的范围，你可以使用两个参数——<code>start</code> 和 <code>stop</code>。请记住，起始值是包括在内的，而结束值则不是。</p><p>如果你想要一个从 5（含）到 10（含）的数值范围，你可以这样写一个 <code>range(5,11)</code>：</p><pre><code class="language-python">for num in range(5,11):
  print(num)
  
# output

# 5
# 6
# 7
# 8
# 9
# 10
</code></pre><p>你也可以向 <code>range()</code> 传递负的整数值：</p><pre><code class="language-python">for num in range(-5, 1):
  print(num)

# output

# -5
# -4
# -3
# -2
# -1
# 0
</code></pre><p>这里需要注意的是，你不能向 <code>range()</code> 传递浮点值。</p><p>在这个例子中，当我传递两个浮点数作为参数时，会出现错误：</p><pre><code class="language-python">for num in range(5.2, 4.3):
  print(num)

# output

# Traceback (most recent call last):
#  File "main.py", line 1, in &lt;module&gt;
#    for num in range(5.2, 4.3):
# TypeError: 'float' object cannot be interpreted as an integer
</code></pre><p>你可以传递负整数或正整数作为 <code>start</code> 和 <code>stop</code> 参数。</p><h2 id="-start-stop-step-range-">如何使用带有 start、stop 和 step 参数的 range() 函数</h2><p>默认情况下，增量值是 <code>1</code>，并且没有指定。也就是说，你可以通过向 <code>range()</code> 函数传递一个 <code>step</code> 参数来改变它。</p><p>让我们看看下面的例子：</p><pre><code class="language-python">for num in range(10,21,2):
  print(num)
  
# output

# 10
# 12
# 14
# 16
# 18
# 20
</code></pre><p>在上面的例子中，我生成了一个从 <code>10</code> 到 <code>20</code> 的数字序列，并将递增量设置为 2，我通过指定 <code>step</code> 值为 <code>2</code> 来实现这一点。</p><p>需要注意的是，<code>step</code> 可以是一个负数或正数，但不能是 <code>0</code>。</p><p>下面是你如何生成一个带有负数 <code>step</code> 参数的范围：</p><pre><code class="language-python">for num in range(20, 11, -2):
  print(num)

# output

# 20
# 18
# 16
# 14
# 12
</code></pre><p>上面的代码生成了一个反向的数字序列。</p><p>再看看当 <code>step</code> 为 <code>0</code> 时发生了什么：</p><pre><code class="language-python">for num in range(10, 21 0):
  print(num)

# output

#  File "main.py", line 1
#    for num in range(10, 21 0):
                            ^
# SyntaxError: invalid syntax
</code></pre><h2 id="-range-">如何使用 range() 函数创建一个数字列表</h2><p>你可以通过将 <code>range()</code> 函数作为参数传递给 <code>list()</code> 构造函数来创建一个数字列表，就像这样：</p><pre><code class="language-python">my_numbers_list = list(range(5))

print(my_numbers_list)

# output

# [0, 1, 2, 3, 4]
</code></pre><p>在上面的例子中，我创建了一个从 <code>0</code> 到 <code>4</code> 的数字列表。</p><h2 id="-python-len-range-">如何在 Python 中使用 len() 函数和 range() </h2><p>假设你有一个项目的列表，想根据列表的长度对这些项目做一些处理。</p><p>为此，你可以使用 <code>range()</code> 并将列表的长度作为参数传给函数。</p><p>要计算一个列表的长度，可以使用 <code>len()</code> 函数。</p><pre><code class="language-python">programming_languages = ["Python", "JavaScript", "Java", "C++"]

programming_languages_length = len(programming_languages)

for languages in range(programming_languages_length):
  print("Hello World")
  
# output

# Hello World
# Hello World
# Hello World
# Hello World
</code></pre><h2 id="-"><strong>总结</strong></h2><p>你现在知道如何在 Python 中使用 <code>range()</code> 函数了。</p><p>要学习更多关于 Python 的知识，请查看 freeCodeCamp 的 <a href="https://www.freecodecamp.org/news/python-programming-course/">Python 入门课程</a>。</p><p>感谢阅读，happy coding！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Git Revert 或 Git Reset 重置文件或提交 ]]>
                </title>
                <description>
                    <![CDATA[ 当与团队一起做一个项目或你自己做一个项目时，通过版本控制来跟踪代码库的变化是很重要的。 Git 为你提供了不同的命令来跟踪文件变化。这些命令将使你能够与其他开发人员协作、访问文件历史、管理代码，等等。 在这篇文章中，你将学习如何恢复和重置一个文件或提交到之前的提交。 我们将使用一些简单的 HTML 代码来演示如何使用 Git 恢复和重置到之前的提交。 让我们开始吧！ 如何重置一个文件或提交 在本节中，你将学习如何使用以下命令来恢复/重置一个文件或提交：  * git revert  * git reset 如何使用 git revert 命令恢复文件或提交 下面是 git revert 命令的语法： git revert [commit ID] 下面是我们将使用的代码： <!DOCTYPE html> <html lang="en">   <head>     <title>Document</title>   </head>   ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/git-revert-how-to-reset-a-file-or-commit/</link>
                <guid isPermaLink="false">6405e8fcf2e3690630c995ae</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 01 Nov 2023 13:22:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/03/roman-synkevych-wX2L8L-fGeA-unsplash--1-.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/git-revert-how-to-reset-a-file-or-commit/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Git Revert – How to Reset a File or Commit</a>
      </p><p>当与团队一起做一个项目或你自己做一个项目时，通过版本控制来跟踪代码库的变化是很重要的。</p><p>Git 为你提供了不同的命令来跟踪文件变化。这些命令将使你能够与其他开发人员协作、访问文件历史、管理代码，等等。</p><p>在这篇文章中，你将学习如何恢复和重置一个文件或提交到之前的提交。</p><p>我们将使用一些简单的 HTML 代码来演示如何使用 Git 恢复和重置到之前的提交。</p><p>让我们开始吧！</p><h2 id="-">如何重置一个文件或提交</h2><p>在本节中，你将学习如何使用以下命令来恢复/重置一个文件或提交：</p><ul><li><code>git revert</code></li><li><code>git reset</code></li></ul><p>如何使用 git revert 命令恢复文件或提交</p><p>下面是 <code>git revert</code> 命令的语法：</p><pre><code class="language-txt">git revert [commit ID]</code></pre><p>下面是我们将使用的代码：</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;title&gt;Document&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello World!&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>让我们假设上面的文件是每个在项目上工作的开发人员的代码库的当前状态。</p><p>我们将对该文件做一些修改，然后添加、提交和推送这些修改，即：</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;title&gt;Document&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello World!&lt;/h1&gt;

    &lt;ul&gt;
        &lt;li&gt;Red&lt;/li&gt;
        &lt;li&gt;Yellow&lt;/li&gt;
        &lt;li&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>我们已经在代码中添加了一个颜色列表。让我们用下面的命令推送这些变化：</p><pre><code class="language-bash">git add .
git commit -m "added colors to the HTML file"
git push -u origin main</code></pre><p>现在，新的变化已经为在特定代码分支上工作的所有人更新了。</p><p>但如果添加的颜色是应该在未来为用户发布的功能的一部分呢？你提前发布了这个功能，就犯了一个错误。</p><p>要恢复到之前的提交，你需要那个特定提交的 ID。要得到提交 ID，请执行下面的命令：</p><pre><code class="language-bash">git log</code></pre><p>该命令会显示每个提交的 ID、作者和日期。它应该是这样的：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/git-log.PNG" class="kg-image" alt="git-log" width="600" height="400" loading="lazy"><figcaption>git log</figcaption></figure><p>在我们的例子中，提交 ID 是 785dd5a6dd0f1ebd9e06045df787d8d28fd38285。</p><p>所以，要重置文件，就用 <code>git revert [commit ID]</code>，即：</p><pre><code class="language-bash">git revert 785dd5a6dd0f1ebd9e06045df787d8d28fd38285</code></pre><p>上面的命令将重置某个特定提交之前对文件所做的所有修改。下面是现在的 HTML 文件：</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;title&gt;Document&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello World!&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><p>现在你可以推送这个新状态了。</p><p>请注意，<code>git revert</code> 命令并不从远程仓库中删除被恢复的提交。相反，它为被恢复的改动创建一个新的提交。</p><p>这意味着你将拥有被恢复的提交的历史和包含被恢复的修改的文件的新提交。</p><h3 id="-git-reset-">如何使用 git reset 命令重置文件或提交</h3><p><code>git reset</code> 命令也可以用来恢复修改。请看下面的提交历史：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/02/git-log-1.PNG" class="kg-image" alt="git-log-1" width="600" height="400" loading="lazy"></figure><p>上面的图片显示了所有的提交历史——从第一次提交到最后一节的还原提交。</p><p>如果我们用 <code>git reset [commit ID]</code> 来还原某个特定的提交，之后的其他提交都会从提交历史中被删除。</p><p>下面是一个例子：</p><pre><code class="language-bash">git reset c1e4962a9b355f023c250609cfa9303a67b3063e</code></pre><p>使用第一次提交的提交 ID，我们回到了第一次提交的状态。</p><p>运行 <code>git log</code> 命令，你会有一个这样的提交历史：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/02/git-log-2.PNG" class="kg-image" alt="git-log-2" width="600" height="400" loading="lazy"></figure><p>虽然你已经成功地恢复到了第一次提交，但代码中其他提交的历史却被抹去了。这在与其他开发者合作时，会产生非常负面的影响。</p><h2 id="--1">摘要</h2><p>在这篇文章中，我们谈到了两个重要的 Git 命令，即 <code>git revert</code> 和 <code>git reset</code>，用于撤销 Git 仓库中的修改。</p><p>这两个命令都能让你回到代码库中之前的指定状态，但后果不同。</p><p><code>git revert</code> 命令会恢复到指定的提交，但会保留代码库中其他所有提交的历史，并为恢复的改动创建一个新的提交。这是一种在与他人合作时撤销修改的更有效的方式。</p><p>另外，<code>git reset</code> 命令会恢复到指定的提交，然后删除指定提交之后的所有提交。</p><p>为了安全起见，在撤销有其他开发者工作的 repo 中的修改时，请使用 <code>git revert</code>。</p><p>你可以在撤销修改后想完全删除提交的情况下使用 <code>git reset</code>。</p><p>Happy coding!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 二维数组 ]]>
                </title>
                <description>
                    <![CDATA[ 在 JavaScript 编程中，我们都习惯于创建和处理一维数组。这些数组中包含具有类似的数据类型或多种数据类型的元素。 但是我们也可以了解一下 JS 中的二维数组。 在这篇文章中，你将了解什么是二维数组以及它们在 JavaScript 中是如何运行的。它与其他编程语言有很大的不同，因为从技术上讲，JavaScript 中没有二维数组。 什么是二维数组 二维数组是一个数据元素的集合，以行和列的网格状结构排列。数组中的每个元素都被称为单元，可以通过其行和列的索引进行访问。 [ a1, a2, a3, ..., an,   b1, b2, b3, ..., bn,   c1, c2, c3, ..., cn,   .   .   .   z1, z2, z3, ..., zn ] 在 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-2d-arrays/</link>
                <guid isPermaLink="false">63d3e2ae7ed3df06baffcade</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 23 Aug 2023 03:22:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/01/cover-template--9-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/javascript-2d-arrays/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript 2D Array – Two Dimensional Arrays in JS</a>
      </p><p>在 JavaScript 编程中，我们都习惯于创建和处理一维数组。这些数组中包含具有类似的数据类型或多种数据类型的元素。</p><p>但是我们也可以了解一下 JS 中的二维数组。</p><p>在这篇文章中，你将了解什么是二维数组以及它们在 JavaScript 中是如何运行的。它与其他编程语言有很大的不同，因为从技术上讲，JavaScript 中没有二维数组。</p><h2 id="-">什么是二维数组</h2><p>二维数组是一个数据元素的集合，以行和列的网格状结构排列。数组中的每个元素都被称为单元，可以通过其行和列的索引进行访问。</p><pre><code class="language-js">[ a1, a2, a3, ..., an,
  b1, b2, b3, ..., bn,
  c1, c2, c3, ..., cn,
  .
  .
  .
  z1, z2, z3, ..., zn ]
</code></pre><p>在 JavaScript 中，没有像其他常用的编程语言如 C、C++ 和 Java 那样直接创建二维数组的语法。</p><p>你可以通过一个数组的数组在 JavaScript 中创建二维数组，像下面这样：</p><pre><code class="language-js">let twoDimensionalArr = [ [ a1, a2, a3, ..., an ],
[ b1, b2, b3, ..., bn ],
[ c1, c2, c3, ..., cn ],
.
.
.
[ z1, z2, z3, ..., zn ] ];
</code></pre><p>但是有一个限制。需要注意的是，二维数组有一个固定的大小。这意味着，一旦它们被创建，行和列的数量应该是固定的。而且，每一行应该有类似数量的元素（列）。</p><p>例如，下面的数组有三行和四个元素：</p><pre><code class="language-js">let myArray = [
[0, 1, 2, 3], 
[4, 5, 6, 7],
[8, 9, 0, 0]
];
</code></pre><p>其局限性在于，对于锯齿形数组，你不能指定固定的行和列。这意味着一个锯齿形数组可以有 m 行，每行有不同数量的元素。</p><pre><code class="language-js">let myArray = [
[0, 1, 2, 3], 
[4, 5, 6, 7],
[8, 9]
];
</code></pre><h2 id="-javascript-">为什么要在 JavaScript 中使用二维数组</h2><p>说到这里，你可能会问自己二维数组的重要性，特别是如果这是你第一次阅读关于二维数组的文章。</p><p>在 JavaScript 中，我们使用二维数组，也称为矩阵，以一种结构化和有组织的方式来存储和处理数据。</p><ul><li>它们允许有效地存储和操作大量的数据，例如在图像或视频处理、科学模拟和电子表格应用中。</li><li>二维数组还可以使用矩阵运算，如矩阵乘法和转置，这可以简化复杂的计算，使代码更易读。</li><li>二维数组可以表示线性代数中的数学矩阵和广泛的数据，如图形、地图和表格。</li><li>二维数组常用于涉及数据表、图像处理和游戏开发的应用中。</li></ul><h2 id="-javascript--1">如何访问 JavaScript 二维数组中的元素</h2><p>在学习如何在 JavaScript 中创建二维数组之前，我们首先要学习如何访问二维数组中的元素。</p><p>你可以使用两个索引来访问二维数组中的元素，一个是行的索引，一个是列的索引。假设你有下面这个二维数组：</p><pre><code class="language-js">let MathScore = [
    ['John Doe', 20, 60, 'A'],
    ['Jane Doe', 10, 52, 'B'],
    ['Petr Chess', 5, 24, 'F'],
    ['Ling Jess', 28, 43, 'A'],
    ['Ben Liard', 16, 51, 'B']
];
</code></pre><p>上面是一个锯齿状的数组，其中每个元素都保存着学生的姓名、测试分数、考试分数和等级。你可以使用行和列的索引来访问特定的元素，如下面的语法：</p><pre><code class="language-js">arrayName[rowIndex][columnIndex]
</code></pre><p>为了更好地理解这一点，让我们使用 <code>console.table(arrayName)</code> 将上面的二维数组转换为表格。</p><p><em><strong>注意：</strong>确保用你的二维数组的名称替换 <code>arrayName</code>。在我的例子中，它是 <code>MathScore</code>。</em></p><p>你会得到这样的输出，显示行和列的索引。记住，数组是零索引的，也就是说，项目的索引是从 0 开始的，而不是 1。</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropboxusercontent.com/s_357699FE5D3810E96F0B6815928F66401CBB5DFE39FAA8BDA7338BEC543A022F_1673843651418_Untitled.drawio+3.png" class="kg-image" alt="s_357699FE5D3810E96F0B6815928F66401CBB5DFE39FAA8BDA7338BEC543A022F_1673843651418_Untitled.drawio+3" width="1221" height="411" loading="lazy"></figure><p>请注意，<code>(index)</code> 列和行是为了表示内部数组的索引。</p><p>你使用两个方括号来访问二维或多维数组的一个元素。第一个是访问行，第二个是访问指定行中的元素。</p><pre><code class="language-js">console.log(MathScore[4][0]); // returns 'Ben Liard'
console.log(MathScore[2][1]); // returns 5
console.log(MathScore[1][3]); // returns 'B'
console.log(MathScore[2][2]); // returns 24
</code></pre><h3 id="--1">如何访问一个二维数组的第一个和最后一个元素</h3><p>有时你可能需要找到一个二维数组的第一个和最后一个元素。你可以使用行和列的第一个和最后一个索引来完成这个任务。</p><p>第一个元素的行和列的索引总是 0，这意味着你将使用 <code>arrayName[0][0],</code>。</p><p>然而，最后一个元素的索引可能不容易确定。例如，在上面的例子中，第一个元素是 “John Doe”，而最后一个是 “B”：</p><pre><code class="language-js">console.log(MathScore[0][0]); // returns 'John Doe'
console.log(MathScore[MathScore.length-1][(MathScore[MathScore.length -1]).length - 1]); // returns 'B'
</code></pre><h3 id="--2">如何将一个二维数组的所有元素相加</h3><p>在某些情况下，你的二维数组的所有元素可能都是数字，你可能需要将它们相加。你可以用一个嵌套的循环来做这件事。你将首先遍历各行，然后，对于每一行，你遍历其元素。</p><pre><code class="language-js">let numberArr = [
  [10, 20, 60],
  [8, 10, 52],
  [15, 5, 24],
  [26, 28, 43],
  [12, 16, 51]
];

var sum = 0;
numberArr.forEach((row) =&gt; {
  row.forEach((element) =&gt; {
    sum += element;
  });
});
console.log("The sum of all elements in the array is:" + sum); // returns "The sum of all elements in the array is: 380"
</code></pre><h2 id="-javascript--2">如何在 JavaScript 中操作二维数组</h2><p>你可以像操作一维数组一样使用一般的数组方法来操作二维数组，比如 pop、push、splice 等等。</p><p>让我们先来学习如何在二维数组中添加/插入一个新的行和元素。</p><h3 id="--3">如何在二维数组中插入一个元素</h3><p>你可以用 <code>push()</code> 和 <code>unshift()</code> 方法向二维数组中添加一个或许多元素。</p><p><code>push()</code> 方法将元素添加到二维数组的结尾，而 <code>unshift()</code> 方法将元素添加到二维数组的开头。</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore.push(["Tom Right", 30, 32, "B"]);

MathScore.unshift(["Alice George", 28, 62, "A"]);
</code></pre><p>当你 <code>console.log()</code> 或 <code>console.table()</code> 数组时，你会看到新的行已经被添加：</p><pre><code class="language-js">[
  ["Alice George", 28, 62, "A"],
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"],
  ["Tom Right", 30, 32, "B"]
]
</code></pre><p>你也可以向内层数组添加元素，但是只向一个内层数组推送而不影响所有数组元素的做法是错误的。这是因为二维数组的每个元素数组应该拥有相同数量的元素。</p><pre><code class="language-js">MathScore[0].push("B");
</code></pre><p>你可以一次性向所有元素数组添加元素，而不是只影响一个数组元素：</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore.forEach((score) =&gt; {
  let totalScore = score[1] + score[2];
  score.push(totalScore);
});

console.log(MathScore);
</code></pre><p>这将返回：</p><pre><code class="language-js">[
  ["John Doe", 20, 60, "A", 80],
  ["Jane Doe", 10, 52, "B", 62],
  ["Petr Chess", 5, 24, "F", 29],
  ["Ling Jess", 28, 43, "A", 71],
  ["Ben Liard", 16, 51, "B", 67]
]
</code></pre><p>你也可以使用 <code>unshift()</code> 方法在开头插入元素，使用 <code>splice()</code> 方法在数组的中间插入：</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore.splice(2, 0, ["Alice George", 28, 62, "A"]);
</code></pre><p>在上面，<code>1</code> 是你想让新数组插入的位置（记住它是零索引的），使用 <code>0</code>，所以它没有删除任何元素，然后第三个参数是要添加的数组。</p><p>这就是输出结果：</p><pre><code class="language-js">[
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Alice George", 28, 62, "A"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
]
</code></pre><h3 id="--4">如何从二维数组中删除一个元素</h3><p>你也可以用 <code>pop()</code> 和 <code>shift()</code> 方法从一个二维数组的开头和结尾处移除元素。这与 <code>push()</code> 和 <code>unshift()</code> 方法的工作原理类似，但这次你不需要向这些方法添加任何参数。</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore.pop();

MathScore.shift();
</code></pre><p>当你 <code>console.log()</code> 或 <code>console.table()</code> 数组时，你会看到第一个和最后一个数组元素已经被移除：</p><pre><code class="language-js">[
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
]
</code></pre><p>你也可以从每个数组元素中移除元素：</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore.forEach((score) =&gt; {
  score.pop();
});

console.log(MathScore);
</code></pre><p>这将返回：</p><pre><code class="language-js">[
  ["John Doe", 20, 60],
  ["Jane Doe", 10, 52],
  ["Petr Chess", 5, 24],
  ["Ling Jess", 28, 43],
  ["Ben Liard", 16, 51]
]
</code></pre><p>你也可以使用 <code>shift()</code> 方法来移除开头的元素，使用 <code>splice()</code> 方法来移除特定位置上的数组元素：</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore.splice(2, 1);
</code></pre><p>在上面的代码中，你从 <code>MathScore</code> 二维数组的索引 <code>2</code> 的位置删除了一个项目。这将输出：</p><pre><code class="language-js">[
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
]
</code></pre><p><strong>注意：</strong>所有的 JavaScript 数组方法都会对二维数组进行操作，因为它是一个数组的数组。你只有在调整元素数组中的单个元素时才需要注意。你通过遍历对所有元素进行类似的改变，尽管 JavaScript 中的二维数组并不严格。</p><p>如何在 JavaScript 中创建二维数组</p><p>创建一个多维数组有两种选择。你可以用数组字面量表示法手动创建数组，它使用方括号 <code>[]</code> 来包裹一个用逗号分隔的元素列表。你也可以使用一个嵌套的循环。</p><h3 id="--5">如何使用数组字面量表示法创建一个二维数组</h3><p>这就像我们一直在考虑的例子一样，使用下面的语法：</p><pre><code class="language-js">let arrayName = [
[ elements ],
[ elements ],
[ elements ], ... ];
</code></pre><p>例如，下面是一个二维数组，保存了每个学生的数学分数和等级的信息：</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];
</code></pre><h3 id="-for-">如何使用嵌套 for 循环创建一个二维数组</h3><p>有很多方法可以做到这一点。但一般来说，你要创建一个嵌套循环，第一个循环将遍历行，而第二个循环将遍历内部数组（列）中的元素：</p><pre><code class="language-js">let arr = [];
let rows = 4;
let columns = 3;

// creating two-dimensional array
for (let i = 0; i &lt; rows; i++) {
  arr[i] = [];
  for (let j = 0; j &lt; columns; j++) {
    arr[i][j] = j;
  }
}

console.log(arr);
</code></pre><p>在这个例子中，<code>j</code> 的索引被使用并将输出：</p><pre><code class="language-js">[
  [0, 1, 2],
  [0, 1, 2],
  [0, 1, 2],
  [0, 1, 2]
]
</code></pre><p>你可以决定创建一个数字，初始化为 0，然后随着遍历增加，这样你就不会对所有元素都有相同的数字：</p><pre><code class="language-js">let arr = [];
let rows = 4;
let columns = 3;

let value = 0;
// creating two-dimensional array
for (let i = 0; i &lt; rows; i++) {
  arr[i] = [];
  for (let j = 0; j &lt; columns; j++) {
    arr[i][j] = value++;
  }
}

console.log(arr);
</code></pre><p>这将返回：</p><pre><code class="language-js">[
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],
  [9, 10, 11]
]
</code></pre><p>你也可以决定创建一个接受这些值作为参数的函数：</p><pre><code class="language-js">const create2Darr = (rows, columns) =&gt; {
  let arr = [];
  let value = 0;

  // creating two-dimensional array
  for (let i = 0; i &lt; rows; i++) {
    arr[i] = [];
    for (let j = 0; j &lt; columns; j++) {
      arr[i][j] = value++;
    }
  }
  console.log(arr);
};

let rows = 4;
let columns = 3;
create2Darr(rows, columns);
</code></pre><h2 id="-javascript--3">如何在 JavaScript 中更新二维数组中的元素</h2><p>这与你处理一维数组的方式非常相似，你可以通过使用索引分配另一个值，来更新一个数组的值。</p><pre><code class="language-js">let array = [1, 2, 3];
array[1] = 5;

console.log(array); // returns [1, 5, 3]
</code></pre><p>你可以用同样的方法来改变整个行，甚至单个元素：</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore[1] = ["Alice George", 28, 62, "A"];
console.log(MathScore);
</code></pre><p>这将用这个新的数组替换索引 <code>1</code> 的值：</p><pre><code class="language-js">[
  ["John Doe", 20, 60, "A"],
  ["Alice George", 28, 62, "A"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
]
</code></pre><p>你可以通过跟踪行和列的索引并更新它们的值来更新单个元素：</p><pre><code class="language-js">let MathScore = [
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Petr Chess", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
];

MathScore[2][0] = "Jack Jim";
console.log(MathScore);
</code></pre><p>这将把索引为 <code>2</code> 的那一行的名字改为 “Jack Jim”，如下图所示：</p><pre><code class="language-js">[
  ["John Doe", 20, 60, "A"],
  ["Jane Doe", 10, 52, "B"],
  ["Jack Jim", 5, 24, "F"],
  ["Ling Jess", 28, 43, "A"],
  ["Ben Liard", 16, 51, "B"]
]
</code></pre><h2 id="--6"><strong>总结</strong></h2><p>在这篇文章中，你了解了什么是二维数组，以及它们在 JavaScript 中是如何运行的。</p><p>重要的是要知道，二维数组在 JavaScript 中的运行方式与一维数组非常相似，所以请放心使用 JavaScript 方法来调整和操作它们。</p><p>祝你编码愉快！</p><p>你可以<a href="https://joelolawanle.com/contents">访问我的网站</a>以阅读我的 150 多篇文章。你也可以使用搜索栏来查看我是否写过特定的文章。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 全栈工程师职业发展指南 ]]>
                </title>
                <description>
                    <![CDATA[ 全栈工程师在过去十年中越来越受欢迎，是科技就业市场上最受欢迎的职位之一。 但究竟什么是全栈工程师？他们每天都在做什么？你如何才能成为一名全栈工程师呢？ 在这篇文章中，我将介绍全栈的定义，并涵盖全栈工程师所从事的一些工作。我还会提到一些你成为一名全栈工程师需要学习的技能。 什么是全栈工程师 现代 web 应用由两层组成：前端和后端。 前端，也被称为客户端，由内容、内容的展示和布局以及互动性元素组成。它包括用户在屏幕上看到的并可以与之互动的所有可见部分。 后端，也被称为服务器端，包括一个运行代码的服务器，它有必要的逻辑来接收、处理以及加工请求，还有一个用于安全地存储用户数据的数据库。它包括用户不直接知道的所有幕后过程。 栈是技术的集合，指的是软件、工具、编程语言、框架和数据存储技术的组合，它们共同构建和运行 web 应用。 有许多技术栈。最流行的 JavaScript 语言的栈之一是 MERN，它代表 MongoDB、Express、React 和 NodeJs。 MongoDB 是一个文档数据库，Express 是 Node.js 的后端 web 应用框架，React 是 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/full-stack-engineer-career-guide/</link>
                <guid isPermaLink="false">64701b7a30ac2607039ca7df</guid>
                
                    <category>
                        <![CDATA[ 全栈 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Tue, 08 Aug 2023 07:05:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/05/nubelson-fernandes-UcYBL5V0xWQ-unsplash.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/full-stack-engineer-career-guide/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Full Stack Engineer –&nbsp;Career&nbsp;Guide</a>
      </p><p>全栈工程师在过去十年中越来越受欢迎，是科技就业市场上最受欢迎的职位之一。</p><p>但究竟什么是全栈工程师？他们每天都在做什么？你如何才能成为一名全栈工程师呢？</p><p>在这篇文章中，我将介绍全栈的定义，并涵盖全栈工程师所从事的一些工作。我还会提到一些你成为一名全栈工程师需要学习的技能。</p><h2 id="-">什么是全栈工程师</h2><p>现代 web 应用由两层组成：前端和后端。</p><p>前端，也被称为客户端，由内容、内容的展示和布局以及互动性元素组成。它包括用户在屏幕上看到的并可以与之互动的所有可见部分。</p><p>后端，也被称为服务器端，包括一个运行代码的服务器，它有必要的逻辑来接收、处理以及加工请求，还有一个用于安全地存储用户数据的数据库。它包括用户不直接知道的所有幕后过程。</p><p>栈是技术的集合，指的是软件、工具、编程语言、框架和数据存储技术的组合，它们共同构建和运行 web 应用。</p><p>有许多技术栈。最流行的 JavaScript 语言的栈之一是 MERN，它代表 MongoDB、Express、React 和 NodeJs。</p><p>MongoDB 是一个文档数据库，Express 是 Node.js 的后端 web 应用框架，React 是一个前端 JavaScript 库，NodeJS 是一个后端 JavaScript 运行环境。</p><p>全栈工程师是指知道如何从头到尾构建一个 web 应用的工程师，包括前端部分、后端部分和它所处的基础设施。他们处理整个技术栈，了解其中的每一部分。</p><h3 id="--1">全栈工程师的任务和职责</h3><p>全栈工程师致力于解决一系列广泛的问题，通常对项目有端到端的所有权——从概念化到部署。</p><p>一些日常职责可能包括：</p><ul><li>通过与客户和利益相关者的沟通来收集项目需求，了解软件产品的愿景。</li><li>集思广益，与设计团队合作，在将设计原型编码成产品之前审查设计原型。</li><li>解决应用程序面向客户端的 HTML、CSS 和 JavaScript 问题，并处理应用程序内使用的前端框架。</li><li>确保 web 应用对终端用户的响应，应用能在大多数设备上运行。</li><li>遵循可访问性的最佳实践。</li><li>管理和维护数据库和服务器，以确保客户端高效和优化地工作。</li><li>确保应用程序的安全性、维护、性能、正常运行时间和可扩展性。</li><li>监控软件并编写测试，以确保代码按预期运行。寻找并修复代码中的错误，以优化软件。</li><li>编写干净的、设计良好的、高效的代码，遵守行业最佳实践。</li><li>跟进新的技术进展，以改善业务需求。</li><li>创建一个最小可行产品，向利益相关者展示并与决策者沟通。</li><li>在收集反馈意见后实施新功能。</li><li>审查其他工程师编写的代码，并提供建设性的反馈。</li><li>阅读和编写概述软件开发过程的文档。</li></ul><p>任务和责任将取决于公司的规模。</p><p>例如，一个小公司可能只有一个处理整个应用程序的全栈开发人员。一个较大的公司可能有前端和后端开发人员，他们在自己的领域和专业知识范围内从事具体的工作。</p><h3 id="--2">全栈工程师的平均薪资是多少</h3><p>软件工程师，包括全栈工程师，一般都有不错的薪水。</p><p>根据<a href="https://www.bls.gov/ooh/computer-and-information-technology/software-developers.htm">美国劳工统计局</a>的数据，软件工程师的年薪中位数为 109,020 美元。</p><p>具体到全栈工程师，<a href="https://www.glassdoor.com/Salaries/full-stack-engineer-salary-SRCH_KO0,19.htm">Glassdoor</a> 列出的平均工资约为每年 120,300 美元。</p><p><a href="https://www.indeed.com/career/full-stack-developer/salaries">Indeed</a> 列出的平均工资为 120,749 美元。而 <a href="https://survey.stackoverflow.co/2022/#salary-united-states">Stack Overflow 的开发者调查</a>显示，全栈开发者的平均工资为 14 万美元。</p><p>请注意，这些数据是面向在美国的全栈工程师统计的。报酬是多少将取决于你的位置和经验年限。</p><p>搜索一下你所在地区与你有相同经验的软件工程师的平均工资，以帮助你更好地了解。</p><h2 id="--3">如何成为一名全栈工程师——全栈工程师所需的技能</h2><p>在下面的章节中，我将介绍成为全栈工程师所需学习的一些技术。</p><p>成为全栈工程师需要时间，你不可能成为所有工具和技术的专家——更多的是要对技术有全面的了解，并充分理解使用代码解决问题。</p><h3 id="-web-">学习互联网和 web 基础知识</h3><p>作为一名全栈工程师，了解互联网的工作原理并熟悉一些网络术语，如 DNS 和 IP 地址，会让你受益匪浅。</p><p>要了解更多关于 DNS、IP 地址以及互联网如何运行的信息，请查看以下资源：</p><ul><li><a href="https://www.freecodecamp.org/news/how-the-web-works-a-primer-for-newcomers-to-web-development-or-anyone-really-b4584e63585c/">网络是如何运作的：web 开发新手入门手册</a></li><li><a href="https://www.freecodecamp.org/news/how-the-web-works-part-ii-client-server-model-the-structure-of-a-web-application-735b4b6d76e3/">网络是如何工作第二部分：客户端-服务器模型和 web 应用程序的结构</a></li><li><a href="https://www.freecodecamp.org/news/what-is-dns/">什么是 DNS？域名系统、DNS 服务器和 IP 地址的概念解释</a></li></ul><p>你还需要知道 HTTP（超文本传输协议），这是万维网的基础，因为它管理着客户（如 web 浏览器）和服务器之间的通信。</p><p>具体而言，你需要了解 HTTP 请求方法（如 GET、POST、PUT、PATCH 和 DELETE）和 HTTP 响应代码（如 200、404 和 500）。</p><p>要了解更多关于 HTTP 的信息，请查看以下资源：</p><ul><li><a href="https://www.freecodecamp.org/news/what-is-http/">什么是 HTTP？入门协议概述</a></li><li><a href="https://www.freecodecamp.org/news/how-the-internet-works/">HTTP 是如何工作的，为什么它很重要</a></li><li><a href="https://www.freecodecamp.org/news/http-networking-protocol-course/">掌握 HTTP 网络协议</a></li><li><a href="https://www.freecodecamp.org/chinese/news/http-request-methods-explained/">HTTP 请求方法——用代码实例解释 Get、Put 和 Post</a></li></ul><h3 id="-web--1">学习前端 web 开发基础知识</h3><p>只有三种前端 web 语言在所有现代网络浏览器中运行： HTML、CSS 和 JavaScript。</p><p>HTML（超文本标记语言的简称）定义了网页的结构和内容，如文本、链接、表单、图像、视频等。</p><p>要学习 HTML，请查看<a href="https://www.freecodecamp.org/news/learn-html-beginners-course/">这个课程</a>。</p><p>CSS（层叠样式表的简称）对 HTML 内容设计样式，并使其具有美感——它决定了网页的外观和感觉。</p><p>CSS 负责确定页面上元素的大小、显示、布局和呈现。CSS 还负责使网页在所有屏幕尺寸上都能使用。</p><p>要了解更多关于 CSS 的信息，请查看<a href="https://www.freecodecamp.org/news/learn-css-in-this-free-6-hour-video-course/">这个课程</a>，其中包括 Flexbox 和 Grid——两个重要的 CSS 主题。当你了解了基础知识，你可以学习一个 CSS 框架，如 <a href="https://www.freecodecamp.org/news/learn-tailwind-css/">Tailwind CSS</a>。</p><p>JavaScript 是一种动态脚本编程语言，用于在浏览器中运行。</p><p>它是唯一可以用于前端 web 开发的编程语言，是 web 开发的重要组成部分。它与 HTML 和 CSS 一起被用来创建交互式网页。</p><p>要了解更多关于 JavaScript 的信息，请查看<a href="https://www.freecodecamp.org/news/full-javascript-course-for-beginners/">这个课程</a>。</p><h3 id="-git-github">学习 Git 和 GitHub</h3><p>Git 和 GitHub 是开发工作流程的核心部分，是每个软件开发工作中使用的工具。</p><p>Git 是一个分布式的版本控制系统，它提供了一种方法来对你的项目进行修改、备份这些修改、跟踪它们，甚至在需要时回溯到它们。它还可以让你在同一时间与其他团队成员协作。</p><p>GitHub 是一个在线托管服务，让你更容易使用 Git，是你和你的团队上传和审查代码的地方。</p><p>要了解更多关于 Git 和 GitHub 的信息，请查看<a href="https://www.freecodecamp.org/news/git-and-github-crash-course/">这个课程</a>。</p><h3 id="--4">学习一个前端库和框架</h3><p>当你理解了 JavaScript 的核心概念，你就可以继续学习 JavaScript 的前端库和框架之一。</p><p>前端库是预先写好的可重复使用的代码，包含各种函数、方法或对象，你可以在你的 JavaScript 项目中使用，以执行任务和实现特定功能。而 web 框架是一种工具，可以使创建 web 应用更容易、更快速。</p><p>根据 <a href="https://survey.stackoverflow.co/2022/#technology-most-popular-technologies">2022 年 Stack Overflow 调查</a>，最受欢迎和最常用的 JavaScript 库是 ReactJS。</p><p>要了解更多关于 ReactJS 的信息，请查看<a href="https://www.freecodecamp.org/news/free-react-course-2022/">这个课程</a>。</p><p>还有其他框架你可以使用和考虑学习，如 <a href="https://www.freecodecamp.org/news/vue-3-full-course/">Vue</a>、<a href="https://www.freecodecamp.org/news/learn-angular-full-course/">Angular</a> 和 <a href="https://www.freecodecamp.org/news/learn-svelte-complete-course/">Svelte</a>。每个框架都有其组织和编写代码的方式，以及其自身的优势和局限性。</p><p>在学习这些工具之前，请确保你有一个坚实的 JavaScript 基础。</p><h3 id="-web--2">学习后端 web 开发</h3><p>作为全栈 web 开发人员，你需要了解前端技术和后端工具，所以你还需要能够使用服务器端的脚本编程语言。</p><p>有很多可供选择的语言，如 Python、Ruby 和 Java，仅举几例。</p><p>尽管 JavaScript 被广泛用于前端开发，近年来，因为 NodeJS，它也被用于后端网络开发。</p><p>NodeJS 是一个提供后端功能的 JavaScript 运行时，旨在建立动态可扩展的 web 应用。</p><p>首先，<a href="https://www.freecodecamp.org/chinese/news/what-is-npm-a-node-package-manager-tutorial-for-beginners/">学习如何使用 NPM</a>，这是一个 Node 包管理器，用于安装和管理 JavaScript 包的本地依赖。它是专门为与 NodeJS 一起使用而设计的。</p><p>要学习 NodeJS 的基础知识，请查看<a href="https://www.freecodecamp.org/news/nodejs-course/">这个课程</a>。</p><p>你可以将 NodeJS 与 ExpressJS 服务器端 web 框架结合，创建全栈 web 应用。要学习使用 NodeJS 和 ExpressJS 的后端开发，请查看<a href="https://www.freecodecamp.org/news/free-8-hour-node-express-course/">这个课程</a>。</p><h3 id="-sql">学习数据库管理系统和 SQL</h3><p>作为一名全栈工程师，你将与数据库打交道，因为你所开发的大多数 web 应用都有一个数据库。你将花很多时间编写数据库查询，以获取你需要的数据。</p><p>数据库是一个存储容器——一个存储项目中所有数据的地方，如用户数据。</p><p>有两种主要的数据库类型：</p><ul><li>SQL 或关系型数据库，也被称为 SQL 数据库，它以结构化的、有组织的、表格的形式存储数据。</li><li>非关系型或 NoSQL 数据库，它们不以表格形式存储数据。</li></ul><p>一个数据库有一个叫作数据库管理系统（DBMS）的程序，它作为数据库之间的接口，允许用户或程序来检索、更新和管理数据。</p><p>要了解关系型数据库，请查看 <a href="https://www.freecodecamp.org/chinese/learn/relational-database">freeCodeCamp 的关系数据库课程</a>。</p><p>为了与关系型数据库通信并操作存储的数据，你要使用查询语言，如 SQL（结构化查询语言的简称）来查询它们。要了解更多关于 SQL 的信息，请查看<a href="https://www.freecodecamp.org/news/learn-sql-in-10-minutes/">这个资源</a>。</p><p>如果你想开始使用 NoSQL 数据库管理系统，MongoDB 是一个不错的开始。要开始使用 MongoDB，请查看<a href="https://learn.mongodb.com/">这些课程</a>。</p><h2 id="--5">总结</h2><p>希望这篇文章能帮助你更好地了解全栈工程师的工作。</p><p>在这篇文章中，我们介绍了全栈工程的定义，并涵盖了全栈工程师所从事的一些工作。</p><p>我们还介绍了一些你成为全栈工程师需要知道的工具和语言。</p><p>感谢你阅读本文！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何在 JavaScript 中验证 URL ]]>
                </title>
                <description>
                    <![CDATA[ 统一资源定位符 URL（Uniform Resource Locator）引导你进入互联网上的一个页面或文件。URL 是互联网上事物的地址。 所有有效的 URL 都遵循某些模式。因此，如果你知道这些模式，你就可以在你的程序中确定一个 URL 是否有效，并给出反馈、抛出一个错误，等等。 在本教程中，你将学习三种方法来检查 JavaScript 中的一个字符串是否是有效的 URL。  * 如何使用 URL 构造器来验证 URL  * 如何使用 npm 包来验证 URL  * 如何使用 Regex 来验证 URL 如何使用 URL 构造函数来验证 URL 当你传递一个字符串给 URL 构造函数时，如果字符串是一个有效的 URL，它将返回一个新的 URL 对象。否则，它将返回一个错误。 const fccUrl = new URL("https://www.freecodecamp.org/"); console.log(fccUrl); 以下是你在控制台记录 fccUrl ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-validate-urls-in-javascript/</link>
                <guid isPermaLink="false">6388695d832e3f07817639ba</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 02 Aug 2023 04:29:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/12/web-design-g84d0f5bf9_1920.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-validate-urls-in-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Validate URLs in JavaScript</a>
      </p><p>统一资源定位符 URL（Uniform Resource Locator）引导你进入互联网上的一个页面或文件。URL 是互联网上事物的地址。</p><p>所有有效的 URL 都遵循某些模式。因此，如果你知道这些模式，你就可以在你的程序中确定一个 URL 是否有效，并给出反馈、抛出一个错误，等等。</p><p>在本教程中，你将学习三种方法来检查 JavaScript 中的一个字符串是否是有效的 URL。</p><ul><li>如何使用 URL 构造器来验证 URL</li><li>如何使用 npm 包来验证 URL</li><li>如何使用 Regex 来验证 URL</li></ul><h2 id="-url-url">如何使用 URL 构造函数来验证 URL</h2><p>当你传递一个字符串给 <code>URL</code> 构造函数时，如果字符串是一个有效的 URL，它将返回一个新的 <code>URL</code> 对象。否则，它将返回一个错误。</p><pre><code class="language-javascript">const fccUrl = new URL("https://www.freecodecamp.org/");
console.log(fccUrl);</code></pre><p>以下是你在控制台记录 <code>fccUrl</code> 时得到的信息：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2022/12/image.png" class="kg-image" alt="image" width="558" height="241" loading="lazy"><figcaption>JavaScript 中的 URL 对象</figcaption></figure><p>这个对象意味着你传递给 <code>URL</code> 构造函数的字符串是一个有效的 URL。</p><p>现在让我们看看当你传递一个无效的 URL 字符串时，你会得到什么：</p><pre><code class="language-javascript">const fccUrl = new URL('freecodecamp');
console.log(fccUrl);
</code></pre><p>字符串 <code>'freecodecamp'</code> 不是一个有效的 URL。因此，你将得到错误 <code>TypeError</code>：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/11/invalidURL.PNG" class="kg-image" alt="A TypeError after passing an invalid URL to the URL constructor" width="600" height="400" loading="lazy"><figcaption>无效的 URL</figcaption></figure><p>回顾一下：</p><ol><li>当你把一个有效的 URL 字符串传递给 <code>URL</code> 构造函数时，它返回一个新的 <code>URL</code> 对象。</li><li>当你向 <code>URL</code> 构造函数传递一个无效的 URL 字符串时，它会返回一个 <code>TypeError</code>。</li></ol><p>有了这些知识，你可以创建一个自定义函数来检查一个给定的 URL 字符串的有效性。</p><h3 id="-url-url-">如何用 URL 构造函数创建一个 URL 验证器函数</h3><p>通过使用 <code>URL</code> 构造函数和 <code>try...catch</code> 语句，你可以创建一个自定义 <code>isValidUrl</code> 函数：</p><pre><code class="language-javascript">function isValidUrl(string) {
  try {
    new URL(string);
    return true;
  } catch (err) {
    return false;
  }
}
</code></pre><p>当你作为参数传递的字符串是一个有效的 URL 时，<code>isValidUrl</code> 函数返回 <code>true</code>。否则，它返回 <code>false</code>：</p><pre><code class="language-javascript">console.log(isValidUrl('https://www.freecodecamp.org/')); // true
console.log(isValidUrl('mailto://mail@freecodecamp.org')); // true
console.log(isValidUrl('freecodecamp')); // false
</code></pre><h3 id="-url-http-url">如何用 URL 构造器只验证 HTTP URL</h3><p>有时，你可能想检查这个字符串是否是一个有效的 HTTP URL，而拒绝其他有效的 URL，如 <code>'mailto://mail@freecodecamp.org'</code>。</p><p>如果你仔细看一下 <code>URL</code> 对象，它的一个属性是 <code>protocol</code>：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2022/12/image-1.png" class="kg-image" alt="image-1" width="558" height="241" loading="lazy"></figure><p>在上面的例子中，<code>protocol</code> 属性的值是 <code>'https:'</code>。</p><p>要检查一个字符串是否是一个有效的 HTTP URL，你可以使用 <code>URL</code> 对象的 <code>protocol</code> 属性：</p><pre><code class="language-javascript">function isValidHttpUrl(string) {
  try {
    const newUrl = new URL(string);
    return newUrl.protocol === 'http:' || newUrl.protocol === 'https:';
  } catch (err) {
    return false;
  }
}

console.log(isValidHttpUrl('https://www.freecodecamp.org/')); // true
console.log(isValidHttpUrl('mailto://mail@freecodecamp.org')); // false
console.log(isValidHttpUrl('freecodecamp')); // false
</code></pre><p>这里的区别是，在新的 <code>URL</code> 对象被创建后，你没有返回 <code>true</code>。相反，你要检查 <code>protocol</code> 属性的值是否等于 <code>"http: "</code> 或 <code>"https:"</code>，如果是则返回 <code>true</code>，如果不是则返回 <code>false</code>。</p><h2 id="-npm-url">如何使用 npm 包来验证 URL</h2><p>有两个 NPM 包供你使用：<code>is-url</code> 和 <code>is-url-http</code>。</p><p>这些包是检查一个字符串是否为有效 URL 的最简单方法。你所需要做的就是传入一个字符串作为参数，而它们将返回 <code>true</code> 或 <code>false</code>。</p><p>让我们看看这两个包是如何工作的：</p><h3 id="-is-url-url">如何用 is-url 包验证 URL</h3><p>你可以使用 <code>is-url</code> 包来检查一个字符串是否是一个有效的 URL。这个包并不检查传递给它的 URL 的协议。</p><p>要使用 <code>is-url</code>，首先使用下面的命令安装它：</p><pre><code class="language-javascript">npm install is-url</code></pre><p>然后导入它，并将你的 URL 字符串作为一个参数传给它：</p><pre><code class="language-javascript">import isUrl from 'is-url';

const firstCheck = isUrl('https://www.freecodecamp.org/');
const secondCheck = isUrl('mailto://mail@freecodecamp.org');
const thirdCheck = isUrl('freeCodeCamp');

console.log(firstCheck); // true
console.log(secondCheck); // true
console.log(thirdCheck); // false
</code></pre><p><code>is-url</code> 包对具有有效 URL 格式的字符串返回 <code>true</code>，对具有无效 <code>URL</code> 格式的字符串返回 <code>false</code>。</p><p>在这个例子中，<code>firstCheck</code>（使用 <code>https:</code> 协议）和 <code>secondCheck</code>（使用 <code>mailto:</code> 协议）都返回 <code>true</code>。</p><h3 id="-is-http-url-http-url">如何用 is-http-url 包来验证 HTTP URL</h3><p>你可以使用 <code>is-url-http</code> 包来检查一个字符串是否是一个有效的 HTTP URL。</p><p>用下面的命令安装这个包：</p><pre><code class="language-javascript">npm install is-url-http</code></pre><p>然后导入它，像这样把 URL 字符串传给它：</p><pre><code class="language-javascript">import isUrlHttp from 'is-url-http';

const firstCheck = isUrlHttp('https://www.freecodecamp.org/');
const secondCheck = isUrlHttp('mailto://freecodecamp@mail.org');
const thirdCheck = isUrlHttp('freeCodeCamp');

console.log(firstCheck); // true
console.log(secondCheck); // false
console.log(thirdCheck); // false
</code></pre><p>在这个例子中，只有 <code>firstCheck</code> 返回 <code>true</code>。<code>is-url-http</code> 包不仅检查字符串是否是一个有效的 URL，它还检查它是否是一个有效的 HTTP URL。这就是为什么它对 <code>secondCheck</code> 返回 <code>false</code>，因为它不是一个有效的 HTTP URL。</p><h2 id="-regex-url">如何使用 Regex 来验证 URL</h2><p>你也可以使用正则表达式来检查一个字符串是否是有效的 URL。</p><p>所有有效的 URL 都遵循一个特定的模式。它们有三个主要部分，分别是：</p><ul><li>协议</li><li>域名（或 IP 地址）</li><li>端口和路径</li></ul><p>有时路径后面是一个查询字符串或片段定位符。</p><p>你可以从这篇关于 URL 模式的 <a href="https://www.freecodecamp.org/chinese/news/what-happens-when-you-hit-url-in-your-browser/">freeCodeCamp 文章</a>中了解更多关于 URL 模式的信息。</p><p>知道了 URL 的模式，你可以使用正则来检查字符串中是否存在这样的模式。如果这些模式存在，那么这个字符串就通过了正则测试。否则，它就会失败。</p><p>另外，使用正则，你可以检查所有有效的 URL，或者只检查有效的 HTTP URL。</p><h3 id="-url">如何用正则验证 URL</h3><pre><code class="language-javascript">function isValidUrl(str) {
  const pattern = new RegExp(
    '^([a-zA-Z]+:\\/\\/)?' + // protocol
      '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
      '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR IP (v4) address
      '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
      '(\\?[;&amp;a-z\\d%_.~+=-]*)?' + // query string
      '(\\#[-a-z\\d_]*)?$', // fragment locator
    'i'
  );
  return pattern.test(str);
}

console.log(isValidUrl('https://www.freecodecamp.org/')); // true
console.log(isValidUrl('mailto://freecodecamp.org')); // true
console.log(isValidUrl('freeCodeCamp')); // false
</code></pre><p>上面 <code>isValidUrl</code> 函数中的正则检查一个字符串是否是一个有效的 URL。协议检查 <code>^([a-zA-Z]+:\\/\\/)?</code> 不仅仅限于 <code>https:</code>。</p><p>这就是为什么第二个例子中的 <code>mailto:</code> 协议会返回 <code>true</code>。</p><h3 id="-http-url">如何用正则验证 HTTP URL</h3><p>要使用正则来检查一个字符串是否是有效的 HTTP URL，你需要编辑协议检查。</p><p>你应该使用 <code>'^(https?:\\/\\/)?'</code>，而不是 <code>^([a-zA-Z]+:\\/\\/)?</code>：</p><pre><code class="language-javascript">function isValidHttpUrl(str) {
  const pattern = new RegExp(
    '^(https?:\\/\\/)?' + // protocol
      '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
      '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
      '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
      '(\\?[;&amp;a-z\\d%_.~+=-]*)?' + // query string
      '(\\#[-a-z\\d_]*)?$', // fragment locator
    'i'
  );
  return pattern.test(str);
}

console.log(isValidHttpUrl('https://www.freecodecamp.org/')); // true
console.log(isValidHttpUrl('mailto://freecodecamp.org')); // false
console.log(isValidHttpUrl('freeCodeCamp')); // false
</code></pre><p>现在只有第一个具有有效的 <code>https:</code> 协议的例子返回 <code>true</code>。请注意，带有 <code>http:</code> 的 URL 字符串也可以工作。</p><h2 id="-"><strong>总结</strong></h2><p>在这篇文章中，你学到了如何在 JavaScript 中检查 URL 的有效性。现在，你知道了以下三种方法，可以做到这一点。</p><ul><li>如何使用 <code>URL</code> 构造器来验证 URL 的有效性</li><li>如何使用 npm 包来验证 URL（<code>is-url</code> 和 <code>is-http-url</code>）</li><li>如何使用正则来验证 URL</li></ul><p>你可以选择自己喜欢的工作方法。</p><p>谢谢你阅读本文，happy coding！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React.useEffect Hook 常见问题及解决方法 ]]>
                </title>
                <description>
                    <![CDATA[ 大多数开发人员都非常熟悉 React hooks 的工作方式和常见用例，但是有一个 useEffect 问题很多人可能不太清楚。 用例 让我们从一个简单的场景开始。我们正在构建一个 React 应用程序，希望在一个组件中显示当前用户的用户名。但首先，我们需要从 API 中获取用户名。 因为我们知道我们也需要在应用程序的其他地方使用用户数据，所以我们还想在自定义 React hook 中抽象数据获取逻辑。 我们希望 React 组件看起来像这样： const Component = () => {   // useUser custom hook      return <div>{user.name}</div>; }; 看起来很简单！ useUser React hook 第二步是创建我们的 useUser 自定义钩子。 const useUser = (user) ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/most-common-react-useeffect-problems-and-how-to-fix-them/</link>
                <guid isPermaLink="false">616cd995d05b5a0660d4f0ff</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 19 Jul 2023 07:18:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/11/react-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/most-common-react-useeffect-problems-and-how-to-fix-them/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">React.useEffect Hook – Common Problems and How to Fix Them</a>
      </p><p>大多数开发人员都非常熟悉 React hooks 的工作方式和常见用例，但是有一个 <code>useEffect</code> 问题很多人可能不太清楚。</p><h2 id="-">用例</h2><p>让我们从一个简单的场景开始。我们正在构建一个 React 应用程序，希望在一个组件中显示当前用户的用户名。但首先，我们需要从 API 中获取用户名。</p><p>因为我们知道我们也需要在应用程序的其他地方使用用户数据，所以我们还想在自定义 React hook 中抽象数据获取逻辑。</p><p>我们希望 React 组件看起来像这样：</p><pre><code>const Component = () =&gt; {
  // useUser custom hook
  
  return &lt;div&gt;{user.name}&lt;/div&gt;;
};
</code></pre><p>看起来很简单！</p><h1 id="useuser-react-hook"><strong>useUser React hook</strong></h1><p>第二步是创建我们的 <code>useUser</code> 自定义钩子。</p><pre><code>const useUser = (user) =&gt; {
  const [userData, setUserData] = useState();
  useEffect(() =&gt; {
    if (user) {
      fetch("users.json").then((response) =&gt;
        response.json().then((users) =&gt; {
          return setUserData(users.find((item) =&gt; item.id === user.id));
        })
      );
    }
  }, []);

  return userData;
};
</code></pre><p>让我们分解一下。我们正在检查钩子是否正在接收用户对象。之后，我们从名为 <code>users.json</code> 的文件中获取用户列表，并对其进行过滤以找到具有我们需要的 id 的用户。</p><p>然后，一旦我们获得了必要的数据，我们就将其保存为钩子的 <code>userData</code> 状态。最后返回 <code>userData</code>。</p><blockquote><strong>注意：</strong>这是一个仅用于说明目的的示例！现实世界中的数据获取要复杂得多。如果你对该主题感兴趣，请查看我关于如何使用 ReactQuery、Typescript 和 GraphQL 创建出色的数据获取设置的<a href="https://blog.whereisthemouse.com/graphql-requests-made-easy-with-react-query-and-typescript">文章</a>。</blockquote><p>让我们在 React 组件中插入钩子，看看会发生什么。</p><pre><code>const Component = () =&gt; {
  const user = useUser({ id: 1 });
  return &lt;div&gt;{user?.name}&lt;/div&gt;;
};
</code></pre><p>好的，一切都按预期进行。但是等等……这是什么？</p><h1 id="eslint-exhaustive-deps-"><strong>ESLint exhaustive-deps 规则</strong></h1><p>我们的钩子中有一个 ESLint 警告：</p><pre><code>React Hook useEffect has a missing dependency: 'user'. Either include it or remove the dependency array. (react-hooks/exhaustive-deps)
</code></pre><p>嗯，我们的 <code>useEffect</code> 似乎缺少依赖项。那好吧！ 让我们添加它。可能发生的最坏情况是什么？ 😂</p><pre><code>const useUser = (user) =&gt; {
  const [userData, setUserData] = useState();
  useEffect(() =&gt; {
    if (user) {
      fetch("users.json").then((response) =&gt;
        response.json().then((users) =&gt; {
          return setUserData(users.find((item) =&gt; item.id === user.id));
        })
      );
    }
  }, [user]);

  return userData;
};
</code></pre><p>看起来我们的组件 <code>Component</code> 现在不会停止重新渲染。这里发生了什么？！</p><p>让我们解释一下。</p><h1 id="--1">无限重新渲染问题</h1><p>我们的组件重新渲染的原因是因为 <code>useEffect</code> 依赖项在不断变化。但为什么？我们总是将相同的对象传递给钩子！</p><p>虽然我们确实传递了一个具有相同键和值的对象，但它并不是完全相同的对象。每次重新渲染组件时，我们实际上都是在创建一个新对象，然后我们将新对象作为参数传递给 <code>useUser</code> 钩子。</p><p>在内部，<code>useEffect</code> 比较两个对象，由于它们有不同的引用，它再次获取用户并将新的用户对象设置为状态。状态更新然后触发组件中的重新渲染，不断重复……</p><p>所以，我们能做些什么？</p><h1 id="--2">如何修复</h1><p>现在我们了解了问题，可以开始寻找解决方案。</p><p>第一个也是最明显的选择是从 <code>useEffect</code> 依赖数组中移除依赖，忽略 ESLint 规则，继续我们的生活。</p><p>但这是错误的做法。它可以（并且可能会）导致我们的应用程序中出现错误和意外行为。如果你想了解更多有关 <code>useEffect</code> 如何工作的信息，我强烈推荐 Dan Abramov 的<a href="https://overreacted.io/a-complete-guide-to-useeffect/">完整指南</a>。</p><p>下一个是什么？</p><p>在我们的例子中，最简单的解决方案是从组件中取出 <code>{ id: 1 }</code> 对象。这将为对象提供稳定的引用并解决我们的问题。</p><pre><code>const userObject = { id: 1 };

const Component = () =&gt; {
  const user = useUser(userObject);
  return &lt;div&gt;{user?.name}&lt;/div&gt;;
};

export default Component;
</code></pre><p>但这并不总是可能的。想象一下，用户 id 以某种方式依赖于组件 props 或状态。</p><p>例如，可能是我们使用 URL 参数来访问它。如果是这种情况，我们可以使用一个方便的 <code>useMemo</code> 钩子来记忆对象并再次确保稳定的引用。</p><pre><code>const Component = () =&gt; {
  const { userId } = useParams();
  
  const userObject = useMemo(() =&gt; {
    return { id: userId };
  }, [userId]); // Don't forget the dependencies here either!

  const user = useUser(userObject);
  return &lt;div&gt;{user?.name}&lt;/div&gt;;
};

export default Component;
</code></pre><p>最后，不是将对象变量传递给我们的 <code>useUser</code> 钩子，而是可以只传递用户 ID 本身，这是一个原始值。这将防止 <code>useEffect</code> 钩子中的引用相等问题。</p><pre><code>const useUser = (userId) =&gt; {
  const [userData, setUserData] = useState();

  useEffect(() =&gt; {
    fetch("users.json").then((response) =&gt;
      response.json().then((users) =&gt; {
        return setUserData(users.find((item) =&gt; item.id === userId));
      })
    );
  }, [userId]);

  return userData;
};

const Component = () =&gt; {
  const user = useUser(1);

  return &lt;div&gt;{user?.name}&lt;/div&gt;;
};
</code></pre><p>问题解决啦！</p><p>在此过程中，我们甚至不必违反任何 ESLint 规则......</p><p><em><strong>注意：</strong>如果我们传递给自定义钩子的参数是一个函数，而不是一个对象，我们将使用非常相似的技术来避免无限重新渲染。一个显着的区别是我们必须在上面的例子中用 <code>useCallback</code> 替换 <code>useMemo</code>。</em></p><p>感谢你阅读本文！</p><p>对代码感到好奇吗？在<a href="https://codesandbox.io/s/useeffect-gotcha-20jw9?file=/src/App.js">这里</a>探索吧。</p><p>欢迎访问我的<a href="https://blog.whereisthemouse.com/">博客</a>并在 <a href="https://twitter.com/iva_kop">Twitter</a> 上关注我，以获取更多与 React 相关的内容。</p><p>图片作者：<a href="https://www.freepik.com/vectors/technology">vectorjuice</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何在 JavaScript 数组中插入元素 ]]>
                </title>
                <description>
                    <![CDATA[ 数组数据类型是处理有序值列表时最常用的数据类型之一。 每个值都被称为具有唯一 id 的元素。它存储可以通过单个变量访问的各种数据类型的元素。 在实践中，一个数组可以包含一个用户列表，我们可能需要在最后一个元素之后、第一个元素之前或数组中的任何指定点添加一个元素到数组中。 本文将向你展示如何使用 JavaScript 将元素插入到数组中。如果你赶时间，以下是我们将在本文中深入讨论的方法： // 添加到数组的开头 Array.unshift(element); // 添加到数组的末尾 Array.push(element); // 添加到指定位置 Array.splice(start_position, 0, new_element...); // 使用 concat 方法添加而不改变原始数组 let newArray = [].concat(Array, element);  * 当你想在数组末尾添加一个元素时，请使用 push()。  * 如果你需要在数组的开头添加一个元素，请使用 unshift()。  * 如果要将元素添加到数组的特定位置，请使用 splice() ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-insert-an-element-into-an-array-in-javascript/</link>
                <guid isPermaLink="false">63285db45ef0a407fd630099</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 数组 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Mon, 10 Jul 2023 04:16:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/09/pawel-czerwinski-dYjFmiQb_aE-unsplash.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-insert-an-element-into-an-array-in-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Push into an Array in JavaScript – How to Insert an Element into an Array in JS</a>
      </p><p>数组数据类型是处理有序值列表时最常用的数据类型之一。</p><p>每个值都被称为具有唯一 <code>id</code> 的元素。它存储可以通过单个变量访问的各种数据类型的元素。</p><p>在实践中，一个数组可以包含一个用户列表，我们可能需要在最后一个元素之后、第一个元素之前或数组中的任何指定点添加一个元素到数组中。</p><p>本文将向你展示如何使用 JavaScript 将元素插入到数组中。如果你赶时间，以下是我们将在本文中深入讨论的方法：</p><pre><code>// 添加到数组的开头
Array.unshift(element);

// 添加到数组的末尾
Array.push(element);

// 添加到指定位置
Array.splice(start_position, 0, new_element...);

// 使用 concat 方法添加而不改变原始数组
let newArray = [].concat(Array, element);
</code></pre><ul><li>当你想在数组末尾添加一个元素时，请使用 <code>push()</code>。</li><li>如果你需要在数组的开头添加一个元素，请使用 <code>unshift()</code>。</li><li>如果要将元素添加到数组的特定位置，请使用 <code>splice()</code>。</li><li>最后，当你想保持原始数组时，可以使用 <code>concat()</code> 方法。</li></ul><h2 id="-unshift-">如何使用 unshift() 方法添加到数组的开头</h2><p>在 JavaScript 中，你可以使用 <code>unshift()</code> 方法将一个或多个元素添加到数组的开头，并在添加新元素后返回数组的长度。</p><p>如果我们有一个国家数组，并且想在当前第一个索引 <code>0</code> 处的 “Nigeria” 之前添加一个国家，我们可以使用 <code>unshift()</code> 方法来完成，如下所示：</p><pre><code>const countries = ["Nigeria", "Ghana", "Rwanda"];

countries.unshift("Kenya");

console.log(countries); // ["Kenya","Nigeria","Ghana","Rwanda"]
</code></pre><p>我们还可以使用 <code>unshift()</code> 方法添加多个元素：</p><pre><code>const countries = ["Nigeria", "Ghana", "Rwanda"];

countries.unshift("South Africa", "Mali", "Kenya");

console.log(countries); // ["South Africa","Mali","Kenya","Nigeria","Ghana","Rwanda"]
</code></pre><p>在我们对 <code>unshift()</code> 方法的解释中，我们还声明它返回新数组的长度，如下所示：</p><pre><code>const countries = ["Nigeria", "Ghana", "Rwanda"];

let countriesLength = countries.unshift("South Africa", "Mali", "Kenya");

console.log(countriesLength); // 6
</code></pre><h2 id="-push-">如何使用 push() 方法推送到数组的末尾</h2><p><code>push()</code> 方法类似于 <code>unshift()</code> 方法，因为它将元素添加到数组的末尾而不是开头。它返回新数组的长度，并且与 <code>unshift()</code> 方法一样，可用于添加多个元素。</p><p>让我们再次尝试相同的示例，但这次使用 <code>push()</code> 方法将它们添加到数组的末尾：</p><pre><code>const countries = ["Nigeria", "Ghana", "Rwanda"];

countries.push("Kenya");

console.log(countries); // ["Nigeria","Ghana","Rwanda","Kenya"]

countries.push("South Africa", "Mali");

console.log(countries); // ["Nigeria","Ghana","Rwanda","Kenya","South Africa","Mali"]
</code></pre><p>我们可以用它来获取新数组的长度：</p><pre><code>const countries = ["Nigeria", "Ghana", "Rwanda"];

let countriesLength = countries.push("Kenya");

console.log(countriesLength); // 4
</code></pre><h2 id="-splice-">如何使用 splice() 方法将元素添加到数组中的指定位置</h2><p>到目前为止，我们只看到了如何将元素添加到数组的开头或结尾，但是你可能想知道如何将元素添加到数组中的特定位置。好吧，你可以使用 <code>splice()</code> 方法来做到这一点。</p><p><code>splice()</code> 方法是一种通用方法，用于通过在数组的指定位置删除、替换或添加元素来更改数组的内容。本节将介绍如何使用此方法将元素添加到特定位置。</p><p>例如，考虑以下国家数组，其中包含按字母顺序排列的三个元素（国家）：</p><pre><code>const countries = ["Ghana", "Nigeria", "Rwanda"];
</code></pre><p>假设我们要添加 “Kenya”，按照字母顺序，它应该放在第二个位置，索引 <code>1</code>（在 Ghana 之后和 Nigeria 之前）。在这种情况下，我们将使用 <code>splice()</code> 方法，语法如下：</p><pre><code>Array.splice(start_position, 0, new_element...);
</code></pre><ul><li><code>start_position</code> 指定了我们希望将新元素插入到数组中的位置的索引。如果有多个元素，它指定插入的元素将从哪里开始。</li><li>如果我们想添加元素到数组中，我们将第二个参数设置为零（<code>0</code>），指示 <code>splice()</code> 方法不要删除任何数组元素。</li><li>以下参数或元素可能不止一个，因为这些是我们要在指定位置添加到数组中的元素。</li></ul><p>例如，让我们在国家数组中将 “Kenya” 放在 “Ghana” 之后：</p><pre><code>const countries = ["Ghana", "Nigeria", "Rwanda"];

countries.splice(1, 0, 'Kenya');

console.log(countries); // ["Ghana","Kenya","Nigeria","Rwanda"]
</code></pre><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_8F843EE332F48B356BFA84EB69212DF653EB2859C5739E7748DB9362133DCFB7_1658069550677_illustration.jpg" class="kg-image" alt="s_8F843EE332F48B356BFA84EB69212DF653EB2859C5739E7748DB9362133DCFB7_1658069550677_illustration" width="600" height="400" loading="lazy"></figure><p>就像我们对其他方法所做的那样，我们也可以添加多个元素：</p><pre><code>const countries = ["Ghana", "Nigeria", "Rwanda"];

countries.splice(1, 0, 'Kenya', 'Mali');

console.log(countries); // ["Ghana","Kenya","Mali","Nigeria","Rwanda"]
</code></pre><p>请注意，前面的方法返回新数组的长度，但 <code>splice()</code> 方法更改了原始数组。它不会删除任何元素，因此它返回一个空数组。</p><h2 id="-concat-">如何使用 concat() 方法将元素添加到数组</h2><p>我们可以使用 <code>concat()</code> 方法将元素添加到数组中，而无需改变或更改原始数组。如果我们不希望原始数组受到影响，则创建一个新数组是一种更好的方法。</p><p>我们可以使用此方法根据放置元素的位置将元素添加到数组的开头和结尾：</p><pre><code>const countries = ["Ghana", "Nigeria", "Rwanda"];

let newCountries = [].concat("Mali", countries, "Kenya");

console.log(newCountries); // ["Mali","Ghana","Nigeria","Rwanda","Kenya"]
</code></pre><p><code>concat()</code> 方法还允许我们将两个（或更多）数组连接到一个新数组中：</p><pre><code>const africanCountries = ["Ghana", "Nigeria", "Rwanda"];
const europeanCountries = ["Germany", "France", "spain"];

let countries = [].concat(africanCountries, europeanCountries);

console.log(countries); // ["Ghana","Nigeria","Rwanda","Germany","France","spain"]
</code></pre><h2 id="-"><strong>总结</strong></h2><p>在本文中，我们学习了使用 <code>splice()</code> 方法将元素添加到数组的开始、末尾或任何位置的各种方法。</p><p>我们还了解到 <code>concat()</code> 方法允许我们在不改变原始数组的情况下添加元素。</p><p>你可以使用任何你需要的方法。</p><p>祝你编程快乐！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ REST API 最佳实践——REST 端点设计实例 ]]>
                </title>
                <description>
                    <![CDATA[ 在 Web 开发中，REST API 在确保客户端和服务器之间的顺利通信方面发挥了重要作用。 你可以把客户端看作是前端，把服务器看作是后端。 客户端（前端）和服务器（后端）之间的通信通常不是超级直接的。因此，我们使用一个叫作“应用编程接口”（或 API）的接口，作为客户端和服务器之间的中介。 因为 API 在这种客户端-服务器通信中起着至关重要的作用，所以我们在设计 API 时应该始终考虑到最佳实践。这有助于维护它们的开发人员和那些使用它们的人，在履行职责时不会遇到问题。 在这篇文章中，我将带你了解创建 REST API 时需要遵循的 9 个最佳实践。这将帮助你创建最好的 API，并使你的 API 用户使用起来更容易。 首先，什么是 REST API REST 是Representational State Transfer 的缩写。它是由 Roy Fielding 在 2000 年创造的一种软件架构风格，用于指导网络的架构设计。 任何遵循 REST 设计原则的 API（应用编程接口）都被称为 RESTful。 简单地说，REST API 是两台计算机通过 HTTP（超文 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/rest-api-best-practices-rest-endpoint-design-examples/</link>
                <guid isPermaLink="false">63e66333e673d23d288785f9</guid>
                
                    <category>
                        <![CDATA[ RESTful API ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Tue, 04 Jul 2023 03:30:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/02/api.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/rest-api-best-practices-rest-endpoint-design-examples/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">REST API Best Practices – REST Endpoint Design Examples</a>
      </p><p>在 Web 开发中，REST API 在确保客户端和服务器之间的顺利通信方面发挥了重要作用。</p><p>你可以把客户端看作是前端，把服务器看作是后端。</p><p>客户端（前端）和服务器（后端）之间的通信通常不是超级直接的。因此，我们使用一个叫作“应用编程接口”（或 API）的接口，作为客户端和服务器之间的中介。</p><p>因为 API 在这种客户端-服务器通信中起着至关重要的作用，所以我们在设计 API 时应该始终考虑到最佳实践。这有助于维护它们的开发人员和那些使用它们的人，在履行职责时不会遇到问题。</p><p>在这篇文章中，我将带你了解创建 REST API 时需要遵循的 9 个最佳实践。这将帮助你创建最好的 API，并使你的 API 用户使用起来更容易。</p><h2 id="-rest-api">首先，什么是 REST API</h2><p>REST 是Representational State Transfer 的缩写。它是由 Roy Fielding 在 2000 年创造的一种软件架构风格，用于指导网络的架构设计。</p><p>任何遵循 REST 设计原则的 API（应用编程接口）都被称为 RESTful。</p><p>简单地说，REST API 是两台计算机通过 HTTP（超文本传输协议）进行通信的媒介，与客户端和服务器的通信方式相同。</p><h2 id="rest-api-">REST API 设计最佳实践</h2><h3 id="-json-">使用 JSON 作为发送和接收数据的格式</h3><p>在过去，接受和响应 API 请求主要是通过 XML 甚至 HTML 完成的。但如今，JSON（JavaScript Object Notation）已经在很大程度上成为发送和接收 API 数据的事实格式。</p><p>这是因为，以 XML 为例，对数据进行解码和编码往往有点麻烦——所以 XML 不再受到框架的广泛支持。</p><p>例如，JavaScript 有一个内置的方法来通过 fetch API 解析 JSON 数据，因为 JSON 主要是为它而生成的。但是如果你使用任何其他编程语言，如 Python 或 PHP，它们现在也都有解析和操作 JSON 数据的方法。</p><p>例如，Python 提供 <code>json.load()</code> 和 <code>json.dumps()</code> 来处理 JSON 数据。</p><p>为了确保客户端正确地解释 JSON 数据，你应该在发出请求时将响应头中的 <code>Content-Type</code> 类型设置为 <code>application/json</code>。</p><p>另一方面，对于服务器端的框架，许多框架会自动设置 <code>Content-Type</code>。例如，Express 现在有 <code>express.json()</code> 中间件来实现这一目的。<code>body-parser</code> NPM 包也仍然适用于同一目的。</p><h3 id="-">在端点中使用名词而不是动词</h3><p>当你设计一个 REST API 时，你不应该在端点路径中使用动词。端点应该使用名词，表示它们各自的作用。</p><p>这是因为 HTTP 方法，例如 <code>GET</code>、<code>POST</code>、<code>PUT</code>、<code>PATCH</code> 和 <code>DELETE</code>，已经以动词形式执行基本的 CRUD（创建、读取、更新、删除）操作。</p><p><code>GET</code>、<code>POST</code>、<code>PUT</code>、<code>PATCH</code> 和 <code>DELETE</code> 是最常见的 HTTP 动词。还有其他一些动词，如 <code>COPY</code>、<code>PURGE</code>、<code>LINK</code>、<code>UNLINK</code> 等等。</p><p>因此，举例来说，一个端点不应该是这样的：</p><p><code>https://mysite.com/getPosts or https://mysite.com/createPost</code></p><p>它应该是这样的： <code>https://mysite.com/posts</code></p><p>简而言之，你应该让 HTTP 动词来处理端点的工作。因此，<code>GET</code> 将检索数据，<code>POST</code> 将创建数据，<code>PUT</code> 将更新数据，<code>DELETE</code> 将删除数据。</p><h3 id="--1">用复数名词命名集合</h3><p>你可以把你的 API 的数据看成是来自用户的不同资源的集合。</p><p>如果你有一个像 <code>https://mysite.com/post/123</code> 这样的端点，用 <code>DELETE</code> 请求删除一个帖子，或用 <code>PUT</code> 或 <code>PATCH</code> 请求更新一个帖子，可能是可以的，但它没有告诉用户在这个集合中可能还有一些其他的帖子。这就是为什么你的集合应该使用复数的名词。</p><p>所以，不应该是 <code>https://mysite.com/post/123</code>，而是 <code>https://mysite.com/posts/123</code>。</p><h3 id="--2">在错误处理中使用状态码</h3><p>你应该在对你的 API 请求的响应中始终使用常规的 HTTP 状态代码。这将帮助你的用户知道发生了什么——请求是否成功，或者是否失败，或者其他情况。</p><p>下面的表格显示了不同的 HTTP 状态代码范围和它们的含义：</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>状态码范围</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>100 – 199</td>
<td>信息性回应：例如，102 表示该资源正在处理中</td>
</tr>
<tr>
<td>300 – 399</td>
<td>重定向：例如，301 表示永久移动</td>
</tr>
<tr>
<td>400 – 499</td>
<td>客户端错误：400 表示错误的请求，404 表示未找到资源</td>
</tr>
<tr>
<td>500 – 599</td>
<td>服务器端错误：例如，500 表示内部服务器错误</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><h3 id="--3">在端点上使用嵌套来显示关系</h3><p>很多时候，不同的端点可以相互联系，所以你应该对它们进行嵌套，这样更容易理解它们。</p><p>例如，对于一个多用户博客平台，不同的帖子可能是由不同的作者写的，所以在这种情况下，像 <code>https://mysite.com/posts/author</code> 这样的端点会成为一个有效的嵌套。</p><p>同样地，帖子可能有各自的评论，所以要检索评论，可以使用 <code>https://mysite.com/posts/postId/comments</code> 这样的端点。</p><p>你应该避免超过 3 层的嵌套，因为这可能使 API 不那么优雅和具有可读性。</p><h3 id="--4">使用过滤、排序和分页来检索请求的数据</h3><p>有时，API 的数据库可能非常大。如果发生这种情况，从这样的数据库中检索数据可能非常缓慢。</p><p>过滤、排序和分页都是可以在 REST API 的集合上执行的操作。这样只能检索、排序和排列必要的数据，并将其分页，以防服务器请求过载。</p><p>以下是一个已过滤的端点的示例：<code>https://mysite.com/posts?tags=javascript</code>。此端点将检索具有 JavaScript 标签的任何帖子。</p><h3 id="-ssl-">使用 SSL 保障安全</h3><p>SSL 指的是安全套接层。这对于 REST API 设计的安全性至关重要。这将保护你的 API，使其更不容易受到恶意攻击。</p><p>你还应考虑其他安全措施，包括：使服务器和客户端之间的通信保密，确保使用 API 的任何人不会获得他们请求的以外的数据。</p><p>SSL 证书不难加载到服务器上，而且大多数情况下在第一年是免费的。即使需要购买，它们也并不昂贵。</p><p>运行在 SSL 上的 REST API 的 URL 与不运行在 SSL 上的 URL 的明显区别是 HTTP 中的 “s”：<code>https://mysite.com/posts</code> 运行在 SSL 上，<code>http://mysite.com/posts</code> 不运行在 SSL 上。</p><h3 id="--5">明确版本划分</h3><p>REST API 应该有不同的版本，所以你不会强迫客户（用户）迁移到新版本。如果你不小心，这甚至可能破坏应用程序。</p><p>网络开发中最常见的版本控制系统之一是语义版本控制。</p><p>语义版本管理的一个例子是 1.0.0、2.1.2 和 3.3.4。第一个数字代表主要版本，第二个数字代表次要版本，第三个数字代表补丁版本。</p><p>许多科技巨头和个人的 RESTful API 通常是这样的：<code>https://mysite.com/v1/</code> 代表版本 1，<a href="https://mysite.com/v2">https://mysite.com/v2</a> 代表版本 2。</p><p>Facebook 的 API 版本是这样的：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/facebook-versioning.jpg" class="kg-image" alt="facebook-versioning" width="600" height="400" loading="lazy"></figure><p>Spotify 以同样的方式做他们的版本管理：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/spotify-versioning.jpg" class="kg-image" alt="spotify-versioning" width="600" height="400" loading="lazy"></figure><p>并不是每个 API 都是这样的情况。Mailchimp 的 API 版本是这样的：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/mailchimp-ersioning.jpg" class="kg-image" alt="mailchimp-ersioning" width="600" height="400" loading="lazy"></figure><p>当你以这种方式创建 REST API 时，你不强制客户端在选择不迁移的情况下迁移到新版本。</p><h3 id="-api-">提供准确的 API 文档</h3><p>当你创建 REST API 时，你需要帮助用户（消费者）正确学习并了解如何使用它。最好的方法是为 API 提供良好的文档。</p><p>文档应包含：</p><ul><li>API 的相关端点</li><li>端点的示例请求</li><li>在几种编程语言中的实现</li><li>不同错误的消息列表及其状态代码</li></ul><p>你可以用于 API 文档的最常用工具是 Swagger。你也可以使用 Postman 来记录你的 API，这是软件开发中最常见的 API 测试工具。</p><h2 id="--6"><strong>总结</strong></h2><p>在这篇文章中，你了解了在创建 REST API 时需要记住的几个最佳实践。</p><p>将这些最佳实践和惯例付诸实践是很重要的，这样你就可以创建功能强大的应用程序，使其运行良好、安全，并最终使你的 API 用户能够更加容易地使用它。</p><p>谢谢你阅读本文。现在，去用这些最佳实践创建一些 API 吧。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 JavaScript 创建有下拉菜单的响应式导航栏 ]]>
                </title>
                <description>
                    <![CDATA[ 导航栏是网站和 web 应用中经常使用的基本组件。作为一名 web 开发人员，你需要能够为客户的项目或者基本的作品集网站定制它。 在本指南中，你将学习如何只用 HTML、CSS 和 JavaScript 从头开始创建一个导航栏。你还将学习如何从无障碍方面优化它。 下面是这个导航栏的截图： 这个设计的灵感来自 Dribbble 上 Tran Mau Tri Tam 的极简导航栏 [https://dribbble.com/shots/17305696-Minimal-Navigation?utm_source=Clipboard_Shot&utm_campaign=tranmautritam&utm_content=Minimal%20Navigation&utm_medium=Social_Share&utm_source=Clipboard_Shot&utm_campaign=tranmautritam&utm_content=Minimal%20Navigation&utm_medium=Social_Share] 。 第 1 步 - 添加 HTML 标记 为了简洁起见，我 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-build-a-responsive-navigation-bar-with-dropdown-menu-using-javascript/</link>
                <guid isPermaLink="false">649c47d10f6905067957c070</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 28 Jun 2023 04:12:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/06/maxwell-nelson-taiuG8CPKAQ-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-build-a-responsive-navigation-bar-with-dropdown-menu-using-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Responsive Navigation Bar with a Dropdown Menu using JavaScript</a>
      </p><p>导航栏是网站和 web 应用中经常使用的基本组件。作为一名 web 开发人员，你需要能够为客户的项目或者基本的作品集网站定制它。</p><p>在本指南中，你将学习如何只用 HTML、CSS 和 JavaScript 从头开始创建一个导航栏。你还将学习如何从无障碍方面优化它。</p><p>下面是这个导航栏的截图：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/navigation-bar-final-result.jpg" class="kg-image" alt="navigation-bar-final-result" width="600" height="400" loading="lazy"></figure><p>这个设计的灵感来自 Dribbble 上 Tran Mau Tri Tam 的<a href="https://dribbble.com/shots/17305696-Minimal-Navigation?utm_source=Clipboard_Shot&amp;utm_campaign=tranmautritam&amp;utm_content=Minimal%20Navigation&amp;utm_medium=Social_Share&amp;utm_source=Clipboard_Shot&amp;utm_campaign=tranmautritam&amp;utm_content=Minimal%20Navigation&amp;utm_medium=Social_Share">极简导航栏</a>。</p><h2 id="-1-html-">第 1 步 - 添加 HTML 标记</h2><p>为了简洁起见，我们将使用一个叫做 <a href="https://github.com/atisawd/boxicons">boxicons</a> 的图标库来为这个导航栏导入某些图标。我强烈建议使用内联 SVG。</p><p>要使用这个库，请在你的 HTML 文件的 head 插入下面的片段：</p><pre><code class="language-html">&lt;link href='https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css' rel='stylesheet'&gt;</code></pre><p>该标记被分为三个主要部分：</p><ul><li>一个 class 为 <code>nav-start</code> 的 <code>div</code> 元素</li><li>另一个 <code>div</code> 元素，class 为 <code>nav-end</code></li><li>一个 id 为 <code>hamburger</code> 的 <code>button</code> 元素</li></ul><p>所有这些元素都将被包裹在一个 <code>header</code> 标签中。为了更好地解释这一点，请复制下面的标记。</p><pre><code class="language-html">&lt;header id="nav-menu" aria-label="navigation bar"&gt;
  &lt;div class="container"&gt;
    &lt;div class="nav-start"&gt;
      &lt;a class="logo" href="/"&gt;
        &lt;img src="https://github.com/Evavic44/responsive-navbar-with-dropdown/blob/main/assets/images/logo.png?raw=true" 
             width="35" 
             height="35" 
             alt="Inc Logo"
          /&gt;
      &lt;/a&gt;
      &lt;nav class="menu"&gt;&lt;/nav&gt;
    &lt;/div&gt;

    &lt;div class="nav-end"&gt;
      &lt;div class="right-container"&gt;
        &lt;form class="search" role="search"&gt;
          &lt;input type="search" name="search" placeholder="Search" /&gt;
          &lt;i class="bx bx-search" aria-hidden="true"&gt;&lt;/i&gt;
        &lt;/form&gt;

        &lt;a href="#profile"&gt;
          &lt;img src="https://github.com/Evavic44/responsive-navbar-with-dropdown/blob/main/assets/images/user.jpg?raw=true" 
               width="30" 
               height="30" 
               alt="user image" 
            /&gt;
        &lt;/a&gt;
        &lt;button class="btn btn-primary"&gt;Create&lt;/button&gt;
      &lt;/div&gt;

      &lt;button id="hamburger" aria-label="hamburger" aria-haspopup="true" aria-expanded="false"&gt;
        &lt;i class="bx bx-menu" aria-hidden="true"&gt;&lt;/i&gt;
      &lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/header&gt;</code></pre><p>对于 nav-start，我们有以下元素：</p><ul><li>一个 <code>&lt;img&gt;</code> 元素作为 logo，包裹在一个 <code>&lt;a&gt;</code> 标签中</li><li>一个 <code>&lt;nav&gt;</code> 元素，class 为 <code>menu</code>，包含所有导航链接，我们将使用 <code>&lt;ul&gt;</code>、<code>&lt;li&gt;</code> 和 <code>&lt;a&gt;</code> 定义这些链接</li></ul><p>nav-end 有以下元素：</p><ul><li>一个 <code>&lt;form&gt;</code> 元素，它的 role 是 search，包含一个搜索输入和搜索图标</li><li>一个 class 为 <code>btn</code> 的按钮元素，我们将使用这个 class 来设计按钮</li></ul><p>对于汉堡包按钮：</p><ul><li>一个按钮，其 id 和 <code>aria-label</code> 为 hamburger，<code>aria-haspopup</code> 设置为 “true”，<code>aria-expanded</code> 设置为 “false”。这些标签将使我们能够使这个按钮更容易被屏幕阅读器访问。</li></ul><p>下面是输出：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/markup-elements-broken-down-into-three-main-parts-1.png" class="kg-image" alt="markup-elements-broken-down-into-three-main-parts-1" width="600" height="400" loading="lazy"></figure><h3 id="-"><strong>导航菜单</strong></h3><p>导航菜单 <code>&lt;nav&gt;</code> 是导航链接所在的位置。用下面这个标记替换你先前添加的 <code>nav</code> 元素：</p><pre><code class="language-html">&lt;nav class="menu"&gt;
    &lt;ul class="menu-bar"&gt;
        &lt;li&gt;
            &lt;button 
              class="nav-link dropdown-btn" 
              data-dropdown="dropdown1" 
              aria-haspopup="true" 
              aria-expanded="false" 
              aria-label="browse"&gt;
                Browse
              &lt;i class="bx bx-chevron-down" aria-hidden="true"&gt;&lt;/i&gt;
            &lt;/button&gt;
            &lt;div id="dropdown1" class="dropdown"&gt;&lt;/div&gt;
        &lt;/li&gt;
        &lt;li&gt;
            &lt;button 
              class="nav-link dropdown-btn" 
              data-dropdown="dropdown2" 
              aria-haspopup="true" 
              aria-expanded="false" 
              aria-label="discover"&gt;
                Discover
              &lt;i class="bx bx-chevron-down" aria-hidden="true"&gt;&lt;/i&gt;
            &lt;/button&gt;
            &lt;div id="dropdown2" class="dropdown"&gt;&lt;/div&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;a class="nav-link" href="/"&gt;Jobs&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a class="nav-link" href="/"&gt;Livestream&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a class="nav-link" href="/"&gt;About&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/nav&gt;</code></pre><p>这里你有一个 <code>nav</code> 标签，它包含一个无序的列表，其中有五个 <code>li</code> 元素，代表每个导航菜单项目：<strong><strong>browse</strong>、<strong>discover</strong>、<strong> jobs</strong>、<strong>livestream</strong> 和</strong> <strong><strong>about</strong></strong>。</p><p>前两个元素，browse 和 discover，是 <code>button</code> 元素，将被用来切换它们各自的下拉菜单。而元素 Jobs、livestream 和 about 只是普通的链接。</p><p>使用到目前为止的代码，你的结果应该是这样的：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/Navigation-markup-with-links-and-popup-buttons.png" class="kg-image" alt="Navigation-markup-with-links-and-popup-buttons" width="600" height="400" loading="lazy"></figure><h3 id="--1"><strong>下拉元素</strong></h3><p>接下来，让我们为每个导航按钮定义下拉元素。下面是第一个下拉菜单的标记。把你的标记中的第一个 <code>li</code> 元素替换成这样：</p><pre><code class="language-html">&lt;!-- markup truncated for brevity--&gt;
&lt;li&gt;
  &lt;button
    class="nav-link dropdown-btn"
    data-dropdown="dropdown1"
    aria-haspopup="true"
    aria-expanded="false"
    aria-label="browse"
  &gt;
    Browse
    &lt;i class="bx bx-chevron-down" aria-hidden="true"&gt;&lt;/i&gt;
  &lt;/button&gt;
  &lt;div id="dropdown1" class="dropdown"&gt;
    &lt;ul role="menu"&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#best-of-the-day"&gt;
          &lt;img src="./assets/icons/botd.svg" class="icon" /&gt;
          &lt;div&gt;
            &lt;span class="dropdown-link-title"
              &gt;Best of the day&lt;/span
            &gt;
            &lt;p&gt;Shorts featured today by curators&lt;/p&gt;
          &lt;/div&gt;
        &lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#featured-streams"&gt;
          &lt;img src="./assets/icons/fs.svg" class="icon" /&gt;
          &lt;div&gt;
            &lt;span class="dropdown-link-title"
              &gt;Featured Streams&lt;/span
            &gt;
            &lt;p&gt;Leading creatives livestreams&lt;/p&gt;
          &lt;/div&gt;
        &lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#subscriptions"&gt;
          &lt;img src="./assets/icons/sp.svg" class="icon" /&gt;
          &lt;div&gt;
            &lt;span class="dropdown-link-title"&gt;Subscriptions&lt;/span&gt;
            &lt;p&gt;Gain exclusive access&lt;/p&gt;
          &lt;/div&gt;
        &lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#creative-feed"&gt;
          &lt;img src="./assets/icons/cf.svg" class="icon" /&gt;
          &lt;div&gt;
            &lt;span class="dropdown-link-title"&gt;Creative Feed&lt;/span&gt;
            &lt;p&gt;See trending creations&lt;/p&gt;
          &lt;/div&gt;
        &lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;

    &lt;ul role="menu"&gt;
      &lt;li class="dropdown-title"&gt;
        &lt;span class="dropdown-link-title"&gt;Browse by apps&lt;/span&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#adobe-xd"&gt;
          &lt;img src="./assets/icons/xd.svg" /&gt;
          Adobe XD
        &lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#after-effect"&gt;
          &lt;img src="./assets/icons/ae.svg" /&gt;
          After Effect
        &lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#sketch"&gt;
          &lt;img src="./assets/icons/sketch.svg" /&gt;
          Sketch
        &lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#indesign"&gt;
          &lt;img src="./assets/icons/indesign.svg" /&gt;
          Indesign
        &lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#figma"&gt;
          &lt;img src="./assets/icons/figma.svg" /&gt;
          Figma
        &lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/li&gt;</code></pre><p>你可以在<a href="https://github.com/Evavic44/responsive-navbar-with-dropdown/tree/main/assets/icons">这里</a>获得 SVG 图标。</p><p>看看这个标记，我们添加了以下内容：</p><ul><li>一个 id 为 <code>dropdown1</code>、class 为 <code>dropdown</code> 的 <code>div</code> 元素。</li><li>两个 <code>ul</code> 元素，每个元素的 role 是 <code>"menu"</code>。</li><li>一个 <code>span</code> 元素，其 class 为 <code>dropdown-link-title</code>，作为每个 <code>menu</code> 集合的 header。</li><li>使用 <code>li</code> 和 <code>a</code> 标签定义的链接集合，<code>li</code> 标签有一个 role 为 <code>"menuitem"</code>，每个链接有一个 class 为 <code>dropdown-link</code>。</li><li>在每个锚标签内，通过 <code>img</code> 标签添加一个图标。</li></ul><p>注意：由于通过img标签添加的图标是严格意义上的声明性的，我强烈建议你直接将它们作为SVG元素添加。我这样做只是为了使代码更容易阅读</p><p>下面是第二个下拉元素dropdown2的标记：</p><pre><code class="language-html">&lt;!-- markup truncated for brevity--&gt;
&lt;li&gt;
  &lt;button
    class="nav-link dropdown-btn"
    data-dropdown="dropdown2"
    aria-haspopup="true"
    aria-expanded="false"
    aria-label="discover"
  &gt;
    Discover
    &lt;i class="bx bx-chevron-down" aria-hidden="true"&gt;&lt;/i&gt;
  &lt;/button&gt;
  &lt;div id="dropdown2" class="dropdown"&gt;
    &lt;ul role="menu"&gt;
      &lt;li&gt;
        &lt;span class="dropdown-link-title"&gt;Browse Categories&lt;/span&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#branding"&gt;Branding&lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#illustrations"&gt;Illustration&lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul role="menu"&gt;
      &lt;li&gt;
        &lt;span class="dropdown-link-title"&gt;Download App&lt;/span&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#mac-windows"&gt;MacOS &amp; Windows&lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#linux"&gt;Linux&lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/li&gt;</code></pre><p>最终结果应该是这样：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/popup1-and-popup2-markup-1.png" class="kg-image" alt="popup1-and-popup2-markup-1" width="600" height="400" loading="lazy"></figure><p>本教程最后将提供完整的标记。</p><h2 id="-2-">第 2 步 - 为导航栏设计样式</h2><p>像往常一样，我们将从重置页面上每个元素的默认 margin 和 padding 开始，添加全局变量，并对一些元素进行一些基本的样式设计。</p><pre><code class="language-css">/* style.css */
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700&amp;display=swap");

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Inter", sans-serif;
}

:root {
  --dark-grey: #333333;
  --medium-grey: #636363;
  --light-grey: #eeeeee;
  --ash: #f4f4f4;
  --primary-color: #2b72fb;
  --white: white;
  --border: 1px solid var(--light-grey);
  --shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,
    rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;
}

body {
  font-family: inherit;
  background-color: var(--white);
  color: var(--dark-grey);
  letter-spacing: -0.4px;
}

ul {
  list-style: none;
}

a {
  text-decoration: none;
  color: inherit;
}

button {
  border: none;
  background-color: transparent;
  cursor: pointer;
  color: inherit;
}</code></pre><p>接下来，添加一些可重复使用的样式。</p><pre><code class="language-css">.btn {
  display: block;
  background-color: var(--primary-color);
  color: var(--white);
  text-align: center;
  padding: 0.6rem 1.4rem;
  font-size: 1rem;
  font-weight: 500;
  border-radius: 5px;
}

.icon {
  padding: 0.5rem;
  background-color: var(--light-grey);
  border-radius: 10px;
}

.logo {
  margin-right: 1.5rem;
}

#nav-menu {
  border-bottom: var(--border);
}

.container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 1600px;
  margin: 0 auto;
  column-gap: 2rem;
  height: 90px;
  padding: 1.2rem 3rem;
}</code></pre><p>现在你已经得到了这些基本的样式，你可以专注于核心导航栏本身的样式。</p><h3 id="--2">导航菜单的样式</h3><p>下面是为导航栏容器设计的标记：</p><pre><code class="language-css">.menu {
  position: relative;
  background: var(--white);
}

.menu-bar li:first-child .dropdown {
  flex-direction: initial;
  min-width: 480px;
}

.menu-bar li:first-child ul:nth-child(1) {
  border-right: var(--border);
}

.menu-bar li:nth-child(n + 2) ul:nth-child(1) {
  border-bottom: var(--border);
}

.menu-bar .dropdown-link-title {
  font-weight: 600;
}

.menu-bar .nav-link {
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: -0.6px;
  padding: 0.3rem;
  min-width: 60px;
  margin: 0 0.6rem;
}

.menu-bar .nav-link:hover,
.dropdown-link:hover {
  color: var(--primary-color);
}

.nav-start,
.nav-end,
.menu-bar,
.right-container,
.right-container .search {
  display: flex;
  align-items: center;
}</code></pre><h3 id="--3">下拉菜单的样式</h3><p>除了对下拉菜单进行样式设计外，还将使用 <code>visibility</code> 和 <code>opacity</code> 属性的组合来隐藏它。我们的想法是，只有在一个按钮被点击时才显示菜单。</p><pre><code class="language-css">.dropdown {
  display: flex;
  flex-direction: column;
  min-width: 230px;
  background-color: var(--white);
  border-radius: 10px;
  position: absolute;
  top: 36px;
  z-index: 1;
  visibility: hidden;
  opacity: 0;
  transform: scale(0.97) translateX(-5px);
  transition: 0.1s ease-in-out;
  box-shadow: var(--shadow);
}

.dropdown.active {
  visibility: visible;
  opacity: 1;
  transform: scale(1) translateX(5px);
}

.dropdown ul {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.2rem;
  font-size: 0.95rem;
}

.dropdown-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.15rem;
}

.dropdown-link {
  display: flex;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-radius: 7px;
  transition: 0.1s ease-in-out;
}

.dropdown-link p {
  font-size: 0.8rem;
  color: var(--medium-grey);
}</code></pre><p>接着，可以通过使用 <code>active</code> class 将 <code>visibility</code> 和 <code>opacity</code> 属性恢复到默认状态来切换该菜单。但我们将通过 JavaScript 来做这件事。</p><p>如果你喜欢完全隐藏菜单，可以用 <code>display: none;</code> 代替 <code>opacity</code> 和 <code>visibility</code> 属性。<a href="https://mdn.org/animatable-properties">虽然这个属性在 CSS 中不能用过渡来做动画</a>。</p><h3 id="--4">右边的菜单样式</h3><p>接下来，为搜索输入、按钮和个人资料图片添加样式，然后在桌面屏幕上隐藏汉堡包按钮。</p><pre><code class="language-css">.right-container {
  display: flex;
  align-items: center;
  column-gap: 1rem;
}

.right-container .search {
  position: relative;
}

.right-container img {
  border-radius: 50%;
}

.search input {
  background-color: var(--ash);
  border: none;
  border-radius: 6px;
  padding: 0.7rem;
  padding-left: 2.4rem;
  font-size: 16px;
  width: 100%;
  border: var(--border);
}

.search .search-icon {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  opacity: 0.6;
}

#hamburger {
  display: none;
  padding: 0.1rem;
  margin-left: 1rem;
  font-size: 1.9rem;
}</code></pre><p>现在它是这样的：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/final-styling-output-of-navigation-bar-and-popup-menu.png" class="kg-image" alt="final-styling-output-of-navigation-bar-and-popup-menu" width="600" height="400" loading="lazy"></figure><p>为了完成样式设计，添加媒体查询样式：</p><pre><code class="language-css">@media (max-width: 1100px) {
  #hamburger {
    display: block;
  }

  .container {
    padding: 1.2rem;
  }

  .menu {
    display: none;
    position: absolute;
    top: 87px;
    left: 0;
    min-height: 100vh;
    width: 100vw;
  }

  .menu-bar li:first-child ul:nth-child(1) {
    border-right: none;
    border-bottom: var(--border);
  }

  .dropdown {
    display: none;
    min-width: 100%;
    border: none !important;
    border-radius: 5px;
    position: static;
    top: 0;
    left: 0;
    visibility: visible;
    opacity: 1;
    transform: none;
    box-shadow: none;
  }

  .menu.show,
  .dropdown.active {
    display: block;
  }

  .dropdown ul {
    padding-left: 0.3rem;
  }

  .menu-bar {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    row-gap: 1rem;
    padding: 1rem;
  }

  .menu-bar .nav-link {
    display: flex;
    justify-content: space-between;
    width: 100%;
    font-weight: 600;
    font-size: 1.2rem;
    margin: 0;
  }

  .menu-bar &gt; li:not(:last-child) {
    padding-bottom: 0.5rem;
    border-bottom: var(--border);
  }
}

@media (max-width: 600px) {
  .right-container {
    display: none;
  }
}</code></pre><p>首先，这排列了元素，最重要的是，它定位 <code>hamburger</code> class 并将其隐藏。现在在平板电脑和手机屏幕上，导航栏是响应式的，汉堡包按钮是可见的。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/responsive-navigation-bar-2.gif" class="kg-image" alt="responsive-navigation-bar-2" width="600" height="400" loading="lazy"></figure><p>这就完成了导航栏的样式设计。让我们在下一节中进行功能设计。</p><h2 id="-3-javascript-">第 3 步 - 添加 JavaScript 功能</h2><p>对于 JavaScript 功能，我们将专注于以下几个类别：</p><ul><li>切换下拉菜单的可见性</li><li>关闭下拉菜单</li><li>切换汉堡包菜单的可见性</li><li>切换 aria-expanded 属性</li></ul><p>首先，使用 DOM 的 <code>querySelector</code> 方法选择你的类，并将它们存储在变量中，以便它们可以重复使用。</p><pre><code class="language-javascript">// script.js
const dropdownBtn = document.querySelectorAll(".dropdown-btn");
const dropdown = document.querySelectorAll(".dropdown");
const hamburgerBtn = document.getElementById("hamburger");
const navMenu = document.querySelector(".menu");
const links = document.querySelectorAll(".dropdown a");</code></pre><p>接下来在你的代码中添加下面的函数。我稍后将解释它们的用途。</p><pre><code class="language-js">function setAriaExpandedFalse() {
  dropdownBtn.forEach((btn) =&gt; btn.setAttribute("aria-expanded", "false"));
}

function closeDropdownMenu() {
  dropdown.forEach((drop) =&gt; {
    drop.classList.remove("active");
    drop.addEventListener("click", (e) =&gt; e.stopPropagation());
  });
}

function toggleHamburger() {
  navMenu.classList.toggle("show");
}</code></pre><h3 id="-id">获取下拉菜单 ID</h3><p>下一步是获取下拉菜单的 ID。由于有两个下拉菜单，其值将基于点击的下拉按钮。</p><p>为了获得 ID，你将利用 <code>dataset</code> 属性，然后将该值存储到它自己的变量中。</p><pre><code class="language-js">dropdownBtn.forEach((btn) =&gt; {
  btn.addEventListener("click", function (e) {
    const dropdownIndex = e.currentTarget.dataset.dropdown;
    const dropdownElement = document.getElementById(dropdownIndex);
    console.log(dropdownElement);
  });
});
</code></pre><p>理解这个片段：</p><ul><li><code>forEach</code> 方法遍历按钮的集合</li><li><code>addEventListener()</code> 方法为每个按钮附加了一个点击事件</li><li><code>currentTarget.dataset</code> 属性获取被点击按钮的当前下拉菜单</li><li>每一个 id 都被用来定位相应的下拉元素</li></ul><p>这意味着，当 dataset 为 <code>dropdown1</code> 的按钮被点击时，id 为 <code>dropdown1</code> 的 <code>div</code> 元素被记录到控制台，反之则为 <code>dropdown2</code> 按钮。</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/05/get-popup-element-id-dynamically-1.gif" class="kg-image" alt="get-popup-element-id-dynamically-1" width="600" height="400" loading="lazy"><figcaption>使用按钮 dataset 属性动态地获得每个下拉元素</figcaption></figure><h3 id="--5">切换下拉菜单</h3><p>切换菜单是相当容易的，因为你已经把下拉元素的 ID 存储到一个叫作 <code>dropdownElement</code> 的变量中。通过定位这个变量，你可以切换每个下拉元素的 <code>active</code> class。</p><pre><code class="language-js">dropdownBtn.forEach((btn) =&gt; {
  btn.addEventListener("click", function (e) {
    const dropdownIndex = e.currentTarget.dataset.dropdown;
    const dropdownElement = document.getElementById(dropdownIndex);

    dropdownElement.classList.toggle("active");
    dropdown.forEach((drop) =&gt; {
      if (drop.id !== btn.dataset["dropdown"]) {
        drop.classList.remove("active");
      }
    });
    e.stopPropagation();
  });
});</code></pre><p>除了切换下拉菜单外，我们还添加了一个条件，检查当前下拉元素的 id 是否与活动按钮相匹配。这可以确保每次只有一个下拉元素被展开。</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/05/toggling-dropdown-element.gif" class="kg-image" alt="toggling-dropdown-element" width="600" height="400" loading="lazy"><figcaption>切换下拉菜单</figcaption></figure><h3 id="-aria-expanded-">切换 aria-expanded 属性</h3><p><code>aria-expanded</code> 属性允许辅助技术告知一个交互式菜单是展开还是折叠的。要切换这个属性，请在 <code>btn</code> 代码块中的 <code>e.stopPropagation()</code> 下插入这段代码：</p><pre><code class="language-js">btn.setAttribute(
    "aria-expanded",
    btn.getAttribute("aria-expanded") === "false" ? "true" : "false"
);</code></pre><p>现在，只要下拉菜单是可见的，<code>aria-expanded</code> 属性就被设置为 true；而当菜单折叠时，它被设置为 false。</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/05/toggling-the-aria-expanded-property.gif" class="kg-image" alt="toggling-the-aria-expanded-property" width="600" height="400" loading="lazy"><figcaption>切换 aria-expanded 属性</figcaption></figure><h3 id="--6">折叠下拉菜单</h3><p>到目前为止，下拉菜单只有在点击按钮的时候才会折叠。它应该被折叠的其他情况包括：</p><ul><li>当点击下拉菜单内的链接时</li><li>当你按下 ESC 键时</li><li>当你点击文档主体时——在下拉容器之外</li></ul><p>通过调用前面创建的函数 <code>closeDropdownMenu</code> 和 <code>setAriaExpandedFalse</code>，可以折叠下拉菜单并将 <code>aria-expanded</code> 属性设置为 false。</p><pre><code class="language-js">// 当点击下拉链接时关闭下拉菜单
links.forEach((link) =&gt;
  link.addEventListener("click", () =&gt; {
    closeDropdownMenu();
    setAriaExpandedFalse();
  })
);

// 当点击文档主体时关闭下拉菜单
document.documentElement.addEventListener("click", () =&gt; {
  closeDropdownMenu();
  setAriaExpandedFalse();
});

// 当按 ESC 键时关闭下拉菜单
document.addEventListener("keydown", (e) =&gt; {
  if (e.key === "Escape") {
    closeDropdownMenu();
    setAriaExpandedFalse();
  }
});
</code></pre><p>下面是输出：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/closing-dropdown-menu-when-dropdown-links-and-escape-key-is-clicked.gif" class="kg-image" alt="closing-dropdown-menu-when-dropdown-links-and-escape-key-is-clicked" width="600" height="400" loading="lazy"></figure><h3 id="--7">切换汉堡包菜单</h3><p>要在平板电脑和手机屏幕上看到导航栏，请将 <code>toggleHamburger</code> 函数作为汉堡包按钮的回调，然后在 <code>links</code> 代码块内调用该函数。</p><pre><code class="language-js">links.forEach((link) =&gt;
  link.addEventListener("click", () =&gt; {
    closeDropdownMenu();
    setAriaExpandedFalse();
    toggleHamburger();
  })
);</code></pre><pre><code class="language-js">hamburgerBtn.addEventListener("click", toggleHamburger);</code></pre><p>这会切换一个名为 <code>show</code> 的不同 class，控制显示或隐藏导航栏。</p><p>下面是最终的输出：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/show-hamburger-menu-on-tablet-and-mobile-screens.gif" class="kg-image" alt="show-hamburger-menu-on-tablet-and-mobile-screens" width="600" height="400" loading="lazy"></figure><h2 id="--8">添加更多下拉菜单</h2><p>你可以添加更多下拉菜单，只需将任何一个列表项替换为按钮和下拉菜单的链接。为了使其发挥作用，请确保你更新以下内容：</p><ul><li>根据你需要的菜单数量，更新下拉菜单的 ID。例如，第三个菜单的 ID 是 <code>dropdown3</code></li><li>按钮的 <code>data-dropdown</code> 值将设置为 <code>dropdown3</code></li></ul><p>下面是一个将 <strong><strong>Jobs</strong></strong> 链接转换为下拉菜单的例子。</p><h3 id="--9"><strong>之前：</strong></h3><pre><code class="language-html">&lt;li&gt;&lt;a class="nav-link" href="/"&gt;Jobs&lt;/a&gt;&lt;/li&gt;</code></pre><h3 id="--10"><strong>之后：</strong></h3><pre><code class="language-html">&lt;li&gt;
  &lt;button
    class="nav-link dropdown-btn"
    data-dropdown="dropdown3"
    aria-haspopup="true"
    aria-expanded="false"
    aria-label="jobs"
  &gt;
    Jobs
    &lt;i class="bx bx-chevron-down" aria-hidden="true"&gt;&lt;/i&gt;
  &lt;/button&gt;
  &lt;div id="dropdown3" class="dropdown"&gt;
    &lt;ul role="menu"&gt;
      &lt;li&gt;&lt;span class="dropdown-link-title"&gt;Software&lt;/span&gt;&lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#frontend"&gt;Frontend&lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#backend"&gt;Backend&lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#ai-ml"&gt;AI/ML&lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#mobile-dev"&gt;Mobile Development&lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul role="menu"&gt;
      &lt;li&gt;
        &lt;span class="dropdown-link-title"&gt;Others&lt;/span&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#ui-ux"&gt;UI/UX&lt;/a&gt;
      &lt;/li&gt;
      &lt;li role="menuitem"&gt;
        &lt;a class="dropdown-link" href="#writing"&gt;Technical Writing&lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/li&gt;</code></pre><p>这是最终的结果：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2023/05/additional-dropdown-menu.png" class="kg-image" alt="additional-dropdown-menu" width="600" height="400" loading="lazy"></figure><p>按照这个过程，你可以添加你想要的下拉菜单。</p><p>就这样，你用 HTML、CSS 和 JavaScript 成功地创建了一个带有下拉菜单的响应式导航栏。你还学会了如何使用包括 <code>aria-expanded</code> 属性在内的几个 aria 属性来使菜单可访问。</p><p>下面是测试这个导航栏运行情况的 CodePen 文件：</p><figure class="kg-card kg-embed-card"><iframe height="300" scrolling="no" title="Embedded content" src="https://codepen.io/evavic44/embed/QWZYEPQ?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-weight: 400; font-stretch: inherit; line-height: inherit; font-family: Lato, sans-serif; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 22px; vertical-align: middle; color: rgb(10, 10, 35); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; width: 720px;"></iframe></figure><p>这是 GitHub 的代码<a href="https://github.com/Evavic44/responsive-navbar-with-dropdown">链接</a>。</p><h2 id="--11"><strong>总结</strong></h2><p>我真诚地希望你觉得这篇文章有趣或有用。如果你这么想，请与你的朋友分享它或订阅我的博客，这样你就不会错过任何未来的文章。感谢阅读。</p><p><a href="https://github.com/Evavic44">GitHub</a> | <a href="https://twitter.com/victorekea">Twitter</a> | <a href="https://eke.hashnode.dev/">Blog</a> | <a href="https://www.linkedin.com/in/victorekeawa/">LinkedIn</a></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
