<?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>Thu, 18 Jun 2026 10:44:45 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/han-shu/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Python 函数——如何定义和调用函数 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：Python Functions – How to Define and Call a Function [https://www.freecodecamp.org/news/python-functions-define-and-call-a-function/] ，作者：Kolade Chris [https://www.freecodecamp.org/news/author/kolade/] 在编程中，函数是一个可重复使用的代码块，当它被调用时执行某种功能。 函数是每一种编程语言的组成部分，因为它们有助于使你的代码更加模块化和可重复使用。 在这篇文章中，我将向你展示如何在 Python 中定义一个函数并调用它，这样你就可以把你的 Python 应用程序的代码分解成更小的块。 我还将向你展示参数和 return 关键字在 Python 函数中是如何工作的。 在 Python 中定义一个函数的基本语法 在 Python 中，你用 def 关键字定义一个函数，然后写上函数标识符（名称），后面是括号和冒号。 接下来你要做的是确保按 Tab 键或 4 个空格缩进，然后指 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/python-functions-define-and-call-a-function/</link>
                <guid isPermaLink="false">625f8df699ec7406219e703a</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 函数 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 20 Apr 2022 04:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/04/functions.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/python-functions-define-and-call-a-function/">Python Functions – How to Define and Call a Function</a>，作者：<a href="https://www.freecodecamp.org/news/author/kolade/">Kolade Chris</a></p><p>在编程中，函数是一个可重复使用的代码块，当它被调用时执行某种功能。</p><p>函数是每一种编程语言的组成部分，因为它们有助于使你的代码更加模块化和可重复使用。</p><p>在这篇文章中，我将向你展示如何在 Python 中定义一个函数并调用它，这样你就可以把你的 Python 应用程序的代码分解成更小的块。</p><p>我还将向你展示参数和 return 关键字在 Python 函数中是如何工作的。</p><h2 id="-python-">在 Python 中定义一个函数的基本语法</h2><p>在 Python 中，你用 def 关键字定义一个函数，然后写上函数标识符（名称），后面是括号和冒号。</p><p>接下来你要做的是确保按 Tab 键或 4 个空格缩进，然后指定你希望这个函数为你做什么。</p><pre><code class="language-py">def functionName():
    # 让函数做什么
</code></pre><h2 id="python-">Python 中函数的基本示例</h2><p>按照上面的基本语法，一个向终端打印 “Hello World” 的基本 Python 函数示例如下所示：</p><pre><code class="language-py">def myfunction():
    print("Hello World")
</code></pre><p><strong>要调用这个函数，</strong>请写出函数的名称，后面加括号：</p><pre><code class="language-py">myfunction()

</code></pre><p>接下来，通过输入 <code>python filename.py</code> 在终端运行你的代码，以显示你希望函数做什么：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/03/sss-1.png" class="kg-image" alt="sss-1" width="600" height="400" loading="lazy"></figure><p>另一个两个数字相减的基本例子是这样的：</p><pre><code class="language-py">def subtractNum():
    print(34 - 4)

subtractNum()
# 输出：30
</code></pre><h2 id="python--1"><strong>Python 函数中的参数</strong></h2><p>在 Python 中定义一个函数时，你可以通过把参数放在括号内将它们传入函数。</p><p>基本语法如下所示：</p><pre><code class="language-py">def functionName(arg1, arg2):
    # 用函数做什么
    
</code></pre><p>调用函数时，你需要为参数指定一个值：</p><pre><code class="language-py">functionName(valueForArg1, valueForArg2)
</code></pre><p>这是 Python 函数中的参数示例：</p><pre><code class="language-py">def addNum(num1, num2):
    print(num1 + num2)
addNum(2, 4)

# 输出：6
</code></pre><p>在上面的例子中：</p><ul><li>我向名为 <code>addNum</code> 的函数传递了两个参数</li><li>我告诉它要把两个参数的和打印到终端上</li><li>然后我用指定的两个参数的值来调用它</li></ul><p><strong>注意：</strong>你可以指定任意多的参数。</p><h2 id="-python-return-">如何在 Python 中使用 Return 关键字</h2><p>在 Python 中，你可以使用 return 关键字来退出一个函数，这样它就回到了被调用的地方。也就是说，从函数中送出一些东西。</p><p>一旦函数被调用，return 语句可以包含一个要执行的表达式。</p><p>下面的例子演示了 return 关键字在 Python 中的作用：</p><pre><code class="language-py">def multiplyNum(num1):
    return num1 * 8

result = multiplyNum(8)
print(result)

# 输出：64
</code></pre><p><strong>上面的代码在做什么？</strong></p><p>我定义了一个名为 <code>multiplyNum</code> 的函数，并将 <code>num1</code> 作为一个参数传给它</p><p>在该函数中，我使用了 return 关键字来指定我希望 <code>num1</code> 被乘以 8。</p><p>之后，我调用了该函数，将 <code>8</code> 作为参数 <code>num1</code> 的值传给它，并将该函数调用分配给一个名为 <code>result</code> 的变量。</p><p>有了这个变量，我就可以把我想用这个函数做的事情打印到终端。</p><h2 id="-">总结</h2><p>在这篇文章中，你学会了如何在 Python 中定义和调用函数。你还学会了如何将参数传入函数并使用 return 关键字，这样你就可以更有创意地编写函数了。</p><p>如果你觉得这篇文章有帮助，不要犹豫，与你的朋友和家人分享它。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 JavaScript 回调函数 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：What is a Callback Function in JavaScript? [https://www.freecodecamp.org/news/what-is-a-callback-function-in-javascript/] 本文将简要介绍 JavaScript 编程语言中回调函数的概念和用法。 函数是对象 我们需要知道的第一件事是，在 JavaScript 中，函数是第一类对象。因此，我们可以用处理其他对象的方式来处理它们，比如把它们分配给变量，并把它们作为参数传给其他函数。这一点很重要，因为正是第二个方式使我们能够在应用程序中扩展功能。 回调函数 回调函数是一个被作为参数传递给另一个函数的函数，以便在稍后时间被“回调”。一个接受其他函数作为参数的函数被称为高阶函数 ，它包含回调函数何时被执行的逻辑。正是这两者的结合，使我们能够扩展应用的功能。 为了说明回调，让我们从一个简单的例子开始。 function createQuote(quote, callback){    var myQuote = "Like I always say, " + qu ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-is-a-callback-function-in-javascript/</link>
                <guid isPermaLink="false">6246f92c7f18d1062895c5bb</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 函数 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Miya Liu ]]>
                </dc:creator>
                <pubDate>Fri, 01 Apr 2022 09:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/04/5f9c9eb0740569d1a4ca3e8d.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/what-is-a-callback-function-in-javascript/">What is a Callback Function in JavaScript?</a></p><p>本文将简要介绍 JavaScript 编程语言中回调函数的概念和用法。</p><h2 id="-">函数是对象</h2><p>我们需要知道的第一件事是，在 JavaScript 中，函数是第一类对象。因此，我们可以用处理其他对象的方式来处理它们，比如把它们分配给变量，并把它们作为参数传给其他函数。这一点很重要，因为正是第二个方式使我们能够在应用程序中扩展功能。</p><h2 id="--1"><strong><strong><strong>回调函数</strong></strong></strong></h2><p><strong>回调函数</strong>是一个被作为参数传递给另一个函数的函数，以便在稍后时间被“回调”。一个接受其他函数作为参数的函数被称为<strong>高阶函数</strong>，它包含回调函数何时被执行的逻辑。正是这两者的结合，使我们能够扩展应用的功能。</p><p>为了说明回调，让我们从一个简单的例子开始。</p><pre><code class="language-javascript">function createQuote(quote, callback){ 
  var myQuote = "Like I always say, " + quote;
  callback(myQuote); // 2
}

function logQuote(quote){
  console.log(quote);
}

createQuote("eat your vegetables!", logQuote); // 1

