<?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[ wangzili - 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[ wangzili - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 19:37:53 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/wangzili/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 中的词法作用域——JS 中的作用域究竟是什么？ ]]>
                </title>
                <description>
                    <![CDATA[ 词法作用域（lexical scope）  的概念不好理解，也很难掌握。但如果我们可以搞清楚概念里每一个词的意思，对于理解词法作用域将大有帮助。 因此本文将分别解释“词法（lexical）”和“作用域（scope）”这两个概念，然后再解释什么是词法作用域。 让我们开始吧~~ 什么是作用域 作用域  表示一个区间，在这个区间内声明的所有内容（比如方法或变量）都可以被该区间内的代码访问到。 注意：  * 作用域（Scope）  指一个范围、区域或空间  * 全局作用域（Global Scope）  指全局空间或一个公共空间  * 局部作用域（Local Scope）  指一个局部空间或一个受限制的空间 举个例子： // 定义一个全局变量: const fullName = "Oluwatobi Sofela"; // 定义多层嵌套函数: function profile() {   function sayName() {     ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-lexical-scope-tutorial/</link>
                <guid isPermaLink="false">6177ca0ae6e485065265cdf7</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ wangzili ]]>
                </dc:creator>
                <pubDate>Tue, 26 Oct 2021 09:30:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/10/kristina-tripkovic-EGmwwDzme6s-unsplash-javascript-lexical-scope-codesweetly.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><strong>词法作用域（lexical scope）</strong> 的概念不好理解，也很难掌握。但如果我们可以搞清楚概念里每一个词的意思，对于理解词法作用域将大有帮助。</p>
<p>因此本文将分别解释“词法（lexical）”和“作用域（scope）”这两个概念，然后再解释什么是词法作用域。</p>
<p>让我们开始吧~~</p>
<h2 id="">什么是作用域</h2>
<p><strong>作用域</strong> 表示一个区间，在这个区间内声明的所有内容（比如方法或变量）都可以被该区间内的代码访问到。</p>
<p><strong>注意：</strong></p>
<ul>
<li><strong>作用域（Scope）</strong> 指一个范围、区域或空间</li>
<li><strong>全局作用域（Global Scope）</strong> 指全局空间或一个公共空间</li>
<li><strong>局部作用域（Local Scope）</strong> 指一个局部空间或一个受限制的空间</li>
</ul>
<p><strong>举个例子：</strong></p>
<pre><code class="language-js">// 定义一个全局变量:
const fullName = "Oluwatobi Sofela";

// 定义多层嵌套函数:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

// 打印结果:
console.log(profile()) // 'Oluwatobi Sofela'
</code></pre>
<p><a href="https://stackblitz.com/edit/web-platform-fqqxjl?file=script.js"><strong>点击查看源代码</strong></a>。</p>
<p>在上述示例中，我们定义了一个<code>fullName</code>全局变量，这就意味着在脚本内所有代码都可以访问<code>fullName</code>变量。</p>
<p>我们在<code>sayName()</code>函数内定义了<code>writeName()</code>函数，所以<code>writeName()</code>被<code>sayName()</code>的局部作用域包裹着。</p>
<p>换言之，<code>writeName()</code>只能被<code>sayName()</code>函数内部的代码访问。</p>
<p>请记住，无论<code>writeName()</code>函数何时被调用，编译器都不会直接访问全局作用域下的<code>fullName</code>变量，而是通过<a href="#what-is-a-scope-chain">作用域链</a>依次查找。</p>
<h2 id="">什么是作用域链</h2>
<p>作用域链是一个独特空间。当一个变量被调用，那么变量在 <strong>被调用</strong> 时所在的局部作用域和全局作用域之间，就形成了一个作用域链。</p>
<p><strong>示例</strong></p>
<pre><code class="language-js">// 定义一个全局作用域变量：
const fullName = "Oluwatobi Sofela";

// 定义多层嵌套函数:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

