<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ 古月 - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ 古月 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 08:28:40 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/gu/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 中的 Var、Let 和 Const 有什么区别 ]]>
                </title>
                <description>
                    <![CDATA[ ES2015（ES6）推出了许多闪亮的新功能。从 2020 年开始，我们假设许多 JavaScript 开发人员已经熟悉并开始使用这些功能。 尽管这个假设可能部分正确，但是其中某些功能可能对一些开发人员来说仍然是个谜。 ES6 附带的功能之一是添加了let和const，可用于变量声明。问题是，它们与var有何不同？ 如果你仍然不清楚-那么读完本文你就知道了 😂。 在本文中，我们将讨论var，let和const的作用域、用途和变量提升。当你阅读时，请注意我将指出的它们之间的差异。 Var 在 ES6 出现之前，必须使用 var  声明。但是，前端开发出现的一些问题与使用 var  声明的变量有关。这就是为什么必须要有新的方法来声明变量。首先，让我们在讨论这些问题之前更多地了解 var。 var 的作用域 作用域本质上是指变量/函数可供访问的范围。var可以在全局范围声明或函数/局部范围内声明。 当在最外层函数的外部声明var变量时，作用域是全局的。这意味着在最外层函数的外部用var声明的任何变量都可以在windows中使用。 当在函数中声明var时，作用域是局部的。这意味 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-var-let-and-const/</link>
                <guid isPermaLink="false">5fcf087d39641a0517d51b49</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 古月 ]]>
                </dc:creator>
                <pubDate>Thu, 02 Dec 2021 05:05:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/12/emile-perron-xrVDYZRGdw4-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>ES2015（ES6）推出了许多闪亮的新功能。从 2020 年开始，我们假设许多 JavaScript 开发人员已经熟悉并开始使用这些功能。</p>
<p>尽管这个假设可能部分正确，但是其中某些功能可能对一些开发人员来说仍然是个谜。</p>
<p>ES6 附带的功能之一是添加了<code>let</code>和<code>const</code>，可用于变量声明。问题是，它们与<code>var</code>有何不同？ 如果你仍然不清楚-那么读完本文你就知道了 😂。</p>
<p>在本文中，我们将讨论<code>var</code>，<code>let</code>和<code>const</code>的作用域、用途和变量提升。当你阅读时，请注意我将指出的它们之间的差异。</p>
<h2 id="var">Var</h2>
<p>在 ES6 出现之前，必须使用 <code>var</code> 声明。但是，前端开发出现的一些问题与使用 <code>var</code> 声明的变量有关。这就是为什么必须要有新的方法来声明变量。首先，让我们在讨论这些问题之前更多地了解 <code>var</code>。</p>
<h3 id="var">var 的作用域</h3>
<p><strong>作用域</strong>本质上是指变量/函数可供访问的范围。<code>var</code>可以在全局范围声明或函数/局部范围内声明。</p>
<p>当在最外层函数的外部声明<code>var</code>变量时，作用域是全局的。这意味着在最外层函数的外部用<code>var</code>声明的任何变量都可以在<code>windows</code>中使用。</p>
<p>当在函数中声明<code>var</code>时，作用域是局部的。这意味着它只能在函数内访问。</p>
<p>要进一步了解，请查看下面的示例。</p>
<pre><code class="language-javascript">var greeter = 'hey hi';

function newFunction() {
    var hello = 'hello';
}
</code></pre>
<p>这里，<code>greeter</code>是全局范围的，因为它存在于函数外部，而<code>hello</code>是函数范围的。因此，我们无法在函数外部访问变量<code>hello</code>。因此，如果我们这样做：</p>
<pre><code class="language-javascript">var tester = 'hey hi';
function newFunction() {
    var hello = 'hello';
}
console.log(hello); // error: hello is not defined
</code></pre>
<p>我们会收到错误消息，这是由于函数外部没有<code>hello</code>导致的。</p>
<h3 id="var">var 变量可以重新声明和修改</h3>
<p>这意味着我们可以在相同的作用域内执行下面的操作，并且不会出错</p>
<pre><code class="language-javascript">var greeter = 'hey hi';
var greeter = 'say Hello instead';
</code></pre>
<p>又比如</p>
<pre><code class="language-javascript">var greeter = 'hey hi';
greeter = 'say Hello instead';
</code></pre>
<h3 id="var">var 的变量提升</h3>
<p>变量提升是 JavaScript 的一种机制:在执行代码之前，变量和函数声明会移至其作用域的顶部。这意味着如果我们这样做:</p>
<pre><code class="language-javascript">console.log(greeter);
var greeter = 'say hello';
</code></pre>
<p>生面的代码会被解释为:</p>
<pre><code class="language-javascript">var greeter;
console.log(greeter); // greeter is undefined
greeter = 'say hello';
</code></pre>
<p>因此，将<code>var</code>声明的变量会被提升到其作用域的顶部，并使用 undefined 值对其进行初始化.</p>
<h3 id="var">var 的问题</h3>
<p><code>var</code>有一个弱点。我将使用以下示例进行说明：</p>
<pre><code class="language-javascript">    var greeter = "hey hi";
    var times = 4;
if (times &amp;gt; 3) {
    var greeter = "say Hello instead";
}

console.log(greeter) // "say Hello instead"
</code></pre>
<p>由于<code>times&gt; 3</code>返回 true，因此将<code>greeter</code>重新定义为<code>saysay Hello</code>。如果你有是故意重新定义<code>greeter</code>，这段代码是问题的，但是当你不知道之前已经定义了变量<code>greeter</code>时，这将成为产生问题。</p>
<p>如果在代码的其他部分使用了<code>greeter</code>，这可能会导致代码中出现许多错误。这就是为什么需要<code>let</code>和<code>const</code>的原因。</p>
<h2 id="let">Let</h2>
<p><code>let</code>现在已经成为变量声明的首选。这并不奇怪，因为它是对<code>var</code>声明的改进。它也解决了我们刚刚介绍的<code>var</code>问题。让我们考虑一下为什么会这样。</p>
<h3 id="let">let 是块级作用域</h3>
<p>块是由 {} 界定的代码块，大括号中有一个块。大括号内的任何内容都包含在一个块级作用域中.</p>
<p>因此，在带有<code>let</code>的块中声明的变量仅可在该块中使用。让我用一个例子解释一下</p>
<pre><code class="language-javascript">let greeting = 'say Hi';
let times = 4;