// 控制台的结果： 
// Like I always say, eat your vegetables!</code></pre><p>在上面的例子中，<code>createQuote</code> 是高阶函数，它接受两个参数，第二个参数是回调。<code>logQuote</code> 函数被用来作为我们的回调函数传入。当我们执行 <code>createQuote</code> 函数（1）时，注意到当我们把 <code>logQuote</code> 作为参数传入时，没有给它加上括号。这是因为我们并不想立即执行回调函数，只是想把函数定义传递给高阶函数，以便以后可以执行。</p><p>另外，我们需要确保，如果我们传入的回调函数期望有参数，那么在执行回调时要提供这些参数。在上面的例子中，这将是 <code>callback(myQuote);</code> 语句，因为我们知道 <code>logQuote</code> 期望传入一句引言。</p><p>此外，我们可以将匿名函数作为回调传入。下面对 <code>createQuote</code> 的调用将产生与上述例子相同的结果。</p><pre><code class="language-javascript">createQuote("eat your vegetables!", function(quote){ 
  console.log(quote); 
});</code></pre><p>顺便说一下，你不必用“回调”这个词作为参数的名称，JavaScript 只需要知道这是一个正确的参数名称。基于上面的例子，下面的函数将以完全相同的方式表现出来。</p><pre><code class="language-javascript">function createQuote(quote, functionToCall) { 
  var myQuote = "Like I always say, " + quote;
  functionToCall(myQuote);
}</code></pre><h2 id="--2">为什么使用回调函数</h2><p>大多数时候，我们创建的程序和应用都是以同步方式运行的。换句话说，我们的一些操作只有在前面的操作完成后才会开始。通常情况下，当我们从其他来源（如外部 API）请求数据时，我们并不总是知道数据何时会被传递回来。在这种情况下，我们希望等待响应，但我们并不总是希望整个应用程序在获取数据时停顿下来。这些情况就是回调函数的用武之地。</p><p>让我们来看看一个模拟向服务器发出请求的例子。</p><pre><code class="language-javascript">function serverRequest(query, callback){
  setTimeout(function(){
    var response = query + "full!";
    callback(response);
  },5000);
}

function getResults(results){
  console.log("Response from the server: " + results);
}

serverRequest("The glass is half ", getResults);