console.log(profile()) // 'Oluwatobi Sofela'
</code></pre>
<p>在上述示例中，<code>fullName</code> 变量在 <code>writeName()</code> 函数作用域中被调用。</p>
<p>因此，从变量的执行作用域到全局作用域之间存在如下作用域链：</p>
<p><strong>writeName() scope ---&gt; sayName() scope ---&gt; profile() scope ---&gt; global scope</strong></p>
<p>换言之，从<code>fullName</code>变量的执行作用域到它的词法作用域（此处指全局作用域）之间有4层作用域。</p>
<p><strong>注意：</strong> 在 <a href="https://www.codesweetly.com/html-css-javascript/">JavaScript</a> 作用域链中，全局作用域是整个作用域链的终点。</p>
<h2 id="">作用域链是如何工作的</h2>
<p>JavaScript 的作用域链规定了编译器在查找 <strong>被调用变量</strong> 的词法作用域时所遵循的查找规则。</p>
<p>考虑如下示例代码：</p>
<pre><code class="language-js">// 定义一个全局作用域变量：
const fullName = "Oluwatobi Sofela";

// 定义多层嵌套函数：
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

console.log(profile()) // 'Oluwatobi Sofela'
</code></pre>
<p>在上述示例中，无论何时调用<code>profile()</code>函数，编译器都会首先调用<code>sayName()</code>函数（<code>profile()</code>函数体内只有一个<code>sayName()</code>函数）。</p>
<p>然后，编译器会调用<code>writeName()</code>函数（<code>sayName()</code>函数体内只有一个<code>writeName()</code>函数）。</p>
<p>最终，<code>writeName()</code>函数会调用并返回<code>fullName</code>变量的值，所以编译器将返回<code>fullName</code>的值。但编译器不会直接从全局作用域中调用<code>fullName</code>。</p>
<p>相反的，编译器必须通过作用域链一步一步的查找<code>fullName</code>的词法作用域。</p>
<p>下面是编译器查找<code>fullName</code>词法作用域的步骤：</p>
<ol>
<li>首先，编译器会检查<code>writeName()</code>的函数作用域是否定义了<code>fullName</code>变量。未找到，继续向上查找下一个作用域，即<code>sayName()</code>作用域。</li>
<li>第二步，编译器在<code>sayName()</code>的函数作用域中查找，依然没找到，继续向上查找下一个作用域，即<code>profile()</code>作用域。</li>
<li>第三步，编译器在<code>profile()</code>的函数作用域中查找，依然没找到，继续向上查找下一个作用域，即全局作用域。</li>
<li>第四步，编译器查找到了全局作用域。幸运的是，在全局作用域下找到了<code>fullName</code>变量，然后将其值（<code>"Oluwatobi Sofela"</code>）作为返回值返回。</li>
</ol>
<h2 id="">实践一下🤸‍♂️🏋️‍♀️🏊‍</h2>
<p>考虑以下代码，并思考：编译器会调用哪一个<code>fullName</code>？</p>
<pre><code class="language-js">// 在全局作用域中定义fullName:
const fullName = "Oluwatobi Sofela";