if (times &gt; 3) {
    let hello = 'say Hello instead';
    console.log(hello); // "say Hello instead"
}
console.log(hello); // hello is not defined
</code></pre>
<p>我们看到在其代码块（定义它的花括号）之外使用<code>hello</code>会返回错误。这是因为<code>let</code>变量是块范围的.</p>
<h3 id="let">let 可以被修改但是不能被重新声明.</h3>
<p>就像<code>var</code>一样，用<code>let</code>声明的变量可以在其范围内被修改。但与<code>var</code>不同的是，<code>let</code>变量无法在其作用域内被重新声明。 来看下面的栗子:</p>
<pre><code class="language-javascript">let greeting = 'say Hi';
greeting = 'say Hello instead';
</code></pre>
<p>上面的代码将会返回一个错误:</p>
<pre><code class="language-javascript">let greeting = 'say Hi';
let greeting = 'say Hello instead'; // error: Identifier 'greeting' has already been declared
</code></pre>
<p>但是，如果在不同的作用域中定义了相同的变量，则不会有错误：</p>
<pre><code class="language-javascript">let greeting = 'say Hi';
if (true) {
    let greeting = 'say Hello instead';
    console.log(greeting); // "say Hello instead"
}
console.log(greeting); // "say Hi"
</code></pre>
<p>为什么没有错误？ 这是因为两个实例的作用域不同，因此它们会被视为不同的变量。</p>
<p>这个事实说明:使用<code>let</code>,是比<code>var</code>更好的选择。当使用<code>let</code>时，你不必费心思考 🤔 变量的名称，因为变量仅在其块级作用域内存在。</p>
<p>同样，由于在一个块级作用域内不能多次声明一个变量，因此不会发生前面讨论的<code>var</code>出现的问题。</p>
<h3 id="let">let 的变量提升</h3>
<p>就像<code>var</code>一样，<code>let</code>声明也被提升到作用域顶部。<br>
但不同的是:</p>
<ul>
<li>用<code>var</code>声明的变量会被提升到其作用域的顶部，并使用 undefined 值对其进行初始化。</li>
<li>用<code>let</code>声明的变量会被提升到其作用域的顶部，不会对值进行初始化。</li>
</ul>
<p>因此，如果你尝试在声明前使用<code>let</code>变量，则会收到<code>Reference Error</code>。</p>
<h2 id="const">Const</h2>
<p>用<code>const</code>声明的变量保持常量值。 <code>const</code>声明与<code>let</code>声明有一些相似之处</p>
<h3 id="const">const 声明的变量在块级作用域内</h3>
<p>像<code>let</code>声明一样，<code>const</code>声明只能在声明它们的块级作用域中访问</p>
<h3 id="const">const 不能被修改并且不能被重新声明</h3>
<p>这意味着用<code>const</code>声明的变量的值保持不变。不能修改或重新声明。因此，如果我们使用<code>const</code>声明变量，那么我们将无法做到这一点:</p>
<pre><code class="language-javascript">const greeting = 'say Hi';
greeting = 'say Hello instead'; // error: Assignment to constant variable.
</code></pre>
<p>或者这个这样:</p>
<pre><code class="language-javascript">const greeting = 'say Hi';
const greeting = 'say Hello instead'; // error: Identifier 'greeting' has already been declared
</code></pre>
<p>因此，每个<code>const</code>声明都必须在声明时进行初始化。</p>
<p>当用<code>const</code>声明对象时，这种行为却有所不同。虽然不能更新<code>const</code>对象，但是可以更新该对象的属性。 因此，如果我们声明一个<code>const</code>对象为</p>
<pre><code class="language-javascript">const greeting = {
    message: 'say Hi',
    times: 4,
};
</code></pre>
<p>同样不能像下面这样做:</p>
<pre><code class="language-javascript">const greeting = {
    words: 'Hello',
    number: 'five',
}; // error:  Assignment to constant variable.
</code></pre>
<p>但我们可以这样做:</p>
<pre><code class="language-javascript">greeting.message = 'say Hello instead';
</code></pre>
<p>这将更新<code>greeting.message</code>的值，而不会返回错误。</p>
<h3 id="const">const 的变量提升</h3>
<p>就像<code>let</code>一样，<code>const</code>声明也被提升到顶部，但是没有初始化。</p>
<p>最后，我们总结一下它们的异同：</p>
<ul>
<li><code>var</code>声明是全局作用域或函数作用域，而<code>let</code>和<code>const</code>是块作用域。</li>
<li><code>var</code>变量可以在其范围内更新和重新声明； <code>let</code>变量可以被更新但不能重新声明； <code>const</code>变量既不能更新也不能重新声明。</li>
<li>它们都被提升到其作用域的顶端。但是，虽然使用变量<code>undefined</code>初始化了<code>var</code>变量，但未初始化<code>let</code>和<code>const</code>变量。</li>
<li>尽管可以在不初始化的情况下声明<code>var</code>和<code>let</code>，但是在声明期间必须初始化<code>const</code>。</li>
</ul>
<p>有任何疑问或补充吗？请告诉我。</p>
<p>谢谢你的阅读:)</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/">Var, Let, and Const – What's the Difference?</a>，作者：Sarah Chima Atuonwu</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 教程——如何创建，更新和遍历 JavaScript 对象数组 ]]>
                </title>
                <description>
                    <![CDATA[ 我每周平均处理 JSON 数据 18 次，但几乎每次我都需要 Google 搜索特定的方法，如果有一本终极指南可以为我提供答案就好了😏 在本文中，我将向你展示在 JavaScript 中使用对象数组的基础知识。 如果你曾经使用过 JSON 结构，那么你就已经使用过 JavaScript 对象。从字面意思看，JSON 代表 JavaScript Object Notation。 创建一个对象很简单: {   "color": "purple",   "type": "minivan",   // 注册日期   "registration": new Date('2012-02-03'),   //载人数   "capacity": 7 } 该对象代表一辆汽车。汽车可以有多种类型和颜色，并且每个对象代表一辆特定的汽车。   [https://camo.githubusercontent.com/0f87e713dc43964d32f89f2fd286ada628bd8a97e101074b5513cb61e9b27af8/68747470733a2f2f7777772e66 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-array-of-objects-tutorial/</link>
                <guid isPermaLink="false">5fd1e4e439641a0517d51b91</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 数组 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 古月 ]]>
                </dc:creator>
                <pubDate>Thu, 10 Dec 2020 09:50:35 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/12/marc-mintel-Q-ioK6NPFos-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>我每周平均处理 JSON 数据 18 次，但几乎每次我都需要 Google 搜索特定的方法，如果有一本终极指南可以为我提供答案就好了😏</p>
<p>在本文中，我将向你展示在 JavaScript 中使用对象数组的基础知识。</p>
<p>如果你曾经使用过 JSON 结构，那么你就已经使用过 JavaScript 对象。从字面意思看，JSON 代表 <strong>JavaScript Object Notation</strong>。</p>
<p>创建一个对象很简单:</p>
<pre><code class="language-js">{
  "color": "purple",
  "type": "minivan",
  // 注册日期
  "registration": new Date('2012-02-03'),
  //载人数
  "capacity": 7
}
</code></pre>
<p>该对象代表一辆汽车。汽车可以有多种类型和颜色，并且每个对象代表一辆特定的汽车。</p>
<p><a href="https://camo.githubusercontent.com/0f87e713dc43964d32f89f2fd286ada628bd8a97e101074b5513cb61e9b27af8/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f707572706c652e706e67"><img src="https://camo.githubusercontent.com/0f87e713dc43964d32f89f2fd286ada628bd8a97e101074b5513cb61e9b27af8/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f707572706c652e706e67" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f707572706c652e706e67" width="600" height="400" loading="lazy"></a></p>
<p>大多数时候你都是从外部服务获取此类数据，但是有时你需要手动创建对象及其数组，就像我创建这个电子商店时一样：</p>
<p><a href="https://camo.githubusercontent.com/8545cd33171f33d7c6786d10c47f500b572e84376791f54ebdd1b2e49275704d/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f63617465676f726965732e6a7067"><img src="https://camo.githubusercontent.com/8545cd33171f33d7c6786d10c47f500b572e84376791f54ebdd1b2e49275704d/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f63617465676f726965732e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f63617465676f726965732e6a7067" width="600" height="400" loading="lazy"></a></p>
<p>每个类别列表项在 HTML 中看起来像这样:</p>
<p><a href="https://camo.githubusercontent.com/08776bf8fb1d8f01faa25b942bc7593b2ec582f62bcc068ba0e8c4ac060bc2e8/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636f64652e6a7067"><img src="https://camo.githubusercontent.com/08776bf8fb1d8f01faa25b942bc7593b2ec582f62bcc068ba0e8c4ac060bc2e8/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636f64652e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636f64652e6a7067" width="600" height="400" loading="lazy"></a></p>
<p>但我不想将此代码重复 12 次，因为这很难维护。</p>
<h2 id=""><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E6%95%B0%E7%BB%84"></a>创建对象数组</h2>
<p>让我们回到汽车的话题上来。我们来看看这些车：</p>
<p><a href="https://camo.githubusercontent.com/3b762da933964451f8d60bb437a509d115e662d0581c86821cd47fbb0e7e3738/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732e6a7067"><img src="https://camo.githubusercontent.com/3b762da933964451f8d60bb437a509d115e662d0581c86821cd47fbb0e7e3738/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732e6a7067" width="600" height="400" loading="lazy"></a></p>
<p>我们用数组表示它们:</p>
<pre><code class="language-js">let cars = [
  {
    "color": "purple",
    "type": "minivan",
    "registration": new Date('2017-01-03'),
    "capacity": 7
  },
  {
    "color": "red",
    "type": "station wagon",
    "registration": new Date('2018-03-03'),
    "capacity": 5
  },
  {
    ...
  },
  ...
]
</code></pre>
<p>对象数组并非始终保持不变，我们总是需要操作它们。因此，让我们看一下如何将新对象添加到已经存在的数组中。</p>
<h3 id="arrayunshift"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E5%9C%A8%E5%A4%B4%E9%83%A8%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%96%B0%E5%AF%B9%E8%B1%A1---arrayunshift"></a>在头部添加一个新对象 - Array.unshift</h3>
<p><a href="https://camo.githubusercontent.com/759c65a6790b2eb4687199d6de1b98a10d64186b8604f05bb4ddfb6a9a7771cc/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f626567696e6e696e672e6a7067"><img src="https://camo.githubusercontent.com/759c65a6790b2eb4687199d6de1b98a10d64186b8604f05bb4ddfb6a9a7771cc/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f626567696e6e696e672e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f626567696e6e696e672e6a7067" width="600" height="400" loading="lazy"></a></p>
<p>在头部添加一个对象, 使用 <code>Array.unshift</code>。</p>
<pre><code class="language-js">let car = {
    color: 'red',
    type: 'cabrio',
    registration: new Date('2016-05-02'),
    capacity: 2,
};
cars.unshift(car);
</code></pre>
<h3 id="arraypush"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E5%9C%A8%E5%B0%BE%E9%83%A8%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%96%B0%E5%AF%B9%E8%B1%A1---arraypush"></a>在尾部添加一个新对象 - Array.push</h3>
<p><a href="https://camo.githubusercontent.com/da848e3cac4507a8633e4afb745dd0875f1444f40f50c6536b5482b80c2b292e/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f656e64696e672e6a7067"><img src="https://camo.githubusercontent.com/da848e3cac4507a8633e4afb745dd0875f1444f40f50c6536b5482b80c2b292e/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f656e64696e672e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f656e64696e672e6a7067" width="600" height="400" loading="lazy"></a></p>
<p>添加一个对象到尾部的位置，使用  <code>Array.push</code>。</p>
<pre><code class="language-js">let car = {
    color: 'red',
    type: 'cabrio',
    registration: new Date('2016-05-02'),
    capacity: 2,
};
cars.push(car);
</code></pre>
<h3 id="arraysplice"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%96%B0%E5%AF%B9%E8%B1%A1%E5%88%B0%E4%B8%AD%E9%97%B4%E4%BD%8D%E7%BD%AE---arraysplice"></a>添加一个新对象到中间位置 - Array.splice</h3>
<p><a href="https://camo.githubusercontent.com/cf2a35e196d60dbd87bfb343d69a42915d0c758933150701bb7d6dd5a1280ffe/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f6d6964646c652e6a7067"><img src="https://camo.githubusercontent.com/cf2a35e196d60dbd87bfb343d69a42915d0c758933150701bb7d6dd5a1280ffe/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f6d6964646c652e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f6d6964646c652e6a7067" width="600" height="400" loading="lazy"></a></p>
<p>添加一个对象到中间位置，使用  <code>Array.splice</code>。此方法非常方便，因为它也可以删除数组的某一个元素。注意其参数:</p>
<pre><code class="language-js">Array.splice(
  {index where to start},
  {how many items to remove},
  {items to add}
);
</code></pre>
<p>因此，如果我们要在第五个位置添加红色敞篷车，可以像下面这样写：</p>
<pre><code class="language-js">let car = {
    color: 'red',
    type: 'cabrio',
    registration: new Date('2016-05-02'),
    capacity: 2,
};
cars.splice(4, 0, car);
</code></pre>
<h2 id=""><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E5%AF%B9%E8%B1%A1%E6%95%B0%E7%BB%84%E7%9A%84%E9%81%8D%E5%8E%86"></a>对象数组的遍历</h2>
<p>JavaScript 提供了许多函数/方法，可以解决你的某些问题，从而无需你手动实现其内部逻辑。我们来看看吧。</p>
<h3 id="arrayfind"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E9%80%9A%E8%BF%87%E5%85%B6%E5%80%BC%E5%9C%A8%E6%95%B0%E7%BB%84%E4%B8%AD%E6%9F%A5%E6%89%BE%E7%89%B9%E5%AE%9A%E7%9A%84%E5%AF%B9%E8%B1%A1---arrayfind"></a>通过其值在数组中查找特定的对象 - Array.find</h3>
<p>如果想在车辆数组中找到一辆红色的，我们可以使用  <code>Array.find</code>。</p>
<p><a href="https://camo.githubusercontent.com/d15ec6d4176855ca19b5538c6e17184c97bcfa8222ea27c1d512b0d66f3071cf/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d636f6c6f727265642e6a7067"><img src="https://camo.githubusercontent.com/d15ec6d4176855ca19b5538c6e17184c97bcfa8222ea27c1d512b0d66f3071cf/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d636f6c6f727265642e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d636f6c6f727265642e6a7067" width="600" height="400" loading="lazy"></a></p>
<pre><code class="language-js">let car = cars.find((car) =&gt; car.color === 'red');
</code></pre>
<p>这个函数返回匹配到的第一个元素：</p>
<pre><code class="language-js">console.log(car);
// output:
// {
//   color: 'red',
//   type: 'station wagon',
//   registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//   capacity: 5
// }
</code></pre>
<p>也可以搜索多个值：</p>
<p><code>let car = cars.find(car =&gt; car.color === "red" &amp;&amp; car.type === "cabrio");</code></p>
<p>在这种情况下，我们将获得车辆列表中的最后一辆车。</p>
<h3 id="arrayfilter"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E4%BB%8E%E6%95%B0%E7%BB%84%E4%B8%AD%E8%8E%B7%E5%8F%96%E7%AC%A6%E5%90%88%E6%9D%A1%E4%BB%B6%E7%9A%84%E5%A4%9A%E4%B8%AA%E5%85%83%E7%B4%A0---arrayfilter"></a>从数组中获取符合条件的多个元素 - Array.filter</h3>
<p><code>Array.find</code>  方法只返回一个对象。如果你想获取所有红色的车，你需要使用  <code>Array.filter</code>。</p>
<p><a href="https://camo.githubusercontent.com/0ed2b4d4c16ebdb54f71aef0d994dad0faf586fdf60d28793ac59b92a2d0326c/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d636f6c6f72726564322e6a7067"><img src="https://camo.githubusercontent.com/0ed2b4d4c16ebdb54f71aef0d994dad0faf586fdf60d28793ac59b92a2d0326c/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d636f6c6f72726564322e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d636f6c6f72726564322e6a7067" width="600" height="400" loading="lazy"></a></p>
<pre><code class="language-js">let redCars = cars.filter((car) =&gt; car.color === 'red');
console.log(redCars);
// output:
// [
//   {
//     color: 'red',
//     type: 'station wagon',
//     registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 5
//   },
//   {
//     color: 'red',
//     type: 'cabrio',
//     registration: 'Sat Mar 03 2012 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 2
//   }
// ]
</code></pre>
<h3 id="arraymap"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E8%BD%AC%E6%8D%A2%E6%95%B0%E7%BB%84%E7%9A%84%E5%AF%B9%E8%B1%A1---arraymap"></a>转换数组的对象 - Array.map</h3>
<p>在数组的所有方法中，<code>Array.map</code>方法可以说是我们使用最频繁的了。将对象数组转换为不同对象的数组，这就是  <code>Array.map</code>  的工作。假设我们要根据汽车的大小将其分为三类：</p>
<p><a href="https://camo.githubusercontent.com/60c8bb237070e5e555c393300ab467edc7fe9f3313a649996104fb966f1404d9/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d73697a65732e6a7067"><img src="https://camo.githubusercontent.com/60c8bb237070e5e555c393300ab467edc7fe9f3313a649996104fb966f1404d9/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d73697a65732e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d73697a65732e6a7067" width="600" height="400" loading="lazy"></a></p>
<pre><code class="language-js">let sizes = cars.map((car) =&gt; {
    if (car.capacity &lt;= 3) {
        return 'small';
    }
    if (car.capacity &lt;= 5) {
        return 'medium';
    }
    return 'large';
});
console.log(sizes);
// output:
// ['large','medium','medium', ..., 'small']
</code></pre>
<p>如果我们需要更多的值，也可以创建一个新的对象：</p>
<pre><code class="language-js">let carsProperties = cars.map((car) =&gt; {
    let properties = {
        capacity: car.capacity,
        size: 'large',
    };
    if (car.capacity &lt;= 5) {
        properties['size'] = 'medium';
    }
    if (car.capacity &lt;= 3) {
        properties['size'] = 'small';
    }
    return properties;
});
console.log(carsProperties);
// output:
// [
//   { capacity: 7, size: 'large' },
//   { capacity: 5, size: 'medium' },
//   { capacity: 5, size: 'medium' },
//   { capacity: 2, size: 'small' },
//   ...
// ]
</code></pre>
<h3 id="arrayforeach"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E5%90%91%E6%95%B0%E7%BB%84%E7%9A%84%E6%AF%8F%E4%B8%AA%E5%AF%B9%E8%B1%A1%E6%B7%BB%E5%8A%A0%E5%B1%9E%E6%80%A7---arrayforeach"></a>向数组的每个对象添加属性 - Array.forEach</h3>
<p><code>Array.map</code>会生成新的数组。那如果我们只想对原来的汽车对象做修改怎么办？这对<code>Array.forEach</code>函数来说是一个很好的使用场景：</p>
<pre><code class="language-js">cars.forEach((car) =&gt; {
    car['size'] = 'large';
    if (car.capacity &lt;= 5) {
        car['size'] = 'medium';
    }
    if (car.capacity &lt;= 3) {
        car['size'] = 'small';
    }
});
</code></pre>
<h3 id="arraysort"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E6%8C%89%E5%B1%9E%E6%80%A7%E5%AF%B9%E6%95%B0%E7%BB%84%E6%8E%92%E5%BA%8F---arraysort"></a>按属性对数组排序 - Array.sort</h3>
<p>完成对象转换后，通常需要以某种方式对它们进行排序。</p>
<p>通常，排序是基于每个对象都具有的属性的值。我们可以使用<code>Array.sort</code>函数，但是我们需要提供一个定义排序机制的函数（compareFunction）。</p>
<p>译者注：</p>
<blockquote>
<p>如果指明了 compareFunction，那么数组会按照调用该函数的返回值排序，即 a 和 b 是两个将要被比较的元素：</p>
<ul>
<li>如果 compareFunction(a, b) 小于 0 ，那么 a 会被排列到 b 之前。</li>
<li>如果 compareFunction(a, b) 等于 0 ， a 和 b 的相对位置不变。备注： ECMAScript 标准并不保证这一行为，而且也不是所有浏览器都会遵守（例如 Mozilla 在 2003 年之前的版本）。</li>
<li>如果 compareFunction(a, b) 大于 0 ， b 会被排列到 a 之前。</li>
<li>compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果，否则排序的结果将是不确定的。</li>
</ul>
</blockquote>
<p>假设我们要根据汽车的载人量对汽车进行降序排序。</p>
<p><a href="https://camo.githubusercontent.com/fb03ffaa4af9db42c778efc252224bfc4d6e87d1388a9d4b132705e4a3d8046e/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d736f72742e6a7067"><img src="https://camo.githubusercontent.com/fb03ffaa4af9db42c778efc252224bfc4d6e87d1388a9d4b132705e4a3d8046e/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d736f72742e6a7067" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f636172732d736f72742e6a7067" width="600" height="400" loading="lazy"></a></p>
<pre><code class="language-js">let sortedCars = cars.sort((c1, c2) =&gt;
    c1.capacity &lt; c2.capacity ? 1 : c1.capacity &gt; c2.capacity ? -1 : 0
);
console.log(sortedCars);
// output:
// [
//   {
//     color: 'purple',
//     type: 'minivan',
//     registration: 'Wed Feb 01 2017 00:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 7
//   },
//   {
//     color: 'red',
//     type: 'station wagon',
//     registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 5
//   },
//   ...
// ]
</code></pre>
<p>如果排序函数的结果为正，则<code>Array.sort</code>比较两个对象，并将第一个对象放在第二位。因此，你可以将排序函数视为一个问题：是否将第一个对象放置在第二个位置。</p>
<p><a href="https://camo.githubusercontent.com/8c1b41541e6d3adfddd76e5bd6bf20279de4d851b639eb52f2d67ef28b27ebb9/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f736f72742e706e67"><img src="https://camo.githubusercontent.com/8c1b41541e6d3adfddd76e5bd6bf20279de4d851b639eb52f2d67ef28b27ebb9/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f736f72742e706e67" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30352f736f72742e706e67" width="600" height="400" loading="lazy"></a></p>
<p>确保两个对象的比较值相同时始终将大小比较写为零，以避免不必要的交换。</p>
<h3 id="arrayeveryarrayincludes"><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E6%A3%80%E6%9F%A5%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E5%AF%B9%E8%B1%A1%E6%98%AF%E5%90%A6%E6%BB%A1%E8%B6%B3%E6%9D%A1%E4%BB%B6---arrayevery-arrayincludes"></a>检查数组中的对象是否满足条件 - Array.every，Array.includes</h3>
<p>当我们只需要检查每个对象的特定条件时，可使用<code>Array.every</code>和<code>Array.some</code>。</p>
<p>汽车清单上有红色敞篷车吗？所有的汽车都能载至少 4 人吗？......</p>
<pre><code class="language-js">cars.some((car) =&gt; car.color === 'red' &amp;&amp; car.type === 'cabrio');
// output: true

cars.every(car =&gt; car.capacity &gt;= 4);
// output: false
</code></pre>
<p>你可能还记得函数<code>Array.includes</code>与<code>Array.some</code>类似，但只有元素是原始类型的时候，二者才类似。</p>
<h2 id=""><a href="https://github.com/freeCodeCamp/news-translation/blob/78a4cf97f0eabfa9c8517c671c5d01d057617f85/chinese/articles/javascript-array.md#%E6%80%BB%E7%BB%93"></a>总结</h2>
<p>在本文中，我们介绍了一些基本函数/方法，这些方法可帮助你创建、操作、转换和遍历对象数组，它们应该涵盖了你遇到的大多数数据处理场景。</p>
<!--kg-card-end: markdown--><p>如果你遇到更复杂的数组使用场景，请查看 <a href="https://www.freecodecamp.org/news/data-structures-101-arrays-a-visual-introduction-for-beginners-7f013bcc355a/">freeCodeCamp 的这篇数组详细指南</a>或参考 <a href="https://www.w3schools.com/Jsref/jsref_obj_array.asp">W3Schools 的这篇资料</a>。</p><p>或者与我联系（<a href="https://twitter.com/ondrabus">Twitter@ondrabus</a>），我可以准备另外一篇文章：-)</p><p>原文：<a href="https://www.freecodecamp.org/news/javascript-array-of-objects-tutorial-how-to-create-update-and-loop-through-objects-using-js-array-methods/">JavaScript Array of Objects Tutorial – How to Create, Update, and Loop Through Objects Using JS Array Methods</a>，作者：Ondrej Polesny</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ TypeScript 高级类型清单（附 demo） ]]>
                </title>
                <description>
                    <![CDATA[ TypeScript 是一种类型化的语言，允许你指定变量的类型，函数参数，返回的值和对象属性。 你可以把本文看做一个带有示例的 TypeScript 高级类型备忘单 让我们开始吧！  * Intersection Types(交叉类型)  * Union Types(联合类型)  * Generic Types(泛型) * 泛型函数     * 泛型接口     * 多参数的泛型类型          * Utility Types * Partial     ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/advanced-typescript-types-cheatsheet/</link>
                <guid isPermaLink="false">5fd089d339641a0517d51b70</guid>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web开发 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 古月 ]]>
                </dc:creator>
                <pubDate>Mon, 07 Dec 2020 08:36:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/12/jessica-ruscello-OQSCtabGkSY-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>TypeScript 是一种类型化的语言，允许你指定变量的类型，函数参数，返回的值和对象属性。</p>