// 延迟 5 秒后，在控制台中显示：
// 服务器的响应：The glass is half full!</code></pre><p>在上面的例子中，我们向一个服务器发出了一个模拟请求。5 秒过后，响应被修改，然后我们的回调函数 <code>getResults</code> 被执行。要查看这个过程，你可以将上述代码复制/粘贴到你的浏览器的开发工具中并执行。</p><p>另外，如果你已经熟悉 <code>setTimeout</code>，那么你就一直在使用回调函数。传入上述例子的 <code>setTimeout</code> 函数调用中的匿名函数参数也是一个回调函数! 所以这个例子的原始回调实际上是由另一个回调执行的。如果可以的话，请注意不要嵌套太多的回调，因为这可能会导致所谓的“回调地狱”！正如它的名字所暗示的那样，处理这个问题比较麻烦。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 通过一个开发实例掌握 Async/Await ]]>
                </title>
                <description>
                    <![CDATA[ 目录 1、介绍回调函数, promises, async/await 2、开发实例--货币转换器，从两个 API 异步获取数据 在开始正文之前 在写这篇文章的同时，我还录制了一个相关的视频，你可以边看视频边敲代码，我建议你先看下视频 [https://www.youtube.com/watch?v=mlb525FgU3k]，然后再以这篇文章为引导自己练习下代码。 简介 Async/Await 是一种建立在 promises 基础上的，书写异步代码的新方式，所以它也是非阻塞的。 与我们之前异步编程所使用到的回调函数和 promises 相比较，最大的区别就是 Async/Await 使我们的异步代码看起来就像同步代码一样，这也正是它的厉害之处。 使用回调函数 setTimeout(() => {  console.log('This runs after 1000 milliseconds.');}, 1000); 回调函数最典型的问题——回调地狱 在回调函数中嵌套回调函数，看起来就像这样： 回调地狱回调地狱 指的是回调函数被嵌套在另一个回调函数中，嵌套层级有多层，使我们 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-master-async-await-with-this-real-world-example/</link>
                <guid isPermaLink="false">5d5b819cfbfdee429dc5f82a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 函数 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Promise ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leo Zou ]]>
                </dc:creator>
                <pubDate>Wed, 10 Feb 2021 08:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2019/08/1_2Nco5zYP_Xv-5-FgL6kCuw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h3 id="-">目录</h3><p>1、介绍回调函数, promises, async/await</p><p>2、开发实例--货币转换器，从两个 API 异步获取数据</p><h3 id="--1">在开始正文之前</h3><p>在写这篇文章的同时，我还录制了一个相关的视频，你可以边看视频边敲代码，我建议你先看下<a href="https://www.youtube.com/watch?v=mlb525FgU3k">视频</a>，然后再以这篇文章为引导自己练习下代码。</p><h3 id="--2">简介</h3><p>Async/Await 是一种建立在 promises 基础上的，书写异步代码的新方式，所以它也是非阻塞的。</p><p>与我们之前异步编程所使用到的回调函数和 promises 相比较，最大的区别就是 Async/Await 使我们的异步代码看起来就像同步代码一样，这也正是它的厉害之处。</p><h3 id="--3">使用回调函数</h3><pre><code class="language-javascript">setTimeout(()&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;console.log('This&nbsp;runs&nbsp;after&nbsp;1000&nbsp;milliseconds.');},&nbsp;1000);</code></pre><h3 id="--4"><br>回调函数最典型的问题——回调地狱</h3><p>在回调函数中嵌套回调函数，看起来就像这样：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2019/08/640.png" class="kg-image" alt="640" width="600" height="400" loading="lazy"><figcaption>回调地狱</figcaption></figure><h3 id="--5">回调地狱</h3><p>指的是回调函数被嵌套在另一个回调函数中，嵌套层级有多层，使我们的代码可读性和可维护性变得非常差。</p><h3 id="-promise">使用 Promise</h3><pre><code class="language-javascript">const&nbsp;promiseFunction&nbsp;=&nbsp;new&nbsp;Promise((resolve,&nbsp;reject)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;const&nbsp;add&nbsp;=&nbsp;(a,&nbsp;b)&nbsp;=&gt;&nbsp;a&nbsp;+&nbsp;b;&nbsp;&nbsp;resolve(add(2,&nbsp;2));});promiseFunction.then((response)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;console.log(response);}).catch((error)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;console.log(error);});</code></pre><p>promiseFunction 这个函数返回一个表示函数运行过程的 Promise 对象， resolve 函数表示任务已经完成。</p><p>然后，我们就可以在 promiseFunction 函数的基础上调用 <code>then()</code> 和 <code>catch()</code> 方法。</p><p>then: Promise 成功后执行的回调函数。<br>catch: 当遇到了错误后执行的回调函数。</p><h3 id="async-">Async 函数</h3><p>Async 函数为我们提供了简洁的语法，我们可以通过它实现与 Promise 相同的效果，但是代码量会减少很多。其实底层原理上，async 不过是 promises 的语法糖而已。</p><p>在普通的函数声明前加上 async 关键字，我们就创建了一个 async 函数：</p><pre><code class="language-javascript">const&nbsp;asyncFunction&nbsp;=&nbsp;async&nbsp;()&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;//&nbsp;Code}</code></pre><p><br>异步函数使用 Await 表达式可以暂停函数的执行，Await 关键字只能在 Async 函数中使用，它会返回 Promise 对象的处理结果。</p><p>promises 和 Async/Await 的区别：</p><pre><code class="language-javascript">//&nbsp;Async/Awaitconst&nbsp;asyncGreeting&nbsp;=&nbsp;async&nbsp;()&nbsp;=&gt;&nbsp;'Greetings';//&nbsp;Promisesconst&nbsp;promiseGreeting&nbsp;=&nbsp;()&nbsp;=&gt;&nbsp;new&nbsp;Promise(((resolve)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;resolve('Greetings');}));asyncGreeting().then(result&nbsp;=&gt;&nbsp;console.log(result));promiseGreeting().then(result&nbsp;=&gt;&nbsp;console.log(result));</code></pre><p>Async/Await 更易于我们理解，因为它看起来像同步代码。</p><p>前面我们已经介绍过这些基础知识了，现在我们来看看在现实开发中怎么使用！</p><h3 id="--6">货币转换器</h3><h3 id="--7">项目说明和初始化</h3><p>在下面的教程中，我们将创建一个简单实用，而且很有学习意义的应用程序，相信会加深你对 Async/Await 理解。</p><p>这个程序会接收到我们想要把什么货币转换成什么货币，以及货币金额，然后会调用相关的 API，显示正确的汇率。</p><p>在这个程序中，我们将从以下两个 API 异步获取数据：</p><ol><li>Currency Layer —— https://currencylayer.com - 你需要先免费注册账号，才能获取 API Access Key。这个 API 为我们提供了 计算货币间汇率所需的数据。</li><li>Rest Countries —— http://restcountries.eu/ - 这个 API 为我们提供了给定一种货币，返回该货币在哪些国家流通。</li></ol><p>首先，创建一个新目录并运行 <code>npm init</code> 初始化项目，接下里我们选择默认值，跳过所有步骤，然后再输入 <code>npm i——save axios</code> 安装 axios。在当前文件夹内创建一个 <code>currency-convert .js</code> 的文件。</p><p>在 <code>currency-convert .js</code> 文件中，我们先通过 <code>require</code> 语法引入 axios。</p><pre><code class="language-javascript">const&nbsp;axios&nbsp;=&nbsp;require(‘axios’);`</code></pre><h3 id="-async-await"><br>深入理解 Async/Await</h3><p>这个程序中，我们需要三个异步函数，第一个函数用来获取关于货币的数据；第二个函数用来获取关于国家的数据；第三个函数用来将所有信息集中起来并展示给用户看</p><h3 id="--8">第一个函数——异步获取有关货币的数据</h3><p>我们创建一个接收两个参数（fromCurrency 和 toCurrency）的异步函数：</p><pre><code class="language-javascript">const&nbsp;getExchangeRate&nbsp;=&nbsp;async&nbsp;(fromCurrency,&nbsp;toCurrency)&nbsp;=&gt;&nbsp;{}</code></pre><p>现在我们获取数据，然后通过使用 Async/Await，可以直接将我们想要的数据赋值给变量。调用接口之前别忘了要注册账号，才能获得 API access key。</p><pre><code class="language-javascript">const&nbsp;getExchangeRate&nbsp;=&nbsp;async&nbsp;(fromCurrency,&nbsp;toCurrency)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;const&nbsp;response&nbsp;=&nbsp;await&nbsp;axios.get('http://data.fixer.io/api/latest?&nbsp;&nbsp;&nbsp;&nbsp;access_key=[yourAccessKey]&amp;format=1');}</code></pre><p>我们可以通过 <code>response.data.rates</code> 来提取我们想要的数据，然后我们将它赋值给一个变量 rate：</p><pre><code class="language-javascript">const&nbsp;rate&nbsp;=&nbsp;response.data.rates;</code></pre><p>因为所有的数据都是从欧元转换过来的，我们可以创建一个变量 <code>euro</code>，它的值等于：</p><pre><code class="language-javascript">const&nbsp;euro&nbsp;=&nbsp;1&nbsp;/&nbsp;rate[fromCurrency];</code></pre><p>最后，我们可以用欧元乘以我们要兑换的货币来得到汇率：</p><pre><code class="language-javascript">const&nbsp;exchangeRate&nbsp;=&nbsp;euro&nbsp;*&nbsp;rate[toCurrency];</code></pre><p>最终的函数看起来像这样：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/08/640-1.png" class="kg-image" alt="640-1" width="600" height="400" loading="lazy"></figure><h3 id="--9">第二个函数——异步获取国家数据</h3><p>创建一个异步函数，接收 currencyCode 作为参数：</p><pre><code class="language-javascript">const&nbsp;getCountries&nbsp;=&nbsp;async&nbsp;(currencyCode)&nbsp;=&gt;&nbsp;{}</code></pre><p>和之前一样，获取数据，然后将其赋值给一个变量：</p><pre><code class="language-javascript">const&nbsp;response&nbsp;=&nbsp;await&nbsp;axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);</code></pre><p>然后通过数组 map 方法将 <code>country.name</code> 提取出来，映射为一个新的数组：</p><pre><code class="language-javascript">return&nbsp;response.data.map(country&nbsp;=&gt;&nbsp;country.name);</code></pre><p>最终代码：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/08/640-2.png" class="kg-image" alt="640-2" width="600" height="400" loading="lazy"></figure><p>最后一个函数——将前面的函数组合起来</p><p>创建一个异步函数，接收 fromCurrency, toCurrency, amount 三个参数：</p><pre><code class="language-javascript">const&nbsp;convert&nbsp;=&nbsp;async&nbsp;(fromCurrency,&nbsp;toCurrency,&nbsp;amount)&nbsp;=&gt;&nbsp;{}</code></pre><p>第一步，获取货币数据：</p><pre><code class="language-javascript">const&nbsp;exchangeRate&nbsp;=&nbsp;await&nbsp;getExchangeRate(fromCurrency,&nbsp;toCurrency);</code></pre><p>第二步，获取国家数据：</p><pre><code class="language-javascript">const&nbsp;countries&nbsp;=&nbsp;await&nbsp;getCountries(toCurrency);</code></pre><p>第三步，将转换后的金额赋值给一个变量：</p><pre><code class="language-javascript">const&nbsp;convertedAmount&nbsp;=&nbsp;(amount&nbsp;*&nbsp;exchangeRate).toFixed(2);</code></pre><p>最后，将数据输出给用户</p><pre><code class="language-javascript">return&nbsp;`${amount}&nbsp;${fromCurrency}&nbsp;is&nbsp;worth&nbsp;${convertedAmount}&nbsp;${toCurrency}.&nbsp;You&nbsp;can&nbsp;spend&nbsp;these&nbsp;in&nbsp;the&nbsp;following&nbsp;countries:&nbsp;${countries}`;</code></pre><p>最后完整的代码：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/08/640-3.png" class="kg-image" alt="640-3" width="600" height="400" loading="lazy"></figure><h3 id="-try-catch-"><br>使用 try/catch 来处理错误</h3><p>我们将程序的逻辑用 try 语句包裹起来，如果出现错误，用 catch 语句捕捉：</p><pre><code class="language-javascript">const&nbsp;getExchangeRate&nbsp;=&nbsp;async&nbsp;(fromCurrency,&nbsp;toCurrency)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;response&nbsp;=&nbsp;await&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;axios.get('http://data.fixer.io/api/latest?access_key=f68b13604ac8e570a00f7d8fe7f25e1b&amp;format=1');&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;rate&nbsp;=&nbsp;response.data.rates;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;euro&nbsp;=&nbsp;1&nbsp;/&nbsp;rate[fromCurrency];&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;exchangeRate&nbsp;=&nbsp;euro&nbsp;*&nbsp;rate[toCurrency];&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;exchangeRate;&nbsp;&nbsp;}&nbsp;catch&nbsp;(error)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;Error(`Unable&nbsp;to&nbsp;get&nbsp;currency&nbsp;${fromCurrency}&nbsp;and&nbsp;&nbsp;${toCurrency}`);&nbsp;&nbsp;}};</code></pre><p>同样第二个函数也这样处理：</p><pre><code class="language-javascript">const&nbsp;getCountries&nbsp;=&nbsp;async&nbsp;(currencyCode)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;response&nbsp;=&nbsp;await&nbsp;axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);return&nbsp;response.data.map(country&nbsp;=&gt;&nbsp;country.name);&nbsp;&nbsp;}&nbsp;catch&nbsp;(error)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;Error(`Unable&nbsp;to&nbsp;get&nbsp;countries&nbsp;that&nbsp;use&nbsp;${currencyCode}`);&nbsp;&nbsp;}};</code></pre><p>因为第三个函数只是处理第一个和第二个函数的结果，所以我们不需要对它进行错误捕获。</p><p>最后，我们调用函数来接收数据：</p><pre><code class="language-javascript">convertCurrency('USD',&nbsp;'HRK',&nbsp;20)&nbsp;&nbsp;.then((message)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;console.log(message);&nbsp;&nbsp;}).catch((error)&nbsp;=&gt;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;console.log(error.message);&nbsp;&nbsp;});</code></pre><p>你会看到下面的结果：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/08/640-4.png" class="kg-image" alt="640-4" width="600" height="400" loading="lazy"></figure><h3></h3><p>很不错，你坚持到了最后！如果在学习过程中遇到了困惑的地方，你可以参考这个<a href="https://github.com/adrianhajdin/tutorial_currency_converter">仓库</a>的代码。如果你有任何问题，可以在下面留言。</p><p>原文链接：<a href="https://medium.com/free-code-camp/how-to-master-async-await-with-this-real-world-example-19107e7558ad">How To Master Async/Await With This Real World Example</a>，作者：<a href="https://medium.com/@adrianhajdin00?source=post_page-----19107e7558ad--------------------------------" rel="noopener">Adrian Hajdin</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 一个不能再普通的函数，如何提供 http 服务？ ]]>
                </title>
                <description>
                    <![CDATA[ 前调：一些吐槽 因为开发过大大大大大大量的 Restful API ，越来越厌烦那种一成不变的代码组织方式。 以 egg为例，要增加一个接口，需要经历繁琐的操作 [路由里注册] ---> [编写 controller] ---> [编写 service]  哪怕我只是想实现一个 a+b => c！ 除此以外，我还调研过某几个比较大的 函数计算 服务商，看看有没有什么好的途径，可以更简便地进行简单接口开发。 让人很失望，天下技术一大抄，它们无一例外都是这样的开发模型： function(event, context, callback) {} 或者 function(event, context) {} 于是你不得不这样写你的代码 // 举例 function(event, context, callback) {   const { a, b } = event.arguments;    ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/way-to-build-restful-api-via-pure-function/</link>
                <guid isPermaLink="false">600a8ecf5f61e30501b5c037</guid>
                
                    <category>
                        <![CDATA[ 函数 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTTP ]]>
                    </category>
                
                    <category>
                        <![CDATA[ API ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 开发者小蓝 ]]>
                </dc:creator>
                <pubDate>Fri, 22 Jan 2021 09:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/01/goran-ivos-TorAcb4AQRc-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h3 id="">前调：一些吐槽</h3>
<p>因为开发过大大大大大大量的 <code>Restful API</code> ，越来越厌烦那种一成不变的代码组织方式。</p>
<p>以 <code>egg</code>为例，要增加一个接口，需要经历繁琐的操作</p>
<pre><code>[路由里注册] ---&gt; [编写 controller] ---&gt; [编写 service] 
</code></pre>
<h4 id="abc">哪怕我只是想实现一个 <code>a+b =&gt; c</code>！</h4>
<p>除此以外，我还调研过某几个比较大的 <strong>函数计算</strong> 服务商，看看有没有什么好的途径，可以更简便地进行<strong>简单接口开发</strong>。</p>
<p>让人很失望，天下技术一大抄，它们无一例外都是这样的开发模型：</p>
<pre><code>function(event, context, callback) {}
或者
function(event, context) {}
</code></pre>
<p>于是你不得不这样写你的代码</p>
<pre><code>// 举例
function(event, context, callback) {
  const { a, b } = event.arguments; 
  callback(a+b);
}
</code></pre>
<h4 id="">这算个什么鬼函数？！</h4>
<p>你必须改变第一直觉，严格按照厂商的要求来写你的代码，并且寄希望于<strong>有人站出来要求它们统一<code>event</code>、<code>context</code>这些旁系知识的实现细节</strong>。</p>
<h3 id="">并且！</h3>
<p>你很难进行本地测试！</p>
<p>你要构造奇奇怪怪的对象作为参数。哪怕只是测试<strong>两数相加</strong>.</p>
<h5 id="ok">OK! 如果你说，稍微学习一下，其实还是可以用的吧。</h5>
<h4 id="">可以用就是我们的追求吗？要 <strong>应然</strong> 还是 <strong>实然</strong> ？</h4>
<h4 id="">难倒我们期望的样子，不应该是这样吗？</h4>
<pre><code>// 🔥🔥
function (a, b) {
	return a+b;
}
</code></pre>
<h3 id="">正文开始：</h3>
<p>所以，如果我需要实现一个 <code>a+b =&gt; c</code> 的接口，</p>
<p>我期望我的代码是这样的：</p>
<pre><code>export default (a: number, b: number): number =&gt; {
  return a + b;
}
</code></pre>
<p>我期望我的测试是这样的：</p>
<pre><code>describe('sum', () =&gt; {
  it('1+1 is 2', () =&gt; {
    assert.equal(sum(1,1), 2);
  });
});
</code></pre>
<h4 id="">十分明显好吗！</h4>
<p>它并不受制于具体平台实现；</p>
<p>它并不需要你学习其他旁系知识，只关注你的功能本身；</p>
<p>它方便测试，因为它就是一个不能再普通的函数；</p>
<p>它可复用！</p>
<p><img src="https://lanhaooss.oss-cn-shenzhen.aliyuncs.com/images/331/1.png" alt="1" width="600" height="400" loading="lazy"></p>
<h3 id="">多说无益，直接动手</h3>
<pre><code>export default (a: number, b: number): number =&gt; {
  return a + b;
}
</code></pre>
<p>有过<a href="https://chinese.freecodecamp.org/news/practice-in-ast-and-code-gen/">上一篇</a>的基础，我们直奔抽象语法树：</p>
<pre><code>// 由于只有一行代码，结构就简单多了
{
	type: 'ExportDefaultDeclaration',
	declaration: {
		type: 'ArrowFunctionExpression',
		params: [ 下文展开 ],
		body: { 不重要 },
		returnType: { 下文展开 }
	}
}

</code></pre>
<p>我们还是从整体上先看一下，上面这个结构充分表达了源代码的意图：</p>
<ul>
<li>这是一个 <code>export default</code>声明 (<code>ExportDefaultDeclaration </code>)</li>
<li>声明的内容是一个箭头函数 (<code>ArrowFunctionExpression </code>)</li>
</ul>
<p>考虑到，我们希望从这一行代码里面，生成<strong>提供HTTP服务</strong>的基础代码，</p>
<p>那么我们的主要任务就是，从以上有限的信息当中，提取出<strong>Restful API</strong>需要的基础信息：</p>
<ul>
<li>Method</li>
<li>Path</li>
<li>请求参数</li>
</ul>
<h4 id="">🤖 我们一个一个来解决：</h4>
<ol>
<li>
<p>HTTP Method</p>
<p>首选是要确定这个接口，最终通过什么 <code>Method</code>对外服务，在这里源代码并不能提供任何有效信息。</p>
<p><strong>那么我们就默认用 GET 好了</strong></p>
<blockquote>
<p>🔥其他的 Method，会在文末提及</p>
</blockquote>
</li>
<li>
<p>Path， 或者叫 URL</p>
<p>对于一个接口来说，这也是十分关键的信息，这里我们有两种解决方案</p>
<ul>
<li>使用函数名，如果有的话</li>
<li>使用当前文件名</li>
</ul>
<p><strong>我们姑且把 URL 定为  <code>/sum</code> 好了</strong></p>
<blockquote>
<p>🔥 笔者的一个实验性项目里，使用的是文件名</p>
</blockquote>
</li>
<li>
<p>请求参数</p>
<p>如果说前面两点都是基于约定，或者一些简单的手段，</p>
<p>那么在请求参数这个环节，我们必须上价值了，要让 <code>ast</code> 发挥作用！</p>
<p>我们先展开一下上面 <code>ast</code> 里关于函数参数的部分：</p>
<pre><code>// params 部分
[
	{
		name: 'a',
		typeAnnotation: { type: 'TSNumberKeyword' }
	},
	{
		name: 'b',
		typeAnnotation: { type: 'TSNumberKeyword' }
	},
]
</code></pre>
<h4 id="ab">好家伙，可以拿到参数名 <code>a</code> 和 <code>b</code> 了</h4>
<p>结合 <code>TSNumberKeyword</code> 信息，我们甚至能笃定这两个参数是数字类型。</p>
<h4 id="">知道类型，就可以做参数校验！</h4>
<blockquote>
<p>🔥通过原始函数定义的参数类型生成 http 参数校验逻辑，比起手工编写 <code>Joi</code> 配置要可靠得多。</p>
</blockquote>
<h4 id="">等等，还有一个问题没解决</h4>
<p>参数名和参数类型都有了，缺的就是参数位置以及 <code>content-type</code> 。</p>
<p>约定大法好，根据多年开发总结得出：</p>
<p><strong>总所周知， GET 请求的参数就放在 queryString 里吧。</strong></p>
<p><strong>至于 <code>content-type</code> 统一使用 <code>application/json</code> 不接受反驳</strong></p>
</li>
</ol>
<h3 id="">齐活了</h3>
<p>结合上面收集到的信息，我们可以想象最终的场景是：</p>
<ul>
<li>step 1 : 按上面的方式，编写一个不能再普通的函数</li>
<li>step 2: 使用我们编写好的工具，运行这个函数</li>
<li>step 3: 可以通过 <code>http://127.0.0.1:3000/sum?a=11&amp;b=22</code> 来访问这个接口，并且得到结果 33</li>
</ul>
<p><img src="https://lanhaooss.oss-cn-shenzhen.aliyuncs.com/images/331/2.png" alt="2" width="600" height="400" loading="lazy"></p>
<p>通过这个截图可知，虽然实现细节比较多，但是<strong>可行性</strong>是没问题的。</p>
<p>并且笔者已经做出了一个实验性项目。</p>
<p>如果对这种模式比较感兴趣，欢迎前来讨论，在这里就不打广告了。</p>
<h3 id="faq">FAQ 环节</h3>
<ul>
<li>
<p>更多的 HTTP_METHOD 怎么办？总不能都用 GET 吧</p>
<blockquote>
<p>目前我选能用的方案是，如果没有声明，就用一个默认的 <code>GET</code>，</p>
</blockquote>
<blockquote>
<p>因为要尽量选一个能覆盖 90% 情况的作为默认值。</p>
</blockquote>
<blockquote>
<p>当我需要使用其他 Method ，我的方案是显式指定，比如</p>
</blockquote>
<pre><code>export const method = 'POST';

export default (a: number, b: number): number =&gt; {
  return a + b;
}
</code></pre>
<p>这个方案可以使信息更紧凑，同时不影响函数逻辑跑本地单元测试。</p>
</li>
<li>
<p>上面提到的入参参数位置，什么时候在 <code>queryString</code>，什么时候在其他？</p>
<blockquote>
<p>遵从大多数的案例，GET 的情况下使用 <code>queryString</code>， 其他情况下在 <code>body</code></p>
</blockquote>
<blockquote>
<p>当然还有完全自定义的方案，为避广告嫌疑就不展开了，基本原则还是不影响函数核心逻辑和单元测试。</p>
</blockquote>
</li>
<li>
<p>上面提到的 入参参数类型 ，有什么应用场景？</p>
<blockquote>
<p>有了这个信息，如果你喜欢 <code>Joi</code>， 应该能很容易生成参数校验的代码了。</p>
</blockquote>
<blockquote>
<p>或者有自己想法的话，和我一样，自己实现一套 <code>validator</code> 也不是什么难事。</p>
</blockquote>
</li>
</ul>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 详解 JavaScript 回调函数 ]]>
                </title>
                <description>
                    <![CDATA[ 如果你了解编程，你应该已经知道函数的功能以及如何使用函数。但是，什么是回调函数呢？回调函数是 JavaScript 的一个重要部分。如果你理解了它如何运行，你就能更擅长编写 JavaScript。 在这篇文章里，我会通过一些例子帮助你理解什么是回调函数，如何在 JavaScript 中使用它们。 什么是回调函数？ 在 JavaScript 中，函数即对象。我们可以将对象作为参数传递给函数吗？答案是“可以”。 所以，我们可以将函数作为参数传递给其他函数，在外部函数中调用它。听起来有点复杂？我们看一下下面的例子： function print(callback) {       callback(); } print( ) 函数将另一个函数作为参数，并在函数体内部调用它。在 JavaScript 里，我们叫它“回调”。所以，被传递给另一个函数作为参数的函数叫作回调函数。 为什么需要回调函数？ JavaScript 按从上到下的顺序运行代码。但是，在有些情况下，必须在某些情况发生之后，代码才能运行（或者说必须运行），这就不是按顺序运行了。这是异步编程。 回调函数确保：函数在某个 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-callback-functions/</link>
                <guid isPermaLink="false">5fc7735f39641a0517d51646</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 函数 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 前端开发 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Thu, 03 Dec 2020 03:24:41 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/12/nathan-fertig-U8zsjmKA840-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>如果你了解编程，你应该已经知道函数的功能以及如何使用函数。但是，什么是回调函数呢？回调函数是 JavaScript 的一个重要部分。如果你理解了它如何运行，你就能更擅长编写 JavaScript。</p><p>在这篇文章里，我会通过一些例子帮助你理解什么是回调函数，如何在 JavaScript 中使用它们。</p><h2 id="-"><strong>什么是回调函数？</strong></h2><p>在 JavaScript 中，函数即对象。我们可以将对象作为参数传递给函数吗？答案是“可以”。</p><p>所以，我们可以将函数作为参数传递给其他函数，在外部函数中调用它。听起来有点复杂？我们看一下下面的例子：</p><pre><code class="language-javascript">function print(callback) {  
    callback();
}</code></pre><p>print( ) 函数将另一个函数作为参数，并在函数体内部调用它。在 JavaScript 里，我们叫它“回调”。所以，被传递给另一个函数作为参数的函数叫作回调函数。</p><h3 id="--1"><strong>为什么需要回调函数？</strong></h3><p>JavaScript 按从上到下的顺序运行代码。但是，在有些情况下，必须在某些情况发生之后，代码才能运行（或者说必须运行），这就不是按顺序运行了。这是异步编程。</p><p>回调函数确保：函数在某个任务完成之前不运行，在任务完成之后立即运行。它帮助我们编写异步 JavaScript 代码，避免问题和错误。</p><p>在 JavaScript 里创建回调函数的方法是将它作为参数传递给另一个函数，然后当某个任务完成之后，立即调用它。</p><h2 id="--2"><strong>如何创建回调函数</strong></h2><p>我们通过一个简单的例子来理解上面的内容。我们想在控制台打印一条消息（message），它在 3 秒之后显示。</p><pre><code class="language-javascript">const message = function() {  
    console.log("This message is shown after 3 seconds");
}
 
setTimeout(message, 3000);</code></pre><p>JavaScript 有一个内建方法叫作 “setTimeout”，它会在给定的时间段（以毫秒为单位）后调用函数或计算表达式。因此，在这里，经过 3 秒后将调用 message 函数（1 秒= 1000 毫秒） 。</p><p>换句话说，message 函数是在发生某事之后（在本示例中为 3 秒之后），而不是在此之前被调用。因此，message 函数就是一个回调函数</p><h3 id="--3"><strong>什么是匿名函数？</strong></h3><p>此外，我们可以直接在另一个函数内定义一个函数，而不是调用它，比如：</p><pre><code class="language-javascript">setTimeout(function() {  
    console.log("This message is shown after 3 seconds");
}, 3000);</code></pre><p>我们可以看到，这里的回调函数没有名称。在 JavaScript 里，一个没有名称的函数叫作“匿名函数”。这个例子和上面的例子执行的任务一样。</p><h3 id="--4"><strong>用箭头函数写回调函数</strong></h3><p>你也可以用 ES6 箭头函数写回调函数。箭头函数是 JavaScript 里的一种新的函数形式。</p><pre><code class="language-javascript">setTimeout(() =&gt; { 
    console.log("This message is shown after 3 seconds");
}, 3000);</code></pre><h2 id="--5"><strong>什么是事件？</strong></h2><p>JavaScript 是一种事件驱动的编程语言。我们还将回调函数用于事件声明。例如，假设我们希望用户单击按钮：</p><pre><code class="language-html">&lt;button id="callback-btn"&gt;Click here&lt;/button&gt;</code></pre><p>这次，只有当用户单击按钮时，我们才会在控制台上看到一条消息：</p><pre><code class="language-javascript">document.queryselector("#callback-btn")
    .addEventListener("click", function() {    
      console.log("User has clicked on the button!");
});</code></pre><p>因此，在这里我们首先通过 ID 选择按钮，然后使用 addEventListener 方法添加一个事件监听器。它有 2 个参数。第一个是其类型，即“click（单击）”，第二个参数是回调函数，该函数记录单击按钮时的消息。 </p><p>所以，回调函数也用于 JavaScript 中的事件声明。</p><h2 id="--6"><strong>总结</strong></h2><p>回调函数在 JavaScript 编程中比较常用。我希望这篇文章可以帮助你理解它是如何运行的，以及如何更好地使用它。接下来，可以学习另一个主题：<a href="https://www.freecodecamp.org/news/javascript-es6-promises-for-beginners-resolve-reject-and-chaining-explained/">JavaScript Promises</a>。</p><p>谢谢阅读！</p><p>原文：<a href="https://www.freecodecamp.org/news/javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/">JavaScript Callback Functions – What are Callbacks in JS and How to Use Them</a>，作者：Cem Eygi</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 的 this 指向 ]]>
                </title>
                <description>
                    <![CDATA[ 面试官出很多考题，基本都会变着方式来考察this指向，看候选人对 JavaScript 基础知识是否扎实。 读者可以先拉到底部看总结，再谷歌（或各技术平台）搜索几篇类似文章，看笔者写的文章和别人有什么不同（欢迎在评论区评论不同之处），对比来看，验证与自己现有知识是否有盲点，多看几篇，自然就会完善自身知识。 附上之前写文章写过的一段话：已经有很多关于this 的文章，为什么自己还要写一遍呢。学习就好比是座大山，人们沿着不同的路登山，分享着自己看到的风景。你不一定能看到别人看到的风景，体会到别人的心情。只有自己去登山，才能看到不一样的风景，体会才更加深刻。 函数的this在调用时绑定的，完全取决于函数的调用位置（也就是函数的调用方法）。为了搞清楚this的指向是什么，必须知道相关函数是如何调用的。 全局上下文 非严格模式和严格模式中this都是指向顶层对象（浏览器中是window）。 this === window // true 'use strict' this === window; this.name = '若川'; console.log(this.name); // 若 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/javascript-this/</link>
                <guid isPermaLink="false">5f8d6eef5f583f0565090b28</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 函数 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web开发 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 若川 ]]>
                </dc:creator>
                <pubDate>Mon, 19 Oct 2020 08:40:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/05/daria-shevtsova-zbWFT4eVopE-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>面试官出很多考题，基本都会变着方式来考察<code>this</code>指向，看候选人对 <code>JavaScript</code> 基础知识是否扎实。 读者可以先拉到底部看总结，再谷歌（或各技术平台）搜索几篇类似文章，看笔者写的文章和别人有什么不同（欢迎在评论区评论不同之处），对比来看，验证与自己现有知识是否有盲点，多看几篇，自然就会完善自身知识。</p><p>附上之前写文章写过的一段话：已经有很多关于<code>this</code>的文章，为什么自己还要写一遍呢。学习就好比是座大山，人们沿着不同的路登山，分享着自己看到的风景。你不一定能看到别人看到的风景，体会到别人的心情。只有自己去登山，才能看到不一样的风景，体会才更加深刻。</p><p>函数的<code>this</code>在调用时绑定的，完全取决于函数的调用位置（也就是函数的调用方法）。为了搞清楚<code>this</code>的指向是什么，必须知道相关函数是如何调用的。</p><h2 id="-">全局上下文</h2><p>非严格模式和严格模式中this都是指向顶层对象（浏览器中是<code>window</code>）。</p><pre><code class="language-js">this === window // true