// 嵌套函数包含2个fullName变量
function profile() {
  const fullName = "Tobi Sho";
  function sayName() {
    const fullName = "Oluwa Sofe";
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

console.log(profile())
</code></pre>
<p>编译器将调用第一个，第二个，还是第三个<code>fullName</code>呢？</p>
<p><strong>注意：</strong> 如果你多多练习，就能从这个教程中收获多多。</p>
<p>如果你卡住了，不要气馁。重新温习以上知识，然后在试一次。</p>
<p>如果你已经用尽了全力（不要自欺欺人）但还是不会，那就继续往下看正确答案吧。</p>
<h2 id="">你做对了吗</h2>
<p>在上面脚本中定义的三个<code>fullName</code>中，编译器将调用并返回定义在<code>sayName()</code>函数中的<code>fullName</code>变量。</p>
<p><code>sayName()</code>中定义的<code>fullName</code>之所以会被调用，是因为编译器查找作用域链时最先在<code>sayName()</code>的作用域中找到<code>fullName</code>。</p>
<p>因此，当<code>profile()</code>被调用时，返回值是<code>"Oluwa Sofe"</code>。</p>
<p><a href="https://stackblitz.com/edit/web-platform-9mpvfv?file=script.js"><strong>点击查看源代码</strong></a></p>
<p><strong>特别强调 3 点：</strong></p>
<ul>
<li>假如编译器没有在任何作用域中找到<code>fullName</code>，那么编译器将返回一个引用错误<code>Uncaught ReferenceError: fullName is not defined</code>。</li>
<li>全局作用域是 JavaScript 作用域链的最后一个作用域，即全局作用域是查找的终点。</li>
<li>一个内部作用域（子作用域）可以访问它的外部作用域（父级作用域），但是外部作用域不能访问它的子作用域。<br>
举个例子，在上面代码中，<code>writeName()</code>可以访问它的任何父级作用域（比如<code>sayName()</code>的局部作用域，<code>profile()</code>的局部作用域，或者全局作用域）。<br>
但是，无论是<code>sayName()</code>和<code>profile()</code>的局部作用域，还是全局作用域，都不能访问<code>writeName()</code>的作用域。</li>
</ul>
<h2 id="">作用域小结</h2>
<p>JavaScript 中所有的作用域都是一个可被访问的区间。</p>
<p>因此，如果你女（男）朋友打电话让你去他的私人作用域时，切记他们是在邀请你去他们的私人空间 😜!</p>
<p>你一旦去了，就要问他们最好的词法游戏是什么...</p>
<p>但是词法（lexical）究竟是什么意思？让我们一起往下看。</p>
<h2 id="lexical">什么是词法（Lexical）</h2>
<p><strong>词法（Lexical）</strong> 指的是定义某个事物。</p>
<p>任何创建文字、表达式或变量的声明都叫词法。</p>
<p>比如，拼字游戏就是一种词法活动，因为这个游戏在创造文字。</p>
<p>语言学家的工作也是一种词法事业。</p>
<p><strong>注意：</strong> 字典（dictionary）又叫词典（lexicon），也就是说，词典（lexicon）就是一部罗列并定义文字的字典（dictionary）。</p>
<p>现在我们知道了作用域和词法的意思，那就可以讨论 <strong>词法作用域</strong> 了。</p>
<h2 id="lexicalscope">什么是词法作用域（Lexical Scope）</h2>
<p><strong>词法作用域（Lexical Scope）</strong> 是定义表达式并能被访问的区间。</p>
<p>换言之，一个声明（定义变量、函数等）的词法作用域就是它被定义时所在的作用域。</p>
<p><strong>注意：</strong></p>
<ul>
<li>词法作用域又叫静态作用域。</li>
<li>一个声明 <strong>被调用时的作用域</strong> 不一定是它的词法作用域。相反的，<strong>定义时的作用域</strong> 才是词法作用域</li>
</ul>
<h3 id="">一个词法作用域的小示例</h3>
<p>考虑如下代码：</p>
<pre><code class="language-js">// 定义一个全局作用域变量：
const myName = "Oluwatobi";

// 在函数体内调用myName变量
function getName() {
  return myName;
}

console.log(getName()) // 'Oluwatobi'
</code></pre>
<p>在上述示例中，我们在全局作用域定义了<code>myName</code>变量，并在<code>getName()</code>函数作用域内调用了该变量。</p>
<p><strong>问题：</strong> <code>myName</code>变量的词法作用域是什么？全局作用域还是 <code>getName()</code>的局部作用域？</p>
<p><strong>答案：</strong> 切记 <strong>词法作用域</strong> 意味着 <strong>定义时的作用域</strong>，并不是<strong>调用时的作用域</strong> 。因此<code>myName</code>变量的词法作用域是全局作用域，因为我们在全局环境下定义了<code>myName</code>变量。</p>
<h3 id="">再来一个小例子</h3>
<pre><code class="language-js">function getName() {
  const myName = "Oluwatobi";
  return myName;
}

console.log(getName())  // 'Oluwatobi'
</code></pre>
<p><strong>问题：</strong> <code>myName</code>变量的词法作用域是什么？</p>
<p><strong>答案：</strong> 我们在<code>getName()</code>函数内定义并调用了<code>myName</code>变量。因此，<code>myName</code>的词法作用域是<code>getName()</code>的局部作用域，因为<code>getName()</code>是<code>myName</code>定义时所在的作用域。</p>
<h2 id="">词法作用域如何工作</h2>
<p>JavaScript 表达式的词法作用域决定了哪些代码可以访问它。</p>
<p>换言之，只有词法作用域内的代码才可以访问该作用域内部的代码。</p>
<p>考虑如下代码：</p>
<pre><code class="language-js">// 定义一个函数：
function showLastName() {
  const lastName = "Sofela";
  return lastName;
}

// 再定义一个函数：
function displayFullName() {
  const fullName = "Oluwatobi " + lastName;
  return fullName;
}

// 调用 displayFullName():
console.log(displayFullName());

// 返回值：
Uncaught ReferenceError: lastName is not defined
</code></pre>
<p>上述代码中，调用<code>displayFullName()</code>时报错<code>Uncaught ReferenceError</code>。这是因为只有词法作用域内的代码才能访问该作用域的代码。</p>
<p>因此，不管是<code>displayFullName()</code>函数，还是它内部的代码都不能访问<code>lastName</code>变量，因为<code>lastName</code>定义在不同的作用域中。</p>
<p>换言之，<code>lastName</code>的词法作用域和<code>displayFullName()</code>不相同。</p>
<p><code>lastName</code>的词法作用域是<code>showLastName()</code>函数作用域，而<code>displayFullName()</code>的词法作用域是全局作用域。</p>
<p>现在，考虑另一段代码：</p>
<pre><code class="language-js">// 定义一个函数：
function showLastName() {
  const lastName = "Sofela";
  return lastName;
}

// 再定义一个函数：
function displayFullName() {
  const fullName = "Oluwatobi " + showLastName();
  return fullName;
}

// 调用 displayFullName():
console.log(displayFullName());

// 返回值：
"Oluwatobi Sofela"
</code></pre>
<p>在上述代码中，<code>displayFullName()</code>成功返回<code>"Oluwatobi Sofela"</code>，因为<code>displayFullName()</code>和<code>showLastName()</code>在相同的词法作用域中。</p>
<p>换言之，<code>displayFullName()</code>可以调用<code>showLastName()</code>，因为这两个函数都定义在全局作用域中。</p>
<p><strong>注意：</strong></p>
<ul>
<li>上述 2 个示例中，<code>displayFullName()</code>不能访问<code>showLastName()</code>中<code>lastName</code>变量。但<code>displayFullName()</code>可以调用<code>showLastName()</code>，而<code>showLastName()</code>返回了<code>lastName</code>变量的值。</li>
<li>词法作用域的替换方案是<a href="https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope_2">动态作用域</a>，但是除了在极少的开发语言（比如 bash 脚本）中使用外，很少使用它。</li>
</ul>
<h2 id="">总结</h2>
<p>只要你一听到词法，就要联想到是定义某个事物。</p>
<p>因此，一辆车、一个变量、一部手机、一个函数、一件泳衣等，这些事物的词法作用域指的就是定义他们时所在的区间。</p>
<h2 id="">写在最后</h2>
<p>这篇文章不仅讨论了在 JavaScript 中什么是词法作用域，还探讨了为什么它是一个很重要的编程概念。感谢你阅读本文！</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/javascript-lexical-scope-tutorial/">Lexical Scope in JavaScript – What Exactly Is Scope in JS</a>，作者：<a href="https://www.freecodecamp.org/news/author/oluwatobiss/">Oluwatobi Sofela</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 一文理解 JavaScript Promise ]]>
                </title>
                <description>
                    <![CDATA[ 如果你还是一个 JavaScript 初学者，你可能要纠结一下 Promise 到底是什么。 最近我发了一条推特，描述了这个问题，我被大家的反馈震惊到了。所以我决定写一篇关于 Promise 的教程。 我看过很多关于 Promise 的文章，但大部分教程都没有通过类比的方式解释清楚 Promise 到底是什么。初学者搞不懂 Promise 的根本原因是他们不知道 Promise 是做什么的，以及如何在简单和复杂的场景中使用它。 因此在这篇教程中，我将通过一个小故事来解释什么是 Promise、Promise 是如何工作的。同时我也会通过一些代码示例来说明在 JavaScript 中如何使用 Promise。 什么是 Promise 想象一下，你准备面试某个公司的前端工程师。 你走进面试会场，当面试马上要开始时你发现简历忘带了，这时你怎么办？ 你没有气馁。因为你很幸运，你有一个室友。 你马上给室友打电话寻求帮助，恳求室友帮你找到简历。你的室友承诺他一旦找到就立马回你消息。 假设简历被找到，室友给你回复信息： > “太好了，我找到你的简历了！” 但是如果室友没有找到， ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-is-promise-in-javascript-for-beginners/</link>
                <guid isPermaLink="false">6167dcac1cb0e006556dc5f6</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Promise ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ wangzili ]]>
                </dc:creator>
                <pubDate>Thu, 14 Oct 2021 07:40:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/10/walling-e_MdMMKrgdY-unsplash.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>如果你还是一个 JavaScript 初学者，你可能要纠结一下 Promise 到底是什么。</p>
<p>最近我发了一条推特，描述了这个问题，我被大家的反馈震惊到了。所以我决定写一篇关于 Promise 的教程。</p>
<p>我看过很多关于 Promise 的文章，但大部分教程都没有通过类比的方式解释清楚 Promise 到底是什么。初学者搞不懂 Promise 的根本原因是他们不知道 Promise 是做什么的，以及如何在简单和复杂的场景中使用它。</p>
<p>因此在这篇教程中，我将通过一个小故事来解释什么是 Promise、Promise 是如何工作的。同时我也会通过一些代码示例来说明在 JavaScript 中如何使用 Promise。</p>
<h2 id="promise">什么是 Promise</h2>
<p>想象一下，你准备面试某个公司的前端工程师。</p>
<p>你走进面试会场，当面试马上要开始时你发现简历忘带了，这时你怎么办？</p>
<p>你没有气馁。因为你很幸运，你有一个室友。</p>
<p>你马上给室友打电话寻求帮助，恳求室友帮你找到简历。你的室友<strong>承诺</strong>他一旦找到就立马回你消息。</p>
<p>假设简历被找到，室友给你回复信息：</p>
<blockquote>
<p>“太好了，我找到你的简历了！”</p>
</blockquote>
<p>但是如果室友没有找到，他就要回复一条失败的信息，并解释他为什么没有找到简历。比如，他可能给正在面试的你发如下信息：</p>
<blockquote>
<p>“对不起，我没有找到你的简历，因为你的保险柜钥匙丢了。”</p>
</blockquote>
<p>与此同时，面试还要继续。但面试官并没有拿到真实的简历，而是得到一个<strong>正在找简历</strong>的承诺，同时面试官把该简历的状态设置成<strong>进行中（PENDING）</strong>。</p>
<p>你回答了所有问题。但不幸的是，能否得到这个工作还要依赖你简历的<strong>最终状态（FINAL STATUS）</strong>。</p>
<p>你的室友终于回消息了。正如我们前面讨论过的，如果他没有找到简历，他就需要发一个失败的信息并解释为什么没有找到。</p>
<p>如果是这种情况，面试结束，你被 <strong>拒绝（Rejected）</strong> 了。</p>
<p>如果室友找到了简历，他会很高兴的告诉你他找到了，而你将继续面试，并 <strong>获得（FULFILL）</strong> 这份工作。</p>
<h2 id="js">如何把上述过程翻译成 JS 代码</h2>
<p>室友承诺找简历并回复信息的过程等同于我们在 JavaScript 中定义一个 Promise。定义 Promise 并不能直接或立即获得返回值，而是返回一个 Promise 对象。这个 Promise 对象在一段时间后会接收返回值。</p>
<p>Promise 对象是异步的，这就意味着程序需要花点时间才能获得结果。这和找简历是一样的，都需要花点时间去找。</p>
<p>基于这个原因，在找的这个时间里，面试官并不是什么都没做，而是基于<strong>简历一会儿就找到</strong>的承诺，他们依然开始面试候选人。在这个场景里，我们用<strong>简历一会儿就找到</strong>的承诺替换了<strong>真实的简历</strong>。</p>
<p>同理，JS 引擎也并不是等着什么也不做，而是继续执行后续代码，并将返回的Promise对象状态设置为 <strong>Pending</strong>。</p>
<p>回复信息包含是否找到简历的状态信息。对于Promise对象来说，回复信息被称作返回值。</p>
<p>如果回复信息是 “success”，我们将录取候选人。如果是 “failure”，我们不录取该候选人。</p>
<p>在 Promise 中，我们通过<a href="https://www.freecodecamp.org/news/javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/">回调函数</a>处理Promise的返回值。这些处理函数定义在<code>then()</code>方法中。</p>
<p>为了指定如何调用回调函数，需要使用以下两个方法：</p>
<ul>
<li><code>resolve(value)</code>: 表明 Promise 任务成功，调用<code>then()</code>的成功回调函数。</li>
<li><code>reject(error)</code>: 表明 Promise 任务失败，调用<code>then()</code>的错误回调函数。</li>
</ul>
<p>如果 Promise 成功，则调用成功回调，如果失败，调用失败回调。</p>
<p>在异步任务完成之前，Promise 只是一个占位符。当你定义了一个 Promise，你并不会<strong>立即</strong>获得返回值，而是获得一个 Promise 对象。</p>
<h2 id="javascriptpromise">如何在 JavaScript 中使用 Promise</h2>
<p>你可以通过<code>Promise</code>类定义一个 Promise 对象。</p>
<pre><code class="language-js">const myPromise = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; {
    resolve('this is the eventual value the promise will return');
  }, 3000);
});