<p>你可以把本文看做一个带有示例的 TypeScript 高级类型备忘单</p>
<p>让我们开始吧！</p>
<ul>
<li><a href="#intersection-types%E4%BA%A4%E5%8F%89%E7%B1%BB%E5%9E%8B">Intersection Types(交叉类型)</a></li>
<li><a href="#union-types%E8%81%94%E5%90%88%E7%B1%BB%E5%9E%8B">Union Types(联合类型)</a></li>
<li><a href="#generic-types%E6%B3%9B%E5%9E%8B">Generic Types(泛型)</a>
<ul>
<li><a href="#%E6%B3%9B%E5%9E%8B%E5%87%BD%E6%95%B0">泛型函数</a></li>
<li><a href="#%E6%B3%9B%E5%9E%8B%E6%8E%A5%E5%8F%A3">泛型接口</a></li>
<li><a href="#%E5%A4%9A%E5%8F%82%E6%95%B0%E7%9A%84%E6%B3%9B%E5%9E%8B%E7%B1%BB%E5%9E%8B">多参数的泛型类型</a></li>
</ul>
</li>
<li><a href="#utility-types">Utility Types</a>
<ul>
<li><a href="#partial">Partial</a></li>
<li><a href="#required">Required</a></li>
<li><a href="#readonly">Readonly</a></li>
<li><a href="#pick">Pick</a></li>
<li><a href="#omit">Omit</a></li>
<li><a href="#extract">Extract</a></li>
<li><a href="#exclude">Exclude</a></li>
<li><a href="#record">Record</a></li>
<li><a href="#nonnullable">NonNullable</a></li>
</ul>
</li>
<li><a href="#mapped-types-%E6%98%A0%E5%B0%84%E7%B1%BB%E5%9E%8B">Mapped Types( 映射类型)</a></li>
<li><a href="#type-guards%E7%B1%BB%E5%9E%8B%E4%BF%9D%E6%8A%A4">Type Guards(类型保护)</a>
<ul>
<li><a href="#typeof">typeof</a></li>
<li><a href="#instanceof">instanceof</a></li>
<li><a href="#in">in</a></li>
</ul>
</li>
<li><a href="#conditional-types%E6%9D%A1%E4%BB%B6%E7%B1%BB%E5%9E%8B">Conditional Types(条件类型)</a></li>
</ul>
<h2 id="intersectiontypes">Intersection Types(交叉类型)</h2>
<p>交叉类型是一种将多种类型组合为一种类型的方法。 这意味着你可以将给定的类型 A 与类型 B 或更多类型合并，并获得具有所有属性的单个类型。</p>
<pre><code class="language-typescript">type LeftType = {
    id: number;
    left: string;
};

type RightType = {
    id: number;
    right: string;
};

type IntersectionType = LeftType &amp; RightType;

function showType(args: IntersectionType) {
    console.log(args);
}

showType({ id: 1, left: 'test', right: 'test' });
// Output: {id: 1, left: "test", right: "test"}
</code></pre>
<p>如你所见，<code>IntersectionType</code>组合了两种类型-<code>LeftType</code>和<code>RightType</code>，并使用<code>＆</code>符号形成了交叉类型。</p>
<h2 id="uniontypes">Union Types(联合类型)</h2>
<p>联合类型使你可以赋予同一个变量不同的类型</p>
<pre><code class="language-typescript">type UnionType = string | number;

function showType(arg: UnionType) {
    console.log(arg);
}

showType('test');
// Output: test

showType(7);
// Output: 7
</code></pre>
<p>函数<code>showType</code>是一个联合类型函数，它接受字符串或者数字作为参数。</p>
<h2 id="generictypes">Generic Types(泛型)</h2>
<p>泛型类型是复用给定类型的一部分的一种方式。 它有助于捕获作为参数传递的类型 T。</p>
<blockquote>
<p>优点: 创建可重用的函数，一个函数可以支持多种类型的数据。 这样开发者就可以根据自己的数据类型来使用函数</p>
</blockquote>
<h3 id="">泛型函数</h3>
<pre><code class="language-typescript">function showType&lt;T&gt;(args: T) {
    console.log(args);
}

showType('test');
// Output: "test"

showType(1);
// Output: 1
</code></pre>
<p>如何创建泛型类型:需要使用<code>&lt;&gt;</code>并将 <code>T</code>(名称可自定义)作为参数传递。<br>
上面的 🌰 栗子中，<br>
我们给 <code>showType</code> 添加了类型变量 <code>T</code>。<code>T</code>帮助我们捕获用户传入的参数的类型(比如：number/string)之后我们就可以使用这个类型</p>
<p>我们把 <code>showType</code> 函数叫做泛型函数，因为它可以适用于多个类型</p>
<h3 id="">泛型接口</h3>
<pre><code class="language-typescript">interface GenericType&lt;T&gt; {
    id: number;
    name: T;
}

function showType(args: GenericType&lt;string&gt;) {
    console.log(args);
}

showType({ id: 1, name: 'test' });
// Output: {id: 1, name: "test"}

function showTypeTwo(args: GenericType&lt;number&gt;) {
    console.log(args);
}

showTypeTwo({ id: 1, name: 4 });
// Output: {id: 1, name: 4}
</code></pre>
<p>在上面的栗子中，声明了一个 <code>GenericType</code> 接口，该接口接收泛型类型 <code>T</code>, 并通过类型 <code>T</code>来约束接口内 <code>name</code> 的类型</p>
<blockquote>
<p>注:泛型变量约束了整个接口后，在实现的时候，必须指定一个类型</p>
</blockquote>
<p>因此在使用时我们可以将<code>name</code>设置为任意类型的值，示例中为字符串或数字</p>
<h3 id="">多参数的泛型类型</h3>
<pre><code class="language-typescript">interface GenericType&lt;T, U&gt; {
    id: T;
    name: U;
}

function showType(args: GenericType&lt;number, string&gt;) {
    console.log(args);
}

showType({ id: 1, name: 'test' });
// Output: {id: 1, name: "test"}

function showTypeTwo(args: GenericType&lt;string, string[]&gt;) {
    console.log(args);
}