'use strict'
this === window;
this.name = '若川';
console.log(this.name); // 若川
</code></pre><h2 id="--1">函数上下文</h2><h3 id="--2">普通函数调用模式</h3><pre><code class="language-js">// 非严格模式
var name = 'window';
var doSth = function(){
    console.log(this.name);
}
doSth(); // 'window'
</code></pre><p>你可能会误以为<code>window.doSth()</code>是调用的，所以是指向<code>window</code>。虽然本例中<code>window.doSth</code>确实等于<code>doSth</code>。<code>name</code>等于<code>window.name</code>。上面代码中这是因为在<code>ES5</code>中，全局变量是挂载在顶层对象（浏览器是<code>window</code>）中。 事实上，并不是如此。</p><pre><code class="language-js">// 非严格模式
let name2 = 'window2';
let doSth2 = function(){
    console.log(this === window);
    console.log(this.name2);
}
doSth2() // true, undefined
</code></pre><p>这个例子中<code>let</code>没有给顶层对象中（浏览器是window）添加属性，<code>window.name2和window.doSth</code>都是<code>undefined</code>。</p><p>严格模式中，普通函数中的<code>this</code>则表现不同，表现为<code>undefined</code>。</p><pre><code class="language-js">// 严格模式
'use strict'
var name = 'window';
var doSth = function(){
    console.log(typeof this === 'undefined');
    console.log(this.name);
}
doSth(); // true，// 报错，因为this是undefined
</code></pre><p>看过的《你不知道的<code>JavaScript</code>》上卷的读者，应该知道书上将这种叫做默认绑定。 对<code>call</code>，<code>apply</code>熟悉的读者会类比为：</p><pre><code class="language-js">doSth.call(undefined);
doSth.apply(undefined);
</code></pre><p>效果是一样的，<code>call</code>，<code>apply</code>作用之一就是用来修改函数中的<code>this</code>指向为第一个参数的。 第一个参数是<code>undefined</code>或者<code>null</code>，非严格模式下，是指向<code>window</code>。严格模式下，就是指向第一个参数。后文详细解释。<br>经常有这类代码（回调函数），其实也是普通函数调用模式。</p><pre><code class="language-js">var name = '若川';
setTimeout(function(){
    console.log(this.name);
}, 0);
// 语法
setTimeout(fn | code, 0, arg1, arg2, ...)
// 也可以是一串代码。也可以传递其他函数
// 类比 setTimeout函数内部调用fn或者执行代码`code`。
fn.call(undefined, arg1, arg2, ...);
</code></pre><h3 id="--3">对象中的函数（方法）调用模式</h3><pre><code class="language-js">var name = 'window';
var doSth = function(){
    console.log(this.name);
}
var student = {
    name: '若川',
    doSth: doSth,
    other: {
        name: 'other',
        doSth: doSth,
    }
}
student.doSth(); // '若川'
student.other.doSth(); // 'other'
// 用call类比则为：
student.doSth.call(student);
// 用call类比则为：
student.other.doSth.call(student.other);
</code></pre><p>但往往会有以下场景，把对象中的函数赋值成一个变量了。 这样其实又变成普通函数了，所以使用普通函数的规则（默认绑定）。</p><pre><code class="language-js">var studentDoSth = student.doSth;
studentDoSth(); // 'window'
// 用call类比则为：
studentDoSth.call(undefined);
</code></pre><h3 id="call-apply-bind-"><code>call、apply、bind</code> 调用模式</h3><p>上文提到<code>call</code>、<code>apply</code>，这里详细解读一下。先通过<code>MDN</code>认识下<code>call</code>和<code>apply</code> <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call" rel="noopener noreferrer">MDN 文档：Function.prototype.call()</a>。</p><p><strong>语法</strong></p><pre><code class="language-js">fun.call(thisArg, arg1, arg2, ...)
</code></pre><p><strong>thisArg</strong><br>在<code>fun</code>函数运行时指定的<code>this</code>值。需要注意的是，指定的<code>this</code>值并不一定是该函数执行时真正的<code>this</code>值，如果这个函数处于<strong>非严格模式</strong>下，则指定为<code>null</code>和<code>undefined</code>的<code>this</code>值会自动指向全局对象(浏览器中就是<code>window</code>对象)，同时值为原始值(数字，字符串，布尔值)的<code>this</code>会指向该原始值的自动包装对象。<br><strong>arg1, arg2, ...</strong><br>指定的参数列表<br><strong>返回值</strong><br>返回值是你调用的方法的返回值，若该方法没有返回值，则返回<code>undefined</code>。<br><code>apply</code>和<code>call</code>类似。只是参数不一样。它的参数是数组（或者类数组）。</p><p>根据参数<code>thisArg</code>的描述，可以知道，<code>call</code>就是改变函数中的<code>this</code>指向为<code>thisArg</code>，并且执行这个函数，这也就使 <code>JavaScript</code> 灵活很多。严格模式下，<code>thisArg</code>是原始值是值类型，也就是原始值。不会被包装成对象。举个例子：</p><pre><code class="language-js">var doSth = function(name){
    console.log(this);
    console.log(name);
}
doSth.call(2, '若川'); // Number{2}, '若川'
var doSth2 = function(name){
    'use strict';
    console.log(this);
    console.log(name);
}
doSth2.call(2, '若川'); // 2, '若川'
</code></pre><p>虽然一般不会把<code>thisArg</code>参数写成值类型。但还是需要知道这个知识。 之前写过一篇文章：<a href="https://juejin.im/post/5bf6c79bf265da6142738b29" rel="noopener noreferrer">面试官问：能否模拟实现<code>JS</code>的<code>call</code>和<code>apply</code>方法</a> 就是利用对象上的函数<code>this</code>指向这个对象，来模拟实现<code>call</code>和<code>apply</code>的。感兴趣的读者思考如何实现，再去看看笔者的实现。</p><p><code>bind</code>和<code>call</code>和<code>apply</code>类似，第一个参数也是修改<code>this</code>指向，只不过返回值是新函数，新函数也能当做构造函数（<code>new</code>）调用。 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind" rel="noopener noreferrer">MDN Function.prototype.bind</a></p><p><code>bind()</code>方法创建一个新的函数， 当这个新函数被调用时<code>this</code>键值为其提供的值，其参数列表前几项值为创建时指定的参数序列。<br><strong>语法：</strong> fun.bind(thisArg[, arg1[, arg2[, ...]]])<br><strong>参数：</strong> <strong>thisArg</strong> 调用绑定函数时作为this参数传递给目标函数的值。 如果使用<code>new</code>运算符构造绑定函数，则忽略该值。当使用<code>bind</code>在<code>setTimeout</code>中创建一个函数（作为回调提供）时，作为<code>thisArg</code>传递的任何原始值都将转换为<code>object</code>。如果没有提供绑定的参数，则执行作用域的<code>this</code>被视为新函数的<code>thisArg</code>。 <strong>arg1, arg2, ...</strong> 当绑定函数被调用时，这些参数将置于实参之前传递给被绑定的方法。 <strong>返回值</strong> 返回由指定的<code>this</code>值和初始化参数改造的原函数拷贝。</p><p>之前也写过一篇文章：<a href="https://chinese.freecodecamp.org/news/javascript-bind-method/">如何模拟实现 JS 的 bind 方法</a>，就是利用<code>call</code>和<code>apply</code>指向这个<code>thisArg</code>参数，来模拟实现<code>bind</code>的。感兴趣的读者思考如何实现，再去看看笔者的实现。</p><h3 id="--4">构造函数调用模式</h3><pre><code class="language-js">function Student(name){
    this.name = name;
    console.log(this); // {name: '若川'}
    // 相当于返回了
    // return this;
}
var result = new Student('若川');
</code></pre><p>使用<code>new</code>操作符调用函数，会自动执行以下步骤。</p><ol><li>创建了一个全新的对象。</li><li>这个对象会被执行<code>[[Prototype]]</code>（也就是<code>__proto__</code>）链接。</li><li>生成的新对象会绑定到函数调用的<code>this</code>。</li><li>通过<code>new</code>创建的每个对象将最终被<code>[[Prototype]]</code>链接到这个函数的<code>prototype</code>对象上。</li><li>如果函数没有返回对象类型<code>Object</code>(包含<code>Functoin</code>, <code>Array</code>, <code>Date</code>, <code>RegExg</code>, <code>Error</code>)，那么<code>new</code>表达式中的函数调用会自动返回这个新的对象。</li></ol><p>由此可以知道：<code>new</code>操作符调用时，<code>this</code>指向生成的新对象。 <strong>特别提醒一下，<code>new</code>调用时的返回值，如果没有显式返回对象或者函数，才是返回生成的新对象</strong>。</p><pre><code class="language-js">function Student(name){
    this.name = name;
    // return function f(){};
    // return {};
}
var result = new Student('若川');
console.log(result); {name: '若川'}
// 如果返回函数f，则result是函数f，如果是对象{}，则result是对象{}
</code></pre><p>很多人或者文章都忽略了这一点，直接简单用<code>typeof</code>判断对象。虽然实际使用时不会显示返回，但面试官会问到。</p><p>之前也写了一篇文章<a href="https://juejin.im/post/5bde7c926fb9a049f66b8b52" rel="noopener noreferrer">面试官问：能否模拟实现<code>JS</code>的<code>new</code>操作符</a>，是使用apply来把this指向到生成的新生成的对象上。感兴趣的读者思考如何实现，再去看看笔者的实现。</p><h3 id="--5">原型链中的调用模式</h3><pre><code class="language-js">function Student(name){
    this.name = name;
}
var s1 = new Student('若川');
Student.prototype.doSth = function(){
    console.log(this.name);
}
s1.doSth(); // '若川'
</code></pre><p>会发现这个似曾相识。这就是对象上的方法调用模式。自然是指向生成的新对象。 如果该对象继承自其它对象。同样会通过原型链查找。 上面代码使用 <code>ES6</code>中<code>class</code>写法则是：</p><pre><code class="language-js">class Student{
    constructor(name){
        this.name = name;
    }
    doSth(){
        console.log(this.name);
    }
}
let s1 = new Student('若川');
s1.doSth();
</code></pre><p><code>babel</code> <code>es6</code>转换成<code>es5</code>的结果，可以去<a href="https://babeljs.io/" rel="noopener noreferrer"><code>babeljs网站转换测试</code></a>自行试试。</p><pre><code class="language-js">'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i &lt; props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Student = function () {
    function Student(name) {
        _classCallCheck(this, Student);

        this.name = name;
    }

    _createClass(Student, [{
        key: 'doSth',
        value: function doSth() {
            console.log(this.name);
        }
    }]);

    return Student;
}();