console.log(myPromise);
</code></pre>
<div style="text-align:center; margin-bottom: 1.5em;">示例 1</div>
<p>在控制台运行将返回一个<code>Promise</code>对象：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/promise-console-1.png" alt="promise-console-1" width="600" height="400" loading="lazy"></p>
<p>除了通过构造函数声明一个 Promise 对象外，还可以使用 Promise 内置的 API 进行声明：</p>
<pre><code class="language-js">const anotherPromise = Promise.resolve("this is the eventual value the promise will return")

console.log(anotherPromise);
</code></pre>
<div style="text-align:center; margin-bottom: 1.5em;">示例 2</div>
<p>示例 1 中的 Promise 等待 3s 后获取到成功返回的信息：<code>this is the eventual...</code>，而示例 2 中将立即获取到成功返回的信息。</p>
<h2 id="javascriptpromise">JavaScript Promise 中的错误处理</h2>
<p>Promise 对象也能被<em>rejected</em>。大多数时候，<strong>rejected</strong> 的发生是因为执行异步任务的时候抛出了错误，此时就会调用<code>reject()</code>方法。</p>
<p>下面的示例展示了一个 Promise 对象是如何执行 reject 方法的:</p>
<pre><code class="language-js">const myPromise = new Promise((resolve, reject) =&gt; {
  let a = false;
  setTimeout(() =&gt; {
    return (a) ? resolve('a is found!'): reject('sorry, no a');
  }, 3000);
}); 