showTypeTwo({ id: '001', name: ['This', 'is', 'a', 'Test'] });
// Output: {id: "001", name: Array["This", "is", "a", "Test"]}
</code></pre>
<p>泛型类型可以接收多个参数。 在上面的代码中，我们传入两个参数：<code>T</code>和<code>U</code>，然后将它们用作<code>id</code>,<code>name</code>的类型。 也就是说，我们现在可以使用该接口并提供不同的类型作为参数。</p>
<h2 id="utilitytypes">Utility Types</h2>
<p>TypeScript 内部也提供了很多方便实用的工具，可帮助我们更轻松地操作类型。 如果要使用它们，你需要将类型传递给<code>&lt;&gt;</code></p>
<h3 id="partial">Partial</h3>
<ul>
<li><code>Partial&lt;T&gt;</code></li>
</ul>
<p>Partial 允许你将<code>T</code>类型的所有属性设为可选。 它将在每一个字段后面添加一个<code>?</code>。</p>
<pre><code class="language-typescript">interface PartialType {
    id: number;
    firstName: string;
    lastName: string;
}

/*
等效于
interface PartialType {
  id?: number
  firstName?: string
  lastName?: string
}
*/

function showType(args: Partial&lt;PartialType&gt;) {
    console.log(args);
}

showType({ id: 1 });
// Output: {id: 1}

showType({ firstName: 'John', lastName: 'Doe' });
// Output: {firstName: "John", lastName: "Doe"}
</code></pre>
<p>上面代码中声明了一个<code>PartialType</code>接口，它用作函数<code>showType()</code>的参数的类型。 为了使所有字段都变为可选，我们使用<code>Partial</code>关键字并将<code>PartialType</code>类型作为参数传递。</p>
<h3 id="required">Required</h3>
<ul>
<li><code>Required&lt;T&gt;</code></li>
</ul>
<blockquote>
<p>将某个类型里的属性全部变为必选项</p>
</blockquote>
<pre><code class="language-typescript">interface RequiredType {
    id: number;
    firstName?: string;
    lastName?: string;
}

function showType(args: Required&lt;RequiredType&gt;) {
    console.log(args);
}

showType({ id: 1, firstName: 'John', lastName: 'Doe' });
// Output: { id: 1, firstName: "John", lastName: "Doe" }

showType({ id: 1 });
// Error: Type '{ id: number: }' is missing the following properties from type 'Required&lt;RequiredType&gt;': firstName, lastName
</code></pre>
<p>上面的代码中，即使我们在使用接口之前先将某些属性设为可选，但<code>Required</code>被加入后也会使所有属性成为必选。 如果省略某些必选参数，TypeScript 将报错。</p>
<h3 id="readonly">Readonly</h3>
<ul>
<li><code>Readonly&lt;T&gt;</code></li>
</ul>
<p>会转换<t>类型的所有属性，以使它们无法被修改</t></p>
<pre><code class="language-typescript">interface ReadonlyType {
    id: number;
    name: string;
}

function showType(args: Readonly&lt;ReadonlyType&gt;) {
    args.id = 4;
    console.log(args);
}

showType({ id: 1, name: 'Doe' });
// Error: Cannot assign to 'id' because it is a read-only property.
</code></pre>
<p>我们使用<code>Readonly</code>来使<code>ReadonlyType</code>的属性不可被修改。 也就是说，如果你尝试为这些字段之一赋予新值，则会引发错误。</p>
<p>除此之外，你还可以在指定的属性前面使用关键字<code>readonly</code>使其无法被重新赋值</p>
<pre><code class="language-typescript">interface ReadonlyType {
    readonly id: number;
    name: string;
}
</code></pre>
<h3 id="pick">Pick</h3>
<ul>
<li><code>Pick&lt;T, K&gt;</code></li>
</ul>
<p>此方法允许你从一个已存在的类型 <code>T</code>中选择一些属性作为<code>K</code>, 从而创建一个新类型</p>
<p>即 抽取一个类型/接口中的一些子集作为一个新的类型</p>
<p><code>T</code>代表要抽取的对象<br>
<code>K</code>有一个约束: 一定是来自<code>T</code>所有属性字面量的联合类型<br>
新的类型/属性一定要从<code>K</code>中选取，</p>
<pre><code class="language-typescript">/**
    源码实现
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick&lt;T, K extends keyof T&gt; = {
    [P in K]: T[P];
};
</code></pre>
<pre><code class="language-typescript">interface PickType {
    id: number;
    firstName: string;
    lastName: string;
}

function showType(args: Pick&lt;PickType, 'firstName' | 'lastName'&gt;) {
    console.log(args);
}

showType({ firstName: 'John', lastName: 'Doe' });
// Output: {firstName: "John"}

showType({ id: 3 });
// Error: Object literal may only specify known properties, and 'id' does not exist in type 'Pick&lt;PickType, "firstName" | "lastName"&gt;'
</code></pre>
<p><code>Pick</code> 与我们前面讨论的工具有一些不同，它需要两个参数</p>
<ul>
<li><code>T</code>是要从中选择元素的类型</li>
<li><code>K</code>是要选择的属性(可以使使用联合类型来选择多个字段)</li>
</ul>
<h3 id="omit">Omit</h3>
<ul>
<li><code>Omit&lt;T, K&gt;</code></li>
</ul>
<p><code>Omit</code>的作用与<code>Pick</code>类型正好相反。 不是选择元素，而是从类型<code>T</code>中删除<code>K</code>个属性。</p>
<pre><code class="language-typescript">interface PickType {
    id: number;
    firstName: string;
    lastName: string;
}

function showType(args: Omit&lt;PickType, 'firstName' | 'lastName'&gt;) {
    console.log(args);
}

showType({ id: 7 });
// Output: {id: 7}

showType({ firstName: 'John' });
// Error: Object literal may only specify known properties, and 'firstName' does not exist in type 'Pick&lt;PickType, "id"&gt;'
</code></pre>
<h3 id="extract">Extract</h3>
<ul>
<li><code>Extract&lt;T, U&gt;</code></li>
</ul>
<blockquote>
<p>提取<code>T</code>中可以赋值给<code>U</code>的类型--取交集</p>
</blockquote>
<p><code>Extract</code>允许你通过选择两种不同类型中的共有属性来构造新的类型。 也就是从<code>T</code>中提取所有可分配给<code>U</code>的属性。</p>
<pre><code class="language-typescript">interface FirstType {
    id: number;
    firstName: string;
    lastName: string;
}

interface SecondType {
    id: number;
    address: string;
    city: string;
}

type ExtractType = Extract&lt;keyof FirstType, keyof SecondType&gt;;
// Output: "id"
</code></pre>
<p>在上面的代码中，<code>FirstType</code>接口和<code>SecondType</code>接口，都存在 <code>id:number</code>属性。 因此，通过使用<code>Extract</code>，即提取出了新的类型 <code>{id:number}</code>。</p>
<h3 id="exclude">Exclude</h3>
<blockquote>
<p><code>Exclude&lt;T, U&gt;</code> --从 <code>T</code> 中剔除可以赋值给 <code>U</code> 的类型。</p>
</blockquote>
<p>与<code>Extract</code>不同，<code>Exclude</code>通过排除两个不同类型中已经存在的共有属性来构造新的类型。 它会从<code>T</code>中排除所有可分配给<code>U</code>的字段。</p>
<pre><code class="language-typescript">interface FirstType {
    id: number;
    firstName: string;
    lastName: string;
}

interface SecondType {
    id: number;
    address: string;
    city: string;
}

type ExcludeType = Exclude&lt;keyof FirstType, keyof SecondType&gt;;

// Output; "firstName" | "lastName"
</code></pre>
<p>上面的代码可以看到，属性<code>firstName</code>和<code>lastName</code> 在<code>SecondType</code>类型中不存在。 通过使用<code>Extract</code>关键字，我们可以获得<code>T</code>中存在而<code>U</code>中不存在的字段。</p>
<h3 id="record">Record</h3>
<ul>
<li><code>Record&lt;K,T&gt;</code></li>
</ul>
<p>此工具可帮助你构造具有给定类型<code>T</code>的一组属性<code>K</code>的类型。将一个类型的属性映射到另一个类型的属性时，<code>Record</code>非常方便。</p>
<pre><code class="language-typescript">interface EmployeeType {
    id: number;
    fullname: string;
    role: string;
}

let employees: Record&lt;number, EmployeeType&gt; = {
    0: { id: 1, fullname: 'John Doe', role: 'Designer' },
    1: { id: 2, fullname: 'Ibrahima Fall', role: 'Developer' },
    2: { id: 3, fullname: 'Sara Duckson', role: 'Developer' },
};

// 0: { id: 1, fullname: "John Doe", role: "Designer" },
// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }
</code></pre>
<p><code>Record</code>的工作方式相对简单。 在代码中，它期望一个<code>number</code>作为类型，这就是为什么我们将 0、1 和 2 作为<code>employees</code>变量的键的原因。 如果你尝试使用字符串作为属性，则会引发错误,因为属性是由<code>EmployeeType</code>给出的具有 ID，fullName 和 role 字段的对象。</p>
<h3 id="nonnullable">NonNullable</h3>
<ul>
<li><code>NonNullable&lt;T&gt;</code></li>
</ul>
<blockquote>
<p>-- 从 <code>T</code> 中剔除 <code>null</code> 和 <code>undefined</code></p>
</blockquote>
<pre><code class="language-typescript">type NonNullableType = string | number | null | undefined;

function showType(args: NonNullable&lt;NonNullableType&gt;) {
    console.log(args);
}

showType('test');
// Output: "test"

showType(1);
// Output: 1

showType(null);
// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.

showType(undefined);
// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.
</code></pre>
<p>我们将类型<code>NonNullableType</code>作为参数传递给<code>NonNullable</code>，<code>NonNullable</code>通过排除<code>null</code>和<code>undefined</code>来构造新类型。 也就是说，如果你传递可为空的值，TypeScript 将引发错误。</p>
<p>顺便说一句，如果将<code>--strictNullChecks</code>标志添加到<code>tsconfig文件</code>，TypeScript 将应用非空性规则。</p>
<h2 id="mappedtypes">Mapped Types( 映射类型)</h2>
<p>映射类型允许你从一个旧的类型，生成一个新的类型。</p>
<p>请注意，前面介绍的某些高级类型也是映射类型。<br>
如:</p>
<pre><code class="language-ts">/*
Readonly， Partial和 Pick是同态的，但 Record不是。 因为 Record并不需要输入类型来拷贝属性，所以它不属于同态：
*/
type Readonly&lt;T&gt; = {
    readonly [P in keyof T]: T[P];
};
type Partial&lt;T&gt; = {
    [P in keyof T]?: T[P];
};
type Pick&lt;T, K extends keyof T&gt; = {
    [P in K]: T[P];
};

Record;
</code></pre>
<pre><code class="language-typescript">type StringMap&lt;T&gt; = {
    [P in keyof T]: string;
};

function showType(arg: StringMap&lt;{ id: number; name: string }&gt;) {
    console.log(arg);
}

showType({ id: 1, name: 'Test' });
// Error: Type 'number' is not assignable to type 'string'.

showType({ id: 'testId', name: 'This is a Test' });
// Output: {id: "testId", name: "This is a Test"}
</code></pre>
<p><code>StringMap&lt;&gt;</code>会将传入的任何类型转换为字符串。 就是说，如果我们在函数<code>showType()</code>中使用它，则接收到的参数必须是字符串-否则，TypeScript 将引发错误。</p>
<h2 id="typeguards">Type Guards(类型保护)</h2>
<p>类型保护使你可以使用运算符检查变量或对象的类型。 这是一个条件块，它使用<code>typeof</code>，<code>instanceof</code>或<code>in</code>返回类型。</p>
<blockquote>
<p>typescript 能够在特定区块中保证变量属于某种确定类型。可以在此区块中放心地引用此类型的属性，或者调用此类型的方法</p>
</blockquote>
<h3 id="typeof">typeof</h3>
<pre><code class="language-typescript">function showType(x: number | string) {
    if (typeof x === 'number') {
        return `The result is ${x + x}`;
    }
    throw new Error(`This operation can't be done on a ${typeof x}`);
}

showType("I'm not a number");
// Error: This operation can't be done on a string

showType(7);
// Output: The result is 14
</code></pre>
<p>什么代码中，有一个普通的 JavaScript 条件块，通过<code>typeof</code>检查接收到的参数的类型。</p>
<h3 id="instanceof">instanceof</h3>
<pre><code class="language-typescript">class Foo {
    bar() {
        return 'Hello World';
    }
}

class Bar {
    baz = '123';
}

function showType(arg: Foo | Bar) {
    if (arg instanceof Foo) {
        console.log(arg.bar());
        return arg.bar();
    }

    throw new Error('The type is not supported');
}

showType(new Foo());
// Output: Hello World

showType(new Bar());
// Error: The type is not supported
</code></pre>
<p>像前面的示例一样，这也是一个类型保护，它检查接收到的参数是否是<code>Foo</code>类的一部分，并对其进行处理。</p>
<h3 id="in">in</h3>
<pre><code class="language-typescript">interface FirstType {
    x: number;
}
interface SecondType {
    y: string;
}

function showType(arg: FirstType | SecondType) {
    if ('x' in arg) {
        console.log(`The property ${arg.x} exists`);
        return `The property ${arg.x} exists`;
    }
    throw new Error('This type is not expected');
}