var s1 = new Student('若川');
s1.doSth();
</code></pre><p>由此看出，<code>ES6</code>的<code>class</code>也是通过构造函数模拟实现的，是一种语法糖。</p><h3 id="--6">箭头函数调用模式</h3><p>先看箭头函数和普通函数的重要区别：</p><p>1、没有自己的<code>this</code>、<code>super</code>、<code>arguments</code>和<code>new.target</code>绑定。 2、不能使用<code>new</code>来调用。 3、没有原型对象。 4、不可以改变<code>this</code>的绑定。 5、形参名称不能重复。</p><p>箭头函数中没有<code>this</code>绑定，必须通过查找作用域链来决定其值。 如果箭头函数被非箭头函数包含，则<code>this</code>绑定的是最近一层非箭头函数的<code>this</code>，否则<code>this</code>的值则被设置为全局对象。 比如：</p><pre><code class="language-js">var name = 'window';
var student = {
    name: '若川',
    doSth: function(){
        // var self = this;
        var arrowDoSth = () =&gt; {
            // console.log(self.name);
            console.log(this.name);
        }
        arrowDoSth();
    },
    arrowDoSth2: () =&gt; {
        console.log(this.name);
    }
}
student.doSth(); // '若川'
student.arrowDoSth2(); // 'window'
</code></pre><p>其实就是相当于箭头函数外的<code>this</code>是缓存的该箭头函数上层的普通函数的<code>this</code>。如果没有普通函数，则是全局对象（浏览器中则是<code>window</code>）。 也就是说无法通过<code>call</code>、<code>apply</code>、<code>bind</code>绑定箭头函数的<code>this</code>(它自身没有<code>this</code>)。而<code>call</code>、<code>apply</code>、<code>bind</code>可以绑定缓存箭头函数上层的普通函数的<code>this</code>。 比如：</p><pre><code class="language-js">var student = {
    name: '若川',
    doSth: function(){
        console.log(this.name);
        return () =&gt; {
            console.log('arrowFn:', this.name);
        }
    }
}
var person = {
    name: 'person',
}
student.doSth().call(person); // '若川'  'arrowFn:' '若川'
student.doSth.call(person)(); // 'person' 'arrowFn:' 'person'
</code></pre><h3 id="dom-"><code>DOM</code>事件处理函数调用</h3><h4 id="addeventerlistener-attachevent-onclick">addEventerListener、attachEvent、onclick</h4><pre><code class="language-js">&lt;button class="button"&gt;onclick&lt;/button&gt;
&lt;ul class="list"&gt;
    &lt;li&gt;1&lt;/li&gt;
    &lt;li&gt;2&lt;/li&gt;
    &lt;li&gt;3&lt;/li&gt;