</code></pre>
<div style="text-align:center; margin-bottom: 1.5em;">示例 3</div>
<p>你能想到 Promise 被 rejected 的原因吗？如果你的答案是：<code>a</code>的值是 false，那么恭喜你答对了。</p>
<p>在示例 3 中，代码执行 3s 后将调用 reject 方法，因为<code>(a)?</code>表达式的值是 false，所以触发<code>reject</code>方法。</p>
<h2 id="promise">Promise 的链式调用</h2>
<p>当 Promise 返回了某个值，通常你都会对返回值进行处理。</p>
<p>比如，你发送了一个网络请求，你期望获取数据并展示在页面上。</p>
<p>你可以定义两个回调函数，当 Promise 返回成功或失败时进行回调。这两个回调函数定义在<code>then()</code>内：</p>
<pre><code class="language-JS">const anotherPromise = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; {
    resolve('this is the eventual value the promise will return');
  }, 3000);
});

// CONTINUATION
anotherPromise
.then(value =&gt; { console.log(value) }) 
</code></pre>
<div style="text-align:center; margin-bottom: 1.5em;">示例 4</div>
<p>示例 4 的代码在 3s 后返回成功信息：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/EVENTAL-RETURN.png" alt="EVENTAL-RETURN" width="600" height="400" loading="lazy"></p>
<p>原则上你可以无限链式调用，调用链会依次执行，而且前一个 then 的返回值作为参数传入后一个 then。</p>
<pre><code class="language-js">const anotherPromise = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; {
    resolve('this is the eventual value the promise will return');
  }, 3000);
});