showType({ x: 7 });
// Output: The property 7 exists

showType({ y: 'ccc' });
// Error: This type is not expected
</code></pre>
<p>什么的栗子中，使用<code>in</code>检查参数对象上是否存在属性<code>x</code>。</p>
<h2 id="conditionaltypes">Conditional Types(条件类型)</h2>
<p>条件类型测试两种类型，然后根据该测试的结果选择其中一种。</p>
<blockquote>
<p>一种由条件表达式所决定的类型， 表现形式为 <code>T extends U ? X : Y</code> , 即如果类型<code>T</code>可以被赋值给类型<code>U</code>，那么结果类型就是<code>X</code>类型，否则为<code>Y</code>类型。</p>
<p>条件类型使类型具有了不唯一性，增加了语言的灵活性，</p>
</blockquote>
<pre><code class="language-typescript">// 源码实现
type NonNullable&lt;T&gt; = T extends null | undefined ? never : T;

// NotNull&lt;T&gt; 等价于 NoneNullable&lt;T,U&gt;

// 用法示例
type resType = NonNullable&lt;string | number | null | undefined&gt;; // string|number
</code></pre>
<p>上面的代码中， <code>NonNullable</code>检查类型是否为 <code>null</code>，并根据该类型进行处理。 正如你所看到的，它使用了 JavaScript 三元运算符。</p>
<p>感谢阅读。</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/react-typescript-how-to-set-up-types-on-hooks/">The React TypeScript Cheatsheet – How To Set Up Types on Hooks</a>，作者：<a href="https://www.freecodecamp.org/news/author/ibrahima92/">Ibrahima Ndaw</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 字符串反转的三种方式 ]]>
                </title>
                <description>
                    <![CDATA[ 反转字符串是技术面试中最常问到的 JavaScript 问题之一。 面试官可能会要求你使用不同的编码方式来反转字符串，或者他们可能会要求你不使用内置方法来反转字符串，甚至会要求你使用递归来反转字符串。 可能有数十种不同的方法可以执行此操作，但内置 reverse 方法除外，因为 JavaScript 的 String 对象上没有此方法。 以下是我解决 JavaScript 反转字符串问题的三种最有趣的方法。 算法要求 > 反转提供的字符串。 你可能需要将字符串转换为数组，然后才能将其反转。 你的结果必须是字符串。 function reverseString(str) {     return str; } reverseString("hello"); 提供测试用例  * reverseString(“hello”) 应该返回 “olleh”  * reverseString(“Howdy”) 应该返回 “ydwoH”  * reverseString(“Greetings from Earth”) 应该返回 ”htraE morf sgniteerG” ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-reverse-a-string-in-javascript-in-3-different-ways/</link>
                <guid isPermaLink="false">5fcca35639641a0517d519e8</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 字符串 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 递归 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 古月 ]]>
                </dc:creator>
                <pubDate>Sat, 05 Dec 2020 09:24:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/04/photo-1593642532744-d377ab507dc8-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><strong>反转字符串</strong>是技术面试中最常问到的 JavaScript 问题之一。 面试官可能会要求你使用不同的编码方式来反转字符串，或者他们可能会要求你不使用内置方法来反转字符串，甚至会要求你使用递归来反转字符串。</p><p>可能有数十种不同的方法可以执行此操作，但内置 <strong>reverse </strong>方法除外，因为 JavaScript 的 String 对象上没有此方法。</p><p>以下是我解决 JavaScript 反转字符串问题的三种最有趣的方法。</p><h4 id="-">算法要求</h4><blockquote>反转提供的字符串。<br><em>你可能需要将字符串转换为数组，然后才能将其反转。</em><br><em>你的结果必须是字符串。</em></blockquote><pre><code class="language-js">function reverseString(str) {
    return str;
}
reverseString("hello");</code></pre><h4 id="--1">提供测试用例</h4><ul><li><strong><em>reverseString(“hello”)</em></strong> &nbsp;应该返回 “olleh”</li><li><strong><em>reverseString(“Howdy”)</em></strong> &nbsp;应该返回 “ydwoH”</li><li><strong><em>reverseString(“Greetings from Earth”)</em></strong> &nbsp;应该返回 ”htraE morf sgniteerG”</li></ul><h3 id="1-">1. 使用内置方法反转字符串</h3><p>对于此解决方案，我们将使用三种方法：String.prototype.split() 方法，Array.prototype.reverse() 方法和 Array.prototype.join() 方法。</p><ul><li>split() 方法使用指定的分隔符字符串将一个 String 对象分割成子字符串数组，以一个指定的分割字串来决定每个拆分的位置</li><li>reverse() 方法将数组中元素的位置颠倒，并返回该数组。数组的第一个元素会变成最后一个，数组的最后一个元素变成第一个。该方法会改变原数组</li><li>join() 方法将一个数组（或一个类数组对象）的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目，那么将返回该项目而不使用分隔符</li></ul><pre><code class="language-js">function reverseString(str) {
    // Step 1. Use the split() method to return a new array
    var splitString = str.split(""); // var splitString = "hello".split("");
    // ["h", "e", "l", "l", "o"]
 
    // Step 2. Use the reverse() method to reverse the new created array
    var reverseArray = splitString.reverse(); // var reverseArray = ["h", "e", "l", "l", "o"].reverse();
    // ["o", "l", "l", "e", "h"]
 
    // Step 3. Use the join() method to join all elements of the array into a string
    var joinArray = reverseArray.join(""); // var joinArray = ["o", "l", "l", "e", "h"].join("");
    // "olleh"
    
    //Step 4. Return the reversed string
    return joinArray; // "olleh"
}
 
reverseString("hello");</code></pre><h4 id="--2">三个方法组合形成链式调用:</h4><pre><code>function reverseString(str) {
    return str.split("").reverse().join("");
}
reverseString("hello");</code></pre><h3 id="2-for-">2. 用递减的 for 循环反转字符串</h3><pre><code class="language-js">function reverseString(str) {
    // Step 1. Create an empty string that will host the new created string
    var newString = "";
 
    // Step 2. Create the FOR loop
    /* The starting point of the loop will be (str.length - 1) which corresponds to the 
       last character of the string, "o"
       As long as i is greater than or equals 0, the loop will go on
       We decrement i after each iteration */
    for (var i = str.length - 1; i &gt;= 0; i--) { 
        newString += str[i]; // or newString = newString + str[i];
    }
    /* Here hello's length equals 5
        For each iteration: i = str.length - 1 and newString = newString + str[i]
        First iteration:    i = 5 - 1 = 4,         newString = "" + "o" = "o"
        Second iteration:   i = 4 - 1 = 3,         newString = "o" + "l" = "ol"
        Third iteration:    i = 3 - 1 = 2,         newString = "ol" + "l" = "oll"
        Fourth iteration:   i = 2 - 1 = 1,         newString = "oll" + "e" = "olle"
        Fifth iteration:    i = 1 - 1 = 0,         newString = "olle" + "h" = "olleh"
    End of the FOR Loop*/
 
    // Step 3. Return the reversed string
    return newString; // "olleh"
}
 