&lt;/ul&gt;
&lt;script&gt;
    var button = document.querySelector('button');
    button.onclick = function(ev){
        console.log(this);
        console.log(this === ev.currentTarget); // true
    }
    var list = document.querySelector('.list');
    list.addEventListener('click', function(ev){
        console.log(this === list); // true
        console.log(this === ev.currentTarget); // true
        console.log(this);
        console.log(ev.target);
    }, false);
&lt;/script&gt;
</code></pre><p><code>onclick</code>和<code>addEventerListener</code>是指向绑定事件的元素。 一些浏览器，比如<code>IE6~IE8</code>下使用<code>attachEvent</code>，<code>this</code>指向是<code>window</code>。 顺便提下：面试官也经常考察<code>ev.currentTarget</code>和<code>ev.target</code>的区别。 <code>ev.currentTarget</code>是绑定事件的元素，而<code>ev.target</code>是当前触发事件的元素。比如这里的分别是<code>ul</code>和<code>li</code>。 但也可能点击的是<code>ul</code>，这时<code>ev.currentTarget</code>和<code>ev.target</code>就相等了。</p><h4 id="--7">内联事件处理函数调用</h4><pre><code class="language-html">&lt;button class="btn1" onclick="console.log(this === document.querySelector('.btn1'))"&gt;点我呀&lt;/button&gt;
&lt;button onclick="console.log((function(){return this})());"&gt;再点我呀&lt;/button&gt;
</code></pre><p>第一个是<code>button</code>本身，所以是<code>true</code>，第二个是<code>window</code>。这里跟严格模式没有关系。 当然我们现在不会这样用了，但有时不小心写成了这样，也需要了解。</p><p>其实<code>this</code>的使用场景还有挺多，比如对象<code>object</code>中的<code>getter</code>、<code>setter</code>的<code>this</code>，<code>new Function()</code>、<code>eval</code>。 但掌握以上几种，去分析其他的，就自然迎刃而解了。 使用比较多的还是普通函数调用、对象的函数调用、<code>new</code>调用、<code>call、apply、bind</code>调用、箭头函数调用。 那么他们的优先级是怎样的呢。</p><h3 id="--8">优先级</h3><p>而箭头函数的<code>this</code>是上层普通函数的<code>this</code>或者是全局对象（浏览器中是<code>window</code>），所以排除，不算优先级。</p><pre><code class="language-js">var name = 'window';
var person = {
    name: 'person',
}
var doSth = function(){
    console.log(this.name);
    return function(){
        console.log('return:', this.name);
    }
}
var Student = {
    name: '若川',
    doSth: doSth,
}
// 普通函数调用
doSth(); // window
// 对象上的函数调用
Student.doSth(); // '若川'
// call、apply 调用
Student.doSth.call(person); // 'person'
new Student.doSth.call(person);
</code></pre><p>试想一下，如果是<code>Student.doSth.call(person)</code>先执行的情况下，那<code>new</code>执行一个函数。是没有问题的。 然而事实上，这代码是报错的。<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence" rel="noopener noreferrer">运算符优先级</a>是<code>new</code>比点号低，所以是执行<code>new (Student.doSth.call)(person)</code> 而<code>Function.prototype.call</code>，虽然是一个函数（<code>apply</code>、<code>bind</code>也是函数），跟箭头函数一样，不能用<code>new</code>调用。所以报错了。</p><pre><code class="language-js">Uncaught TypeError: Student.doSth.call is not a constructor
</code></pre><p>这是因为函数内部有两个不同的方法：<code>[[Call]]</code>和<code>[[Constructor]]</code>。 当使用普通函数调用时，<code>[[Call]]</code>会被执行。当使用构造函数调用时，<code>[[Constructor]]</code>会被执行。<code>call</code>、<code>apply</code>、<code>bind</code>和箭头函数内部没有<code>[[Constructor]]</code>方法。</p><p>从上面的例子可以看出普通函数调用优先级最低，其次是对象上的函数。 <code>call（apply、bind）</code>调用方式和<code>new</code>调用方式的优先级，在《你不知道的JavaScript》是对比<code>bind</code>和<code>new</code>，引用了<code>mdn</code>的<code>bind</code>的<code>ployfill</code>实现，<code>new</code>调用时bind之后的函数，会忽略<code>bind</code>绑定的第一个参数，(<code>mdn</code>的实现其实还有一些问题，感兴趣的读者，可以看我之前的文章：<a href="https://juejin.im/post/5bec4183f265da616b1044d7" rel="noopener noreferrer">面试官问：能否模拟实现<code>JS</code>的<code>bind</code>方法</a>)，说明<code>new</code>的调用的优先级最高。 所以它们的优先级是<code>new</code> 调用 &gt; <code>call、apply、bind</code> 调用 &gt; 对象上的函数调用 &gt; 普通函数调用。</p><h2 id="--9">总结</h2><p>如果要判断一个运行中函数的 <code>this</code> 绑定， 就需要找到这个函数的直接调用位置。 找到之后 就可以顺序应用下面这四条规则来判断 <code>this</code> 的绑定对象。<br></p><ol><li><code>new</code> 调用：绑定到新创建的对象，注意：显示<code>return</code>函数或对象，返回值不是新创建的对象，而是显式返回的函数或对象。<br></li><li><code>call</code> 或者 <code>apply</code>（ 或者 <code>bind</code>） 调用：严格模式下，绑定到指定的第一个参数。非严格模式下，<code>null</code>和<code>undefined</code>，指向全局对象（浏览器中是<code>window</code>），其余值指向被<code>new Object()</code>包装的对象。<br></li><li>对象上的函数调用：绑定到那个对象。<br></li><li>普通函数调用： 在严格模式下绑定到 <code>undefined</code>，否则绑定到全局对象。<br></li></ol><p><code>ES6</code> 中的箭头函数：不会使用上文的四条标准的绑定规则， 而是根据当前的词法作用域来决定<code>this</code>， 具体来说， 箭头函数会继承外层函数，调用的 this 绑定（ 无论 this 绑定到什么），没有外层函数，则是绑定到全局对象（浏览器中是<code>window</code>）。 这其实和 <code>ES6</code> 之前代码中的 <code>self = this</code> 机制一样。</p><p><code>DOM</code>事件函数：一般指向绑定事件的<code>DOM</code>元素，但有些情况绑定到全局对象（比如<code>IE6~IE8</code>的<code>attachEvent</code>）。</p><p>一定要注意，有些调用可能在无意中使用普通函数绑定规则。 如果想“ 更安全” 地忽略 <code>this</code> 绑 定， 你可以使用一个对象， 比如<code>ø = Object.create(null)</code>， 以保护全局对象。</p><p>面试官考察<code>this</code>指向就可以考察<code>new、call、apply、bind</code>，箭头函数等用法。从而扩展到作用域、闭包、原型链、继承、严格模式等。这就是面试官乐此不疲的原因。</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