anotherPromise
.then(fulfillFn, rejectFn)
.then(fulfilFn, rejectFn)
.then(value =&gt; { console.log(value) })
</code></pre>
<div style="text-align:center; margin-bottom: 1.5em;">示例 5</div>
<p>但我们还是遗漏了重要的内容。</p>
<p>要时刻记住，<code>then()</code>方法必须有两个回调函数：第一个参数是成功回调，第二个参数是错误回调。</p>
<p>在示例 4 和示例 5 中都没有传入第二个回调函数。因此，如果代码报错，就没有错误回调捕获错误信息。</p>
<p>如果你执意要在<code>then()</code>中只定义一个回调函数（即成功回调），那么你就需要在 Promise 调用链的末端调用<code>catch()</code>方法捕获任何可能的报错信息。</p>
<h3 id="jscatch">如何在 JS 中使用<code>catch()</code>方法</h3>
<p>在 Promise 调用链上，无论哪个环节报错，<code>catch()</code>方法都会被调用。</p>
<pre><code class="language-js">const myPromise = new Promise((resolve, reject) =&gt; {
  let a = false;
  setTimeout(() =&gt; {
    return (a) ? resolve('a is found!'): reject('sorry, no a');
  }, 3000);
}); 

myPromise
.then(value =&gt; { console.log(value) })
.catch(err =&gt; { console.log(err) });