reverseString('hello');</code></pre><h4 id="--3">删除注释:</h4><pre><code class="language-js">function reverseString(str) {
    var newString = "";
    for (var i = str.length - 1; i &gt;= 0; i--) {
        newString += str[i];
    }
    return newString;
}
reverseString('hello');</code></pre><h3 id="3-">3. 用递归反转字符串</h3><p>对于此解决方案，我们将使用两种方法：String.prototype.substr() 方法和 String.prototype.charAt() 方法</p><ul><li>substr() 方法返回一个字符串中从指定位置开始到指定字符数的字符.</li></ul><blockquote>译者注：尽管 String.prototype.substr(……) 没有严格被废弃 (as in "removed from the Web standards")，但它被认作是遗留的函数并且可以的话应该避免使用。它并非 JavaScript 核心语言的一部分，未来将可能会被移除掉。如果可以的话，使用 substring() 替代它。</blockquote><pre><code class="language-js">'hello'.substr(1); // "ello"
</code></pre><ul><li>charAt() 方法从一个字符串中返回指定的字符.</li></ul><pre><code class="language-js">'hello'.charAt(0); // "h"
</code></pre><p>递归的深度等于 String 的长度。 当 String 很长且堆栈大小是主要问题的时候，代码运行非常慢。所以此方案不是最佳解决方案</p><pre><code class="language-js">function reverseString(str) {
  if (str === "") // 如果传入空字符串，则直接返回它
    return "";
  else
    return reverseString(str.substr(1)) + str.charAt(0);
/*
递归方法的第一部分
你需要记住不会只有一次回调，会存在多次嵌套回调
每次回调的公式: str === "?"                         reverseString(str.subst(1))     + str.charAt(0)
1st call – reverseString("Hello")   will return   reverseString("ello")           + "h"
2nd call – reverseString("ello")    will return   reverseString("llo")            + "e"
3rd call – reverseString("llo")     will return   reverseString("lo")             + "l"
4th call – reverseString("lo")      will return   reverseString("o")              + "l"
5th call – reverseString("o")       will return   reverseString("")               + "o"
递归方法的第二部分
该方法达一旦到if条件，嵌套最深的调用会立即返回
</code></pre><h4 id="--4">删除注释:</h4><pre><code class="language-js">function reverseString(str) {
    if (str === '') return '';
    else return reverseString(str.substr(1)) + str.charAt(0);
}
reverseString('hello');
</code></pre><h4 id="--5">使用三元表达式:</h4><pre><code class="language-js">function reverseString(str) {
    return str === '' ? '' : reverseString(str.substr(1)) + str.charAt(0);
}
reverseString('hello');
</code></pre><p><strong>JavaScript 字符串反转</strong> &nbsp;是一种小型且简单的算法，在电话技术筛查或技术面试中都可能被问到。 你可以采用最简单的方式解决此问题，也可以采用递归或更复杂的解决方案来解决。</p><p>希望本篇文章对你有所帮助。 这是我的“如何解决 FCC 算法”系列文章的一部分，下面几篇文章是其他算法的了一些解决方案。</p><p><a href="https://www.freecodecamp.org/news/three-ways-to-repeat-a-string-in-javascript-2a9053b93a2d/"><strong>在 JavaScript 中实现重复字符串的三种方法</strong></a><br><a href="https://www.freecodecamp.org/news/three-ways-to-repeat-a-string-in-javascript-2a9053b93a2d/"><em>在本文中，我将说明如何解决 freeCodeCamp 的“重复字符串”挑战.</em></a></p><p><a href="https://www.freecodecamp.org/news/two-ways-to-confirm-the-ending-of-a-string-in-javascript-62b4677034ac/"><strong>在 JavaScript 中判断字符串是否结束的两种方法</strong></a><br><a href="https://www.freecodecamp.org/news/two-ways-to-confirm-the-ending-of-a-string-in-javascript-62b4677034ac/"><em>在本文中，我将说明如何解决 freeCodeCamp 的“确定结束”挑战.</em></a></p><p><a href="https://www.freecodecamp.org/news/how-to-factorialize-a-number-in-javascript-9263c89a4b38/"><strong>在 JavaScript 中实现数字阶乘的三种方法</strong></a><br><a href="https://www.freecodecamp.org/news/how-to-factorialize-a-number-in-javascript-9263c89a4b38/"><em>在本文中，我将说明如何解决 freeCodeCamp 的“数字阶乘”挑战”</em></a></p><p><a href="https://www.freecodecamp.org/news/two-ways-to-check-for-palindromes-in-javascript-64fea8191fd7/"><strong>在 JavaScript 中判断回文字符串的两种方法</strong></a><br><a href="https://www.freecodecamp.org/news/two-ways-to-check-for-palindromes-in-javascript-64fea8191fd7/"><em>在本文中，我将说明如何解决 freeCodeCamp 的“检查回文字符串”挑战”.</em></a></p><p><a href="https://www.freecodecamp.org/news/three-ways-to-find-the-longest-word-in-a-string-in-javascript-a2fb04c9757c/"><strong>在 JavaScript 中查找字符串中最长单词的三种方法</strong></a><br><a href="https://www.freecodecamp.org/news/three-ways-to-find-the-longest-word-in-a-string-in-javascript-a2fb04c9757c/"><em>在本文中，我将说明如何解决 freeCodeCamp 的“检查字符串中的最长单词”挑战”.</em></a></p><p><a href="https://www.freecodecamp.org/news/three-ways-to-title-case-a-sentence-in-javascript-676a9175eb27/"><strong>用 JavaScript 对句子加标题的三种方法</strong></a><br><a href="https://www.freecodecamp.org/news/three-ways-to-title-case-a-sentence-in-javascript-676a9175eb27/"><em>在本文中，我将说明如何解决 freeCodeCamp 的“字符串中每个单词首字母转为大写”挑战”.</em></a></p><p>如果你有自己的解决方案或任何建议，欢迎在评论中留言.</p><p>或者，你也可以在 <a href="https://medium.com/@sonya.moisset">Medium</a>，<a href="https://twitter.com/SonyaMoisset">Twitter</a>，<a href="https://github.com/SonyaMoisset">GitHub</a> 和 <a href="https://www.linkedin.com/in/sonyamoisset">LinkedIn</a> 上关注我；-)</p><p>＃保持好奇，＃持续前进和＃实现目标！</p><h3 id="--6">资源</h3><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split">split() method — MDN</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse">reverse() method — MDN</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join">join() method — MDN</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length">String.length — MDN</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr">substr() method — MDN</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt">charAt() method — MDN</a></li></ul><p>原文：<a href="https://www.freecodecamp.org/news/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb/">Three Ways to Reverse a String in JavaScript</a>，作者：Sonya Moisset</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 什么是 npm —— 写给初学者的编程教程 ]]>
                </title>
                <description>
                    <![CDATA[ 前言 本篇文章可以作为 npm（Node.js 最喜欢的伙伴）的一个基本学习指南。 自 2009 年以来，Node.js 一直席卷全球。成千上万个系统基于 Node.js 构建，促使开发者在社区宣称“JavaScript 正在吞噬软件”。 Node 成功的主要因素之一是它广受欢迎的软件包管理器——npm，因为 npm 使 JavaScript 开发人员可以快速方便地共享软件包， 例如 lodash [https://www.npmjs.com/package/lodash] 和 moment [https://www.npmjs.com/package/moment]。 在我撰写这篇文章时，npm 已帮助发布了 130 万个软件包，每周下载量超过 160 亿次！ 这些数字对于任何软件工具来说都非常厉害。 所以，现在让我们讨论一下 npm 到底是什么。 NPM 是什么 npm（“Node 包管理器”）是 JavaScript 运行时 Node.js 的默认程序包管理器。 它也被称为“Ninja Pumpkin Mutants”，“Nonprofit Pizza Makers”， ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-is-npm-a-node-package-manager-tutorial-for-beginners/</link>
                <guid isPermaLink="false">5f84642c5f583f0565090aa6</guid>
                
                    <category>
                        <![CDATA[ NPM ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 古月 ]]>
                </dc:creator>
                <pubDate>Mon, 12 Oct 2020 13:11:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/10/cover-4.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="-">前言</h2><p>本篇文章可以作为 npm（Node.js 最喜欢的伙伴）的一个基本学习指南。</p><p>自 2009 年以来，Node.js 一直席卷全球。成千上万个系统基于 Node.js 构建，促使开发者在社区宣称“JavaScript 正在吞噬软件”。</p><p>Node 成功的主要因素之一是它广受欢迎的软件包管理器——npm，因为 npm 使 JavaScript 开发人员可以快速方便地共享软件包， 例如 <a href="https://www.npmjs.com/package/lodash">lodash</a> &nbsp;和 <a href="https://www.npmjs.com/package/moment">moment</a>。</p><p>在我撰写这篇文章时，npm 已帮助发布了 130 万个软件包，每周下载量超过 160 亿次！ 这些数字对于任何软件工具来说都非常厉害。 所以，现在让我们讨论一下 npm 到底是什么。</p><h2 id="npm-">NPM 是什么</h2><p>npm（“Node 包管理器”）是 JavaScript 运行时 Node.js 的默认程序包管理器。</p><p>它也被称为“Ninja Pumpkin Mutants”，“Nonprofit Pizza Makers”，以及许多其他随机名称，你可以在 &nbsp;<a href="https://github.com/npm/npm-expansions">npm-expansions</a> &nbsp;上探索这些名称。</p><p>npm 由两个主要部分组成:</p><ul><li>用于发布和下载程序包的 CLI（命令行界面）工具</li><li>托管 JavaScript 程序包的 &nbsp;<a href="https://www.npmjs.com/">在线存储库</a></li></ul><p>为了更直观地解释，我们可以将存储库 &nbsp;<a href="https://npmjs.com/">npmjs.com</a> &nbsp;视为一个物流集散中心，该中心从卖方（npm 包裹的作者）那里接收货物的包裹，并将这些货物分发给买方（npm 包裹的用户）。</p><p>为了促进此过程，<a href="https://npmjs.com/">npmjs.com</a> &nbsp;物流集散中心雇用了一群勤劳的袋熊（npm CLI），他们将被分配给每个 &nbsp;<a href="https://npmjs.com/">npmjs.com</a> &nbsp;用户作为私人助理。 因此，dependencies（依赖项）会如下传递给 JavaScript 开发人员：</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/26f5c4e6537732f5e6a5c36c4f0f54ebefd70f19/68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30362f776f6d6261742d696e7374616c6c2e706e67" class="kg-image" alt="68747470733a2f2f7777772e66726565636f646563616d702e6f72672f6e6577732f636f6e74656e742f696d616765732f323032302f30362f776f6d6261742d696e7374616c6c2e706e67" width="600" height="400" loading="lazy"></figure><p>发布 JS 软件包的过程如下：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/10/image-5.png" class="kg-image" alt="image-5" width="600" height="400" loading="lazy"></figure><p>让我们看看这只袋熊如何协助想要在项目中使用 JavaScript 包的开发人员。下面我们还将看到它们（npm CLI）如何帮助开源向导将其出色的库推向世界。</p><h2 id="package-json">package.json</h2><p>每个 JavaScript 项目（无论是 Node.js 还是浏览器应用程序）都可以被当作 npm 软件包，并且通过 &nbsp;<code>package.json</code> &nbsp;来描述项目和软件包信息。</p><p>我们可以将 &nbsp;<code>package.json</code> &nbsp;视为快递盒子上的运输信息。</p><p>当运行 &nbsp;<code>npm init</code> &nbsp;初始化 JavaScript/Node.js 项目时，将生成 &nbsp;<code>package.json</code> &nbsp;文件，文件内的内容(基本元数据)由开发人员提供：</p><ul><li><code>name</code>：JavaScript 项目或库的名称。</li><li><code>version</code>：项目的版本。通常，在应用程序开发中，由于没有必要对开源库进行版本控制，因此经常忽略这一块。但是，仍可以用它来定义版本。</li><li><code>description</code>：项目的描述。</li><li><code>license</code>：项目的许可证。</li></ul><h3 id="npm-scripts">npm scripts</h3><p><code>package.json</code> &nbsp;还支持一个 &nbsp;<code>scripts</code> &nbsp;属性，可以把它当作在项目本地运行的命令行工具。例如，一个 npm 项目的 &nbsp;<code>scripts</code>部分可能看起来像这样：</p><pre><code class="language-json">{
  "scripts": {
    "build": "tsc",
    "format": "prettier --write **/*.ts",
    "format-check": "prettier --check **/*.ts",
    "lint": "eslint src/**/*.ts",
    "pack": "ncc build",
    "test": "jest",
    "all": "npm run build &amp;&amp; npm run format &amp;&amp; npm run lint &amp;&amp; npm run pack &amp;&amp; npm test"
  }
}
</code></pre><p><code>eslint</code>，<code>prettier</code>，<code>ncc</code>，<code>jest</code> &nbsp;不是安装为全局可执行文件，而是安装在项目本地的 <code>node_modules/.bin/</code> 中。</p><p>最新引入的 &nbsp;<a href="https://www.freecodecamp.org/news/npm-vs-npx-whats-the-difference/">npx</a> &nbsp;使我们可以像在全局安装程序一样运行这些 &nbsp;<code>node_modules</code> &nbsp;项目作用域命令，方法是在其前面加上 &nbsp;<code>npx ...</code>（即<code>npx prettier --write ** / *。ts</code>）。</p><h3 id="dependencies-vs-devdependencies">dependencies vs devDependencies</h3><p>这两个以键值对象的形式出现，其中 npm 库的名称为键，其<a href="https://semver.org/">语义格式</a>版本为值。 大家可以看看 <a href="https://github.com/actions/typescript-action">Github 的 TypeScript 操作模板</a>中的示例：</p><pre><code class="language-json">{
  "dependencies": {
    "@actions/core": "^1.2.3",
    "@actions/github": "^2.1.1"
  },
  "devDependencies": {
    "@types/jest": "^25.1.4",
    "@types/node": "^13.9.0",
    "@typescript-eslint/parser": "^2.22.0",
    "@zeit/ncc": "^0.21.1",
    "eslint": "^6.8.0",
    "eslint-plugin-github": "^3.4.1",
    "eslint-plugin-jest": "^23.8.2",
    "jest": "^25.1.0",
    "jest-circus": "^25.1.0",
    "js-yaml": "^3.13.1",
    "prettier": "^1.19.1",
    "ts-jest": "^25.2.1",
    "typescript": "^3.8.3"
  }
}</code></pre><p>这些依赖通过带有 &nbsp;<code>--save</code> &nbsp;或 &nbsp;<code>--save-dev</code> &nbsp;标志的 &nbsp;<code>npm install</code> &nbsp;命令安装。 它们分别用于生产和开发/测试环境。 在下一节中，我们将更深入地研究这些软件包的安装。</p><p>同时，理解语义版本前面的符号非常重要（假设你已经阅读 <a href="https://semver.org/">semver</a> 的 &nbsp;<code>major.minor.patch</code> &nbsp;模型）：</p><ul><li><code>^</code>：表示最新的次版本，例如， <code>^1.0.4</code> &nbsp;可能会安装主版本系列 &nbsp;<code>1</code> &nbsp;的最新次版本 <code>1.3.0</code>。</li><li><code>〜</code>：表示最新的补丁程序版本，与 &nbsp;<code>^</code> &nbsp;类似， <code>〜1.0.4</code> &nbsp;可能会安装次版本系列 <code>1.0</code> &nbsp;的最新次版本<code>1.0.7</code>。</li></ul><p>所有这些确切的软件包版本都将记录在 &nbsp;<code>package-lock.json</code> &nbsp;文件中。</p><h3 id="package-lock-json">package-lock.json</h3><p>该文件描述了 npm JavaScript 项目中使用的依赖项的确切版本。如果 &nbsp;<code>package.json</code> &nbsp;是通用的描述性标签，则 &nbsp;<code>package-lock.json</code> &nbsp;是成分表。</p><p>就像我们通常不会读取食品包装袋上的成分表（除非你太无聊或需要知道）一样，<code>package-lock.json</code> &nbsp;并不会被开发人员一行一行进行读取。</p><p><code>package-lock.json</code> &nbsp;通常是由 &nbsp;<code>npm install</code> &nbsp;命令生成的，也可以由我们的 NPM CLI 工具读取，以确保使用 &nbsp;<code>npm ci</code> &nbsp;复制项目的构建环境。</p><h2 id="-npm">用户如何使用 NPM</h2><p>从前面提到的 130 万个发布的软件包中，有 160 亿次下载，可以推断出，大多数 npm 用户都朝这个方向使用 npm。所以，了解如何使用这个强大的工具会很有帮助。</p><h3 id="npm-install">npm install</h3><p>这是现在我们开发 JavaScript/Node.js 应用程序时最常用的命令。</p><p>默认情况下，<code>npm install &lt;package-name&gt;</code> &nbsp;将安装带有 &nbsp;<code>^</code> &nbsp;版本号的软件包的最新版本。npm 项目上下文中的 &nbsp;<code>npm install</code> &nbsp;将根据 &nbsp;<code>package.json</code> &nbsp;规范将软件包下载到项目的 &nbsp;<code>node_modules</code> &nbsp;文件夹中，从而升级软件包的版本（并重新生成 &nbsp;<code>package-lock.json</code> &nbsp;）。 <code>npm install &lt;package-name&gt;</code> &nbsp;可以基于 &nbsp;<code>^</code> &nbsp;和 &nbsp;<code>〜</code> &nbsp;版本匹配。</p><p>如果要在全局上下文中安装程序包，可以在机器的任何地方使用它，则可以指定全局标志 &nbsp;<code>-g</code>（例如 &nbsp;<a href="https://github.com/tapio/live-server">live-server</a>）。</p><p>npm 使安装 JavaScript 软件包非常容易，以至于经常错误地使用此命令。 导致一些程序员对 npm 开这样的玩笑：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/10/image-6.png" class="kg-image" alt="image-6" width="600" height="400" loading="lazy"></figure><p>但是，npm 包太大、太深这样的问题可以通过 &nbsp;<code>--production</code> &nbsp;标志来拯救！在上一节中，我们讨论了分别用于生产和开发/测试环境的 &nbsp;<code>dependencies</code> &nbsp;和 &nbsp;<code>devDependencies</code> &nbsp;。 这个 &nbsp;<code>--production</code> &nbsp;标志是如何在 &nbsp;<code>node_modules</code> &nbsp;中进行区别的。</p><p>通过将此标志附加到 &nbsp;<code>npm install</code> &nbsp;命令，我们将仅从 &nbsp;<code>dependencies</code> &nbsp;安装软件包，从而将 &nbsp;<code>node_modules</code> &nbsp;的大小大大减小到应用程序正常运行所必需的大小。——不应该将 &nbsp;<code>devDependencies</code> &nbsp;引入生产环境！</p><h3 id="npm-ci">npm ci</h3><p>因此，如果 &nbsp;<code>npm install --production</code> &nbsp;对于生产环境是最佳选项，那么是否必须有一个对本地环境，测试环境最合适的选项？</p><p>答案是 &nbsp;<code>npm ci</code>。</p><p>就像如果 &nbsp;<code>package_lock.json</code> &nbsp;尚不存在于项目中一样，无论何时调用 &nbsp;<code>npm install</code> &nbsp;都会生成它，<code>npm ci</code> &nbsp;会消耗该文件来下载项目所依赖的每个软件包的确切版本。 </p><p>这样，无论是用于本地开发的笔记本电脑还是 Github Actions 等 CI（持续集成）构建环境，我们都可以确保项目上下文在不同机器上保持完全相同。</p><h3 id="npm-audit">npm audit</h3><p>随着越来越多的软件包发布，并且易于安装，因此 npm 软件包容易受到恶意作者的恶意攻击，例如<a href="https://medium.com/@jsoverson/how-two-malicious-npm-packages-targeted-sabotaged-one-other-fed7199099c8">这些</a>。</p><p>意识到生态系统存在问题，npm.js 组织提出了 &nbsp;<code>npm audit</code> &nbsp;的<a href="https://blog.npmjs.org/post/173719309445/npm-audit-identify-and-fix-insecure">主意</a>。 他们维护了一个安全漏洞列表，开发人员可以使用 &nbsp;<code>npm audit</code> &nbsp;命令来审核项目中的依赖项。</p><p><code>npm audit</code> &nbsp;为开发人员提供了有关漏洞以及是否有要修复的版本的信息，例如：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/10/image-8.png" class="kg-image" alt="image-8" width="600" height="400" loading="lazy"></figure><p>如果补救措施在下一个不间断的版本升级中可用，则可以使用 &nbsp;<code>npm audit fix</code> &nbsp;来自动升级受影响的依赖项的版本。</p><h2 id="-npm-1">作者如何使用 NPM</h2><p>我们已经了解了作为用户，如何通过 NPM CLI 有效得使用 NPM，但是作为作者又如何使用呢？</p><h3 id="npm-publish">npm publish</h3><p>将软件包发送到 &nbsp;<a href="https://npmjs.com/">npmjs.com</a> &nbsp;非常容易，因为我们只需要运行 &nbsp;<code>npm publish</code> &nbsp;。 棘手的部分（并非专门针对 npm 软件包作者）是确定软件包的版本。</p><p>根据 &nbsp;<a href="https://semver.org/">semver.org</a> &nbsp;的经验法则：</p><ol><li>当你进行不兼容的 API 更改时使用 MAJOR 版本</li><li>以向后兼容的方式添加功能时使用 MINOR 版本</li><li>进行向后兼容的 bug 修复时使用 PATCH 版本</li></ol><p>在发布软件包时，遵循上述规则尤为重要，可以确保你不会破坏任何人的代码，因为 npm 中匹配的默认版本是<code>^</code>（又称下一个次版本）。</p><h2 id="-npm-javascript-node-js-">❤️ &nbsp;npm &nbsp;❤️ &nbsp;JavaScript &nbsp;❤️ &nbsp;Node.js &nbsp;❤️</h2><p>了解了以上知识，我们就可以开始有效地使用 npm 并指挥可爱的袋熊大军啦！</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/10/image-9.png" class="kg-image" alt="image-9" width="600" height="400" loading="lazy"></figure><p>原文：<a href="https://www.freecodecamp.org/news/what-is-npm-a-node-package-manager-tutorial-for-beginners/">What is npm? A Node Package Manager Tutorial for Beginners</a>，作者：Stanley Nguyen</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 了解 JavaScript 模块系统基础知识，搭建自己的库 ]]>
                </title>
                <description>
                    <![CDATA[ 我想很多前端工程师都听说过 “JavaScript 模块”，那你们都知道如何处理它，以及它在日常工作中如何发挥作用吗？ JS 模块系统到底是什么呢？ 随着 JavaScript 开发越来越广泛，命名空间和依赖项变得越来越难以处理，极客们早已经开发出不同的模块系统解决方案来解决该问题。 为什么理解 JS 模块系统很重要 我的日常工作是设计和项目架构，并且我很快意识到跨项目需要许多通用功能。我总是一次又一次地将这些功能复制粘贴到新项目中。 问题是，每当更改一部分代码时，我都需要在所有项目中手动同步这些更改。为了避免所有这些繁琐的手动任务，我决定提取通用功能并从中组成一个 NPM 软件包 。这样，团队中的其他人将能够将它们重新用作依赖项，并在每次推出新版本时都可以对其进行更新。 这种方法具有一些优点：  * 如果核心库中有一些更改，则只需在一个地方进行更改，而无需为同一件事重构所有应用程序的代码。  * 所有应用程序保持同步。无论何时进行更改，所有应用程序仅需要运行npm update 命令。 库的源码因此，下一步是发布库 。 这是最困难的部分，因为我脑海中突然跳出一堆东西，例 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/anatomy-of-js-module-systems-and-building-libraries/</link>
                <guid isPermaLink="false">5f74118e027c3105323f5642</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 库 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web开发 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 古月 ]]>
                </dc:creator>
                <pubDate>Wed, 30 Sep 2020 05:17:59 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/09/1_TAApMtvwRDbQ3dTU4bAg7Q.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>我想很多前端工程师都听说过 “JavaScript 模块”，那你们都知道如何处理它，以及它在日常工作中如何发挥作用吗？</p><h2 id="js-">JS 模块系统到底是什么呢？</h2><p>随着 JavaScript 开发越来越广泛，命名空间和依赖项变得越来越难以处理，极客们早已经开发出不同的<strong>模块系统</strong>解决方案来解决该问题。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/cbe170880d2306f31cbfa79d036fae246a6037bf/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f706e672f3338323530342f313630313236333335313332392d32643436613362332d633864322d346438632d396664332d6139313864636636353332352e706e67" class="kg-image" alt="image.png" width="600" height="400" loading="lazy"></figure><h2 id="-js-">为什么理解 JS 模块系统很重要</h2><p>我的日常工作是设计和项目架构，并且我很快意识到跨项目需要许多通用功能。我总是一次又一次地将这些功能复制粘贴到新项目中。</p><p>问题是，每当更改一部分代码时，我都需要在所有项目中手动同步这些更改。为了避免所有这些繁琐的手动任务，我决定<strong>提取通用功能并从中组成一个 NPM 软件包</strong>。这样，团队中的其他人将能够将它们重新用作依赖项，并在每次推出新版本时都可以对其进行更新。</p><p>这种方法具有一些优点：</p><ul><li>如果核心库中有一些更改，则只需在一个地方进行更改，而无需为同一件事重构所有应用程序的代码。</li><li>所有应用程序保持同步。无论何时进行更改，所有应用程序仅需要运行 &nbsp;<code>npm update</code> &nbsp;命令。</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://camo.githubusercontent.com/07ecc51384c74e0c3fd824710cc6ad6b54632160/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f706e672f3338323530342f313630313236333731303136312d62623163383163622d336661332d346630612d623635322d3265383931613830316334382e706e67" class="kg-image" alt="68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f706e672f3338323530342f313630313236333731303136312d62623163383163622d336661332d346630612d623635322d3265383931613830316334382e706e67" width="600" height="400" loading="lazy"><figcaption>库的源码</figcaption></figure><p>因此，下一步是<strong>发布库</strong> &nbsp;。</p><p>这是最困难的部分，因为我脑海中突然跳出一堆东西，例如：</p><ol><li>如何使用摇树优化</li><li>应该针对哪些 JS 模块系统（CommonJS、AMD、ES modules）</li><li>需要转译源码吗</li><li>需要打包源码吗</li><li>应该发布哪些文件</li></ol><p>在发布第三方库（组件库，工具库）时，我们每个人的脑海中都应该冒出这些问题。</p><p>来， 我们一步步解决以上的问题。</p><h2 id="-js--1">不同类型的 JS 模块系统</h2><h3 id="1-commonjs">1. CommonJS</h3><ul><li>由 <strong>Node.js</strong> 实现</li><li>多用在<strong>服务器端</strong>安装模块时</li><li>没有 runtime/async 模块</li><li>通过 &nbsp;<code>require</code> &nbsp;导入模块</li><li>通过 &nbsp;<code>module.exports</code> &nbsp;导出模块</li><li>无法使用摇树优化，因为当你导入时会得到一个模块时，得到的是一个对象，所以属性查找在运行时进行，无法静态分析</li><li>会得到一个对象的副本，因此模块本身<strong>不会实时更改</strong></li><li>循环依赖的不能优雅处理</li><li>语法简单</li></ul><h3 id="2-amd-">2. AMD 异步模块定义</h3><ul><li>由 <strong>RequireJs</strong> 实现</li><li>当你在<strong>客户端（浏览器）环境</strong>中，异步加载模块时使用</li><li>通过 &nbsp;<code>require</code> &nbsp;实现导入</li><li>语法复杂</li></ul><h3 id="3-umd-">3. UMD 通用模块定义</h3><ul><li><strong>CommonJs + AMD</strong> 的组合（即 CommonJs 的语法 + AMD 的异步加载）</li><li>可以用于 <strong>AMD/CommonJs</strong> 环境</li><li>UMD 还支持全局变量定义，因此，UMD 模块能够在<strong>客户端和服务器</strong>上工作。</li></ul><h3 id="4-es-modules">4. ES modules</h3><ul><li>用于<strong>服务器/客户端</strong></li><li>支持模块的 &nbsp;<strong>Runtime/static loading</strong></li><li>当你导入时，获得是<strong>实际对象</strong></li><li>通过 &nbsp;<code>import</code> &nbsp;导入，通过 &nbsp;<code>export</code> &nbsp;导出</li><li><strong>静态分析</strong>——你可以决定编译时的导入和导出（静态），你只需要看源码，不需要执行它</li></ul><p>You can determine imports and exports at compile time (statically) — you only have to look at the source code, you don’t have to execute it</p><ul><li>由于 ES6 支持<strong>静态分析</strong> &nbsp;，因此摇树优化是可行的</li><li>始终获取<strong>实际值</strong> ，以便<strong>实时更改模块本身</strong></li><li>比 CommonJS 有更好的循环依赖管理</li></ul><p>现在，我们了解了不同类型的 JS 模块系统以及它们如何演变。</p><p>尽管所有工具和现代浏览器都支持 <strong>ES modules</strong> ，但我们在发布库时不知道用户如何利用我们的库。 因此，我们必须确保我们的库在所有环境中都能正常工作。</p><p>让我们深入研究并设计一个示例库，更好地回答与发布库有关的所有问题。</p><p>我已经建立了一个小型的 UI 库（你可以在 <a href="https://github.com/kamleshchandnani/js-module-system">GitHub</a> 上找到源代码），并且我将分享我在编译，打包和发布中的所有经验和探索。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://camo.githubusercontent.com/f0dc3b9fa2a3c6f121b6323b4b5cd9107c2ecc41/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f706e672f3338323530342f313630313238373933373630392d66636234643734322d653236362d343263332d623562632d6633323763376630626230622e706e67" class="kg-image" alt="68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f706e672f3338323530342f313630313238373933373630392d66636234643734322d653236362d343263332d623562632d6633323763376630626230622e706e67" width="600" height="400" loading="lazy"><figcaption>目录结构</figcaption></figure><p>在这里，我们有一个小的 UI 库，其中包含 3 个组件：Button，Card 和 NavBar。 让我们一步步进行编译并发布。</p><h2 id="-">发布前的最佳实践</h2><h3 id="1-tree-shaking-">1. 摇树优化(Tree Shaking)</h3><p><a href="https://www.webpackjs.com/guides/tree-shaking/">webpack 官方文档有说明</a></p><ul><li>摇树优化是一个术语，通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的<a href="http://exploringjs.com/es6/ch_modules.html#static-module-structure">静态结构特性</a>，例如 &nbsp;<code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">import</a></code> &nbsp;和 &nbsp;<code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export">export</a></code>。这个术语和概念实际上是兴起于 ES2015 模块打包工具 &nbsp;<a href="https://github.com/rollup/rollup">rollup</a>。新的 webpack 4 正式版本，扩展了这个检测能力，通过 &nbsp;<code>package.json</code> &nbsp;的 &nbsp;<code>"sideEffects"</code> &nbsp;属性作为标记，向 compiler 提供提示，表明项目中的哪些文件是纯的 ES2015 模块，由此可以安全地删除文件中未使用的部分。</li><li>webpack 和 Rollup 都支持摇树优化，这意味着我们需要牢记某些事情，以便我们的代码可被 Tree Shaking。</li></ul><h3 id="2-">2. 发布所有模块形态</h3><ul><li>我们应该发布所有模块形态，例如 <strong>UMD</strong> 和 <strong>ES Module</strong> ，因为我们永远不知道用户在哪个版本的浏览器或 webpack 中使用此库/包。</li><li>即使所有打包程序（如 <strong>webpack</strong> 和 <strong>Rollup</strong>）都能解析 ES Module ，但如果我们的使用者使用的是 webpack 1.x，则它无法解析 ES 模块。</li></ul><pre><code class="language-json">// package.json
{
  "name": "js-module-system",
  "version": "0.0.1",

  "main": "dist/index.js",
  "module": "dist/index.es.js"
}
</code></pre><ul><li><strong>package.json</strong> 文件的 <strong>main</strong> 字段通常用于指向 <strong>UMD</strong> 版本的库/包。</li><li><strong>package.jso</strong> 文件的 <strong>module</strong> 字段用于指向 <strong>ES</strong> 版本的库/包。</li></ul><blockquote><strong>鲜为人知的事实</strong>：webpack 使用 <a href="https://webpack.docschina.org/configuration/resolve/#resolvemainfieldshttps://webpack.js.org/configuration/resolve/#resolve-mainfields">resolve.mainfields</a> 确定检查 &nbsp;<code>package.json</code> &nbsp;中的哪些字段。</blockquote><blockquote><strong>性能提示</strong>：由于所有现代浏览器现在都支持 <strong>ES 模块</strong> ，因此也请务必发布 <strong>ES 版本</strong>的库/包。这样一来，可以减少编译次数，最终可以减少向用户交付的代码。 这将提高应用程序的性能。</blockquote><p>那么，下一步是什么？ 编译还是打包？ 我们应该使用什么工具？ 啊，这是最棘手的部分！让我们深入研究研究。</p><h2 id="webpack-vs-rollup-vs-babel">webpack vs Rollup vs Babel</h2><p>这些我们在日常工作中使用的工具，用于承载我们的应用程序/库/软件包。没有它们，我无法想象现代的 Web 开发有多么糟糕。因此，我们无法将它们进行比较 &nbsp;❌</p><p>每种工具都有其自身的优势，并根据使用者的需求达到不同的目的。</p><p>现在让我们看一下这些工具：</p><h3 id="webpack">webpack</h3><p><a href="https://webpack.docschina.org/concepts/">webpack</a>是一个很棒的模块打包工具， 它被广泛接受并且主要用于构建 SPA。它提供了开箱即用的所有功能，例如<a href="https://webpack.docschina.org/guides/code-splitting/">代码拆分</a>、<a href="https://webpack.docschina.org/guides/code-splitting/#dynamic-imports">按需加载</a>、<a href="https://webpack.docschina.org/guides/tree-shaking/">摇树优化</a>等，并且它本身使用的是 <strong>CommonJS</strong> 模块系统。</p><h3 id="rollupjs">RollupJS</h3><p><a href="https://rollupjs.org/guide/en/">RollupJS</a> 还是类似于 webpack 的模块打包器。但是，RollupJS 的主要优点是它遵循 ES6 修订版中包含的代码模块的新标准化格式，因此你可以使用它来打包 <strong>ES module variant</strong> 的 library/package。但它不支持 <a href="https://webpack.docschina.org/guides/code-splitting/#dynamic-imports">按需加载</a>。</p><h3 id="babel">Babel</h3><p><a href="https://babeljs.io/docs/en/">Babel</a> 是 JavaScript 的编译器，以将 ES6 代码转换为可在你的浏览器（或服务器）中运行的代码而闻名。请记住，它只是编译而不会打包你的代码。</p><p><strong>我的建议：对库使用 Rollup.js，对应用程序使用 webpack。</strong></p><h2 id="-babel-ify-">编译（Babel-ify）源代码还是直接打包源代码</h2><p>在构建我的 NPM 库时，我花费了大量时间来试图找出该问题（如何编译、如何打包）的答案。我开始挖掘自己的 node_modules，查找所有优秀的库并检查它们的构建系统。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://camo.githubusercontent.com/fdd9de4cec5cf501e7d161c0963ade651b65ea9c/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f6a7065672f3338323530342f313630313330373938333731372d34623864363831332d653134302d343038322d623964652d6364336231353065313035362e6a706567" class="kg-image" alt="68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f6a7065672f3338323530342f313630313330373938333731372d34623864363831332d653134302d343038322d623964652d6364336231353065313035362e6a706567" width="600" height="400" loading="lazy"><figcaption>对比 libraries/packages 搭建的输出</figcaption></figure><p>在查看了不同 libraries/packages 的构建输出之后，我清楚地了解了这些库的作者在发布之前可能会想到的不同策略。 以下是我的观察。</p><p>如你在上图中所看到的，我已根据它们的特性将这些库/软件包分为两组：</p><ul><li>UI Libraries-UI 库（<code>styled-components</code>, &nbsp;<code>material-ui</code>）</li><li>Core Packages-核心包（<code>react</code>，<code>react-dom</code>）</li></ul><p>你可能已经弄清楚了这两组之间的区别。</p><p><strong>UI Libraries</strong></p><ul><li>有一个 <strong>dist</strong> 文件夹，该文件夹是针对 <strong>ES 和 UMD/CJS 模块系统</strong> &nbsp;的打包和压缩版本。</li><li>有一个 <strong>lib</strong> 文件夹，用来存放被编译后的代码。</li></ul><p><strong>Core Packages</strong></p><ul><li>只有一个文件夹，其中包含针对 <strong>CJS 或 UMD 模块系统</strong>的打包和压缩版本。</li></ul><p>但是，为什么 UI Libraries 和 Core Packages 的构建输出有所不同？？</p><h3 id="ui-libraries">UI Libraries</h3><p>想象一下，如果我们只是发布库的 bundled version 将其托管在 CDN 上，我们的用户将直接在 &nbsp;<code>&lt;script /&gt;</code> &nbsp;标记中使用它。现在，如果使用者只想使用 &nbsp;<code>&lt;Button /&gt;</code> &nbsp;组件，则他们必须加载整个库。另外，在浏览器中，没有可以解决 tree shaking 的打包工具，最终我们会将整个库代码发送给我们的使用者。 因此，我们不能像如下代码引入整个库文件。</p><pre><code class="language-html">&lt;script type="module"&gt;
  import { Button } from "https://unpkg.com/uilibrary/index.js";