</code></pre>
<div style="text-align:center; margin-bottom: 1.5em;">示例 6</div>
<p>因为<code>myPromise</code>最终状态是 rejected，<code>then()</code>方法中的成功回调被忽略。而<code>catch()</code>方法中的错误回调被执行，并在控制台打印如下错误信息：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Catch.png" alt="Catch" width="600" height="400" loading="lazy"></p>
<h2 id="">写在最后</h2>
<p>JavaScript 中的 Promise 是一个运行异步任务的强大功能。大部分情况下，在前端面试时，面试官都会问一些关于 Promise 的问题。</p>
<p>在这片文章中，我已经解释了 Promise 的简单应用场景，也通过示例的方式展示了基本用法。</p>
<p>希望你能从文章中获取有用的知识。如果你喜欢编程教程，点击查看<a href="https://ubahthebuilder.tech/user-authentication-vs-user-authorization-what-do-they-mean-in-back-end-web-development">我的博客</a>。我会经常发布一些有关软件开发的文章。</p>
<p>再次感谢您的阅读，再会。</p>
<p><strong>P/S：</strong> 如果你也在学习 JavaScript，我也创建了一个电子书，上面有 50 个关于 js 的主题，而且都是我亲自手绘的哦~~<a href="https://ubahthebuilder.gumroad.com/l/js-50">点此查看</a>。</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/what-is-promise-in-javascript-for-beginners/">What is a Promise? JavaScript Promises for Beginners</a>，作者：<a href="https://www.freecodecamp.org/news/author/ubahthebuilder/">Kingsley Ubah</a></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