&lt;/script&gt;
</code></pre><p>现在，如果我们只是简单地将 src 转换为 lib 并将该 lib 托管在 CDN 上，那么我们的使用者实际上可以得到他们想要的任何东西而没有任何开销，“代码更少，加载更快”。 &nbsp;✅</p><pre><code class="language-html">&lt;script type="module"&gt;
  import { Button } from "https://unpkg.com/uilibrary/lib/button.js";
&lt;/script&gt;
</code></pre><h3 id="core-packages">Core Packages</h3><p>Core Packages（核心包）永远不会通过 &nbsp;<code>&lt;script /&gt;</code> &nbsp;标记使用，因为它们必须是主应用程序的一部分。因此，我们可以安全地发布这些软件包的构建版本（ &nbsp;<strong>UMD，ES</strong>），并将构建后的系统交给用户使用。</p><p>例如，他们可以使用 &nbsp;<strong>UMD</strong> &nbsp;而不使用摇树优化，或者如果打包器能够识别并获得摇树优化的好处，则可以使用 &nbsp;<strong>ES</strong> &nbsp;。</p><pre><code class="language-js">// CJS require
const Button = require("uilibrary/button");
// ES import
import {Button} from "uilibrary";
</code></pre><p>但是……关于我们的问题：should we &nbsp;<strong>transpile (Babelify) the source or bundle it</strong>？？</p><p><strong>对于 UI 库</strong></p><ul><li>当我们针对 <strong>es</strong> 模块系统构建时，需要 <strong>Babel</strong> 编译源代码，并将编译后的代码放置在 <strong>lib</strong> 文件夹中。我们甚至可以将 lib 托管在 CDN 上。</li><li>当我们针对 <strong>cjs/umd</strong> 模块系统和 <strong>es</strong> 模块系统 等多个模块系统构建时，需要 &nbsp;<strong>rollup</strong> &nbsp;📦 &nbsp;打包和压缩代码。</li></ul><p>下面我们修改 &nbsp;<code>package.json</code> &nbsp;以指向对应的模块系统。</p><pre><code class="language-json">// package.json
{
  "name": "js-module-system",
  "version": "0.0.1",
  // for umd/cjs builds
  "main": "dist/index.js",
  // for es build
  "module": "dist/index.es.js"
}
</code></pre><p><strong>对于 core packages</strong>，我们不需要 <strong>lib</strong> 版本。我们只需要针对 <strong>cjs/umd</strong> 模块系统和 <strong>es</strong> 模块系统，使用 <strong>rollup</strong> 进行 &nbsp;📦 &nbsp;打包和压缩源代码即可。</p><p>提示：对于愿意通过 &nbsp;<code>&lt;script /&gt;</code> &nbsp;标记下载整个库/软件包的用户，我们也可以在 CDN 上托管 <strong>dist 文件夹</strong> &nbsp;。</p><h2 id="--1">我们怎么进行打包</h2><p>我们应在在 &nbsp;<code>package.json</code> &nbsp;中为了不同的目的编写不同的脚本。你能在 GitHub 上面找到 Rollup 的一些配置——<a href="https://github.com/kamleshchandnani/js-module-system/blob/master/rollup.config.js">rollup config</a></p><pre><code class="language-json">// package.json
{
  "scripts": {
    "clean": "rimraf dist",
    "build": "run-s clean &amp;&amp; run-p build:es build:cjs build:lib:es",
    "build:es": "NODE_ENV=es rollup -c",
    "build:cjs": "NODE_ENV=cjs rollup -c",
    "build:lib:es": "BABEL_ENV=es babel src -d lib"
  }
}
</code></pre><h2 id="--2">我们应该发布哪些东西</h2><ul><li>License</li><li>README</li><li>Changelog</li><li>Metadata（ &nbsp;<code>"main"</code> &nbsp;, &nbsp;<code>"module"</code> &nbsp;, &nbsp;<code>"bin"</code> &nbsp;）— &nbsp;<strong>package.json</strong></li><li>Control through &nbsp;<code>package.json</code> &nbsp;<code>"files"</code> &nbsp;property</li></ul><p>在 &nbsp;<code>package.json</code> &nbsp;中， <code>"files"</code> &nbsp;字段是一个数组类型 ，用来表示软件包被当做第三方依赖安装时，都有哪些文件或文件夹需要下载到业务项目中。如果你在数组中加入了一个文件夹，那么在你 &nbsp;<code>npm install</code> &nbsp;时，文件夹及下面的文件都会被下载。</p><p>在我的示例项目中，我在 &nbsp;<code>"files"</code> &nbsp;中加入了 <strong>lib</strong> 和 <strong>dist</strong> 文件夹。</p><pre><code class="language-json">// package.json
{
  "files": ["dist", "lib"]
}
</code></pre><p>最后，终于可以准备发布了。只需在终端中键入 &nbsp;<code>npm run build</code> &nbsp;命令，你就看到以下输出。仔细查看 <strong>dist</strong> 和 <strong>lib</strong> 文件夹都有哪些东西。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://camo.githubusercontent.com/0e5613070eaa5e8ef55545e28689eb6fe2dd56b2/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f706e672f3338323530342f313630313331303338383731352d39303634653934372d336134362d343463662d386365342d3732386234643532313238372e706e67" class="kg-image" alt="68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032302f706e672f3338323530342f313630313331303338383731352d39303634653934372d336134362d343463662d386365342d3732386234643532313238372e706e67" width="600" height="400" loading="lazy"><figcaption>可以发布了？</figcaption></figure><h2 id="--3">总结</h2><p>至此，我们已经了解了 JavaScript 模块系统以及如何创建自己的库并发布它。下面是一些注意事项：</p><ol><li>是否可以启用摇树优化</li><li>至少需要构建 &nbsp;<strong>ES modules</strong> &nbsp;and &nbsp;<strong>CommonJS</strong> &nbsp;两种模块系统</li><li>使用 Babel 和 Bundlers 搭建 libraries</li><li>使用 Bundlers 搭建 Core packages</li><li>在 &nbsp;<code>package.json</code> &nbsp;中使用 &nbsp;<code>"module"</code> &nbsp;字段 来构建 es 模块的版本 (PS: 这有助于使用摇树).</li><li>发布已编译的文件夹以及模块的编译版</li></ol><p>原文：<a href="https://www.freecodecamp.org/news/anatomy-of-js-module-systems-and-building-libraries-fadcd8dbd0e/">Learn the basics of the JavaScript module system and build your own library</a>，作者：Kamlesh Chandnani</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
