<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://yangjanx.github.io/atom.xml" rel="self" type="application/atom+xml" /><link href="https://yangjanx.github.io/" rel="alternate" type="text/html" /><updated>2024-04-28T02:20:55+00:00</updated><id>https://yangjanx.github.io/atom.xml</id><title type="html">Ryan’s Blog</title><subtitle>Lights will guide you home.</subtitle><author><name>Ryan</name></author><entry><title type="html">初探Javascript函数式编程</title><link href="https://yangjanx.github.io/2018/12/30/exploring-functional-programming" rel="alternate" type="text/html" title="初探Javascript函数式编程" /><published>2018-12-30T00:00:00+00:00</published><updated>2018-12-30T00:00:00+00:00</updated><id>https://yangjanx.github.io/2018/12/30/exploring%20functional%20programming</id><content type="html" xml:base="https://yangjanx.github.io/2018/12/30/exploring-functional-programming"><![CDATA[<!--more-->
<p><img src="https://raw.githubusercontent.com/yjoops/yjoops.github.io/master/assets/images/imgs/exploring-functional-programming.jpg" alt="image" /></p>
<h2 id="函数式编程">函数式编程</h2>
<blockquote>
  <p>函数式编程(Functionnal programming)起源于lambda(λ)演算，这是一种在20世纪30年代开发的用于研究可计算性，“Entscheidungs”问题，函数定义、应用和递归的系统。在计算机科学中，函数式编程是一种编程范式：将计算视为数学函数并避免改变状态和数据。它是一种声明式编程范例，在函数代码中，函数输出的值仅取决于传递给函数的参数。</p>
</blockquote>

<h2 id="纯函数-pure-functions">纯函数 Pure Functions</h2>
<p><strong>纯函数的三个原则</strong></p>
<ul>
  <li>变量都只在函数作用域内获取, 或作为的函数的参数传入</li>
  <li>引用透明(Referentially transparent)</li>
  <li>相同的输入保证相同的输出(same input -&gt; same ouput)。</li>
</ul>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//纯函数</span>
<span class="kd">const</span> <span class="nx">add</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">y</span><span class="p">;</span>

<span class="c1">//反例</span>
<span class="kd">let</span> <span class="nx">name</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">Ryan</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">getName</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">name</span><span class="p">;</span> <span class="c1">//非纯函数</span>
<span class="kd">const</span> <span class="nx">setName</span> <span class="o">=</span> <span class="nx">newName</span> <span class="o">=&gt;</span> <span class="p">{</span>    <span class="c1">//非纯函数</span>
    <span class="nx">name</span> <span class="o">=</span> <span class="nx">newName</span><span class="p">;</span>
<span class="p">};</span>
<span class="kd">const</span> <span class="nx">printUpperName</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>  <span class="c1">//非纯函数</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">name</span><span class="p">.</span><span class="nx">toUpperCase</span><span class="p">());</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>纯函数的优点</strong></p>
<ul>
  <li>易于测试</li>
  <li>因相同的输入总是得到相同的结果，故其结果可以缓存</li>
  <li>不会产生副作用(Side effects), 不会改变被传入的数据或者其他数据</li>
</ul>

<h2 id="声明式编程-declarative-style-programming">声明式编程 Declarative Style Programming</h2>
<ul>
  <li>命令式(Imperative)编程：要实现某个功能，利用指令一步一步的要求程序执行，通过一步步修改它们的状态或值，以达到需要的结果。</li>
</ul>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//输入一个数组，将每一项乘以2后输出</span>
<span class="kd">let</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
<span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span><span class="nx">len</span><span class="o">=</span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span><span class="nx">i</span><span class="o">&lt;</span><span class="nx">len</span><span class="p">;</span><span class="nx">i</span><span class="o">++</span><span class="p">){</span>
    <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">*=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">};</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">arr</span><span class="p">)</span> <span class="c1">//[2,4,6]</span>
</code></pre></div></div>

<ul>
  <li>声明式(Declarative)编程：告诉程序我们需要什么结果，声明我们的用意，而无需提供具体的指令，所有的细节指令都将在函数内部完成。</li>
</ul>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//输入一个数组，将每一项乘以2后输出</span>
<span class="kd">let</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
<span class="nx">arr</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">arr</span><span class="p">)</span> <span class="c1">//[2,4,6]</span>
</code></pre></div></div>

<h2 id="一等公民-first-class-functions">一等公民 First Class Functions</h2>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">multiply</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">*</span> <span class="nx">y</span><span class="p">;</span>    <span class="c1">//函数表达式</span>

<span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">)</span> <span class="p">{</span>    <span class="c1">//函数声明 --- 存在函数提升</span>
    <span class="k">return</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">y</span><span class="p">;</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">addAlias</span> <span class="o">=</span> <span class="nx">add</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">evens</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">].</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="闭包-closures">闭包 Closures</h2>
<blockquote>
  <p>广义上说，闭包就是指一个变量在他自身作用域外被使用了，就叫发生了闭包。<br /></p>
</blockquote>

<p>闭包允许我们封装某些需要的状态，特别在偏函数应用(partial application)，柯里化(currying)和组合函数(composing functions)中,这使得我们可以用非常小的模块来构建高阶模式。</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//一个简单的闭包</span>
<span class="kd">function</span> <span class="nx">fun</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">d</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">return</span> <span class="kd">function</span><span class="p">(){</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">d</span><span class="o">++</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//计数器例子</span>
<span class="kd">let</span> <span class="nx">makeCounter</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">privateCounter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">function</span> <span class="nx">changeBy</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">privateCounter</span> <span class="o">+=</span> <span class="nx">val</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="p">{</span>
        <span class="na">increment</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
            <span class="nx">changeBy</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="p">},</span>
        <span class="na">decrement</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
            <span class="nx">changeBy</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
        <span class="p">},</span>
        <span class="na">value</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">privateCounter</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="kd">let</span> <span class="nx">Counter1</span> <span class="o">=</span> <span class="nx">makeCounter</span><span class="p">();</span>
<span class="kd">let</span> <span class="nx">Counter2</span> <span class="o">=</span> <span class="nx">makeCounter</span><span class="p">();</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter1</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span>  <span class="c1">//0</span>
<span class="nx">Counter1</span><span class="p">.</span><span class="nx">increment</span><span class="p">();</span>
<span class="nx">Counter1</span><span class="p">.</span><span class="nx">increment</span><span class="p">();</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter1</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span>  <span class="c1">//2</span>
<span class="nx">Counter1</span><span class="p">.</span><span class="nx">decrement</span><span class="p">();</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter1</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span>  <span class="c1">//1</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter2</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span>  <span class="c1">//0</span>
</code></pre></div></div>

<p>每次调用其中一个计数器时，通过改变这个变量的值，会改变这个闭包的词法环境。然而在一个闭包内对变量的修改，不会影响到另外一个闭包中的变量。<br />
<strong>以这种方式使用闭包，提供了许多与面向对象编程相关的好处 —— 特别是数据隐藏和封装。</strong></p>

<h2 id="柯里化-currying">柯里化 Currying</h2>
<blockquote>
  <p>Currying(柯里化) 又称部分求值。一个curring的函数首先会接受一些参数，接受了这些参数之后，该函数不会立即求值，而是继续返回另一个函数，刚才传入的参数在函数形成的闭包中被保存起来，待到函数被真正需要求值的时候，之前传入的所有参数都会被一次性用于求值。<br /></p>
</blockquote>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//一个简单的柯里化函数</span>
<span class="kd">const</span> <span class="nx">adder</span> <span class="o">=</span> <span class="nx">x</span> <span class="o">=&gt;</span> <span class="nx">y</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">y</span><span class="p">;</span>
<span class="nx">adder</span><span class="p">(</span><span class="mi">2</span><span class="p">)(</span><span class="mi">3</span><span class="p">);</span>  <span class="c1">//5</span>
</code></pre></div></div>

<p>如果我们想统计一个月花了多少钱，利用柯里化函数，无需每天都累加之前的花费，只需在最后一天统一结算即可。</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">curring</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="p">[];</span>
    <span class="k">return</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">arguments</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">args</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="p">[].</span><span class="nx">push</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">args</span><span class="p">,</span> <span class="nx">arguments</span><span class="p">);</span>
            <span class="k">return</span> <span class="nx">arguments</span><span class="p">.</span><span class="nx">callee</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="kd">let</span> <span class="nx">cost</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">money</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">return</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">l</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">l</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">money</span> <span class="o">+=</span> <span class="nx">arguments</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="nx">money</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">})()</span>

<span class="nx">cost</span> <span class="o">=</span> <span class="nx">curring</span><span class="p">(</span><span class="nx">cost</span><span class="p">);</span>
<span class="nx">cost</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>  <span class="c1">//未被真正求值</span>
<span class="nx">cost</span><span class="p">(</span><span class="mi">200</span><span class="p">);</span>  <span class="c1">//未被真正求值</span>
<span class="nx">cost</span><span class="p">(</span><span class="mi">300</span><span class="p">);</span>  <span class="c1">//未被真正求值</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">cost</span><span class="p">());</span>  <span class="c1">//求值并输出: 600</span>
</code></pre></div></div>

<h2 id="偏函数应用-partial-application">偏函数应用 Partial Application</h2>
<blockquote>
  <p>Partial Application(偏函数应用) 是指使用一个函数并将其应用一个或多个参数，但不是全部参数，在这个过程中创建一个新函数。<br /></p>
</blockquote>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//一个简单的偏函数例子</span>
<span class="kd">const</span> <span class="nx">multiply</span> <span class="o">=</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">a</span> <span class="o">*</span> <span class="nx">b</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">double</span> <span class="o">=</span> <span class="nx">mulyiply</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">double</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span>  <span class="c1">//6</span>
</code></pre></div></div>

<p><strong>偏函数的优点：</strong></p>
<ul>
  <li>创建一个名称易懂的独立函数，调用时无需每次传入第一个参数，因为第一个参数通过bind提供了固定值。</li>
  <li>当我们有一个很通用的函数，为了方便提供一个较常用的变体。如，我们有一个函数send(from, to, text)，那么使用偏函数可以创建一个从当前用户发送的变体：sendTo(to, text)。</li>
</ul>

<h2 id="柯里化和偏函数的区别">柯里化和偏函数的区别</h2>
<ul>
  <li><strong>柯里化</strong> 将一个多参数函数转换成多个单参数函数，也就是将一个 n 元函数转换成 n 个一元函数。<br /></li>
  <li><strong>偏函数</strong> 固定一个函数的一个或者多个参数，也就是将一个 n 元函数转换成一个 n - x 元函数。</li>
</ul>

<h2 id="组合函数-composing-functions">组合函数 Composing Functions</h2>
<blockquote>
  <p>组合函数Compose把里面的参数都组合起来，一起调用。类似流式。<br /></p>
</blockquote>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">processWord</span> <span class="o">=</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">hyphenate</span><span class="p">,</span> <span class="nx">reverse</span><span class="p">,</span> <span class="nx">toUpperCase</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">words</span> <span class="o">=</span> <span class="p">[</span><span class="dl">'</span><span class="s1">hello</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">world</span><span class="dl">'</span><span class="p">];</span>

<span class="kd">const</span> <span class="nx">newWords</span> <span class="o">=</span> <span class="nx">words</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">processWord</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">newWords</span><span class="p">);</span>  <span class="c1">//['OL-LEH', 'DL-ROW']</span>
</code></pre></div></div>

<p>在函数式编程里，compose是避免命令式的重要一环，compose要做到的每一个函数都是”纯”的，脏的东西交给使用者。把每个纯的函数组合到compose里面，类似于管道，每个函数就像一个插件，来处理数据流。</p>

<h2 id="尾递归优化-tail-call-optimization">尾递归优化 Tail Call Optimization</h2>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//未进行尾递归优化</span>
<span class="kd">const</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="nx">n</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="nx">n</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">){</span>
        <span class="nx">retutn</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">factorial</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">value1</span> <span class="o">=</span> <span class="nx">factorail</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">value1</span><span class="p">);</span>  <span class="c1">//24</span>
<span class="kd">const</span> <span class="nx">value2</span> <span class="o">=</span> <span class="nx">factorial</span><span class="p">(</span><span class="mi">10000</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">value2</span><span class="p">);</span>  <span class="c1">//报错：爆栈了</span>
</code></pre></div></div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//进行尾递归优化</span>
<span class="kd">const</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">accum</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="nx">n</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">){</span>
        <span class="k">return</span> <span class="nx">accum</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nx">factorial</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">accum</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">factorial</span><span class="p">(</span><span class="mi">10000</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>  <span class="c1">//Infinity</span>
</code></pre></div></div>

<p><br /></p>

<p><strong>PS：</strong> 这段时间一直忙于处理业务，心态有点浮躁。趁着元旦假期调整好心态，继续努力！</p>]]></content><author><name>Ryan</name></author><category term="Javascript" /><category term="functional programming" /><summary type="html"><![CDATA[函数式编程 函数式编程(Functionnal programming)起源于lambda(λ)演算，这是一种在20世纪30年代开发的用于研究可计算性，“Entscheidungs”问题，函数定义、应用和递归的系统。在计算机科学中，函数式编程是一种编程范式：将计算视为数学函数并避免改变状态和数据。它是一种声明式编程范例，在函数代码中，函数输出的值仅取决于传递给函数的参数。]]></summary></entry><entry><title type="html">node.js简单小爬虫实践</title><link href="https://yangjanx.github.io/2018/11/09/nodejs-spider" rel="alternate" type="text/html" title="node.js简单小爬虫实践" /><published>2018-11-09T00:00:00+00:00</published><updated>2018-11-09T00:00:00+00:00</updated><id>https://yangjanx.github.io/2018/11/09/nodejs%20spider</id><content type="html" xml:base="https://yangjanx.github.io/2018/11/09/nodejs-spider"><![CDATA[<!--more-->

<p>最近花了点时间了解了一下node.js的爬虫程序，发现还是挺简单的，遂以此记录我的学习历程。</p>

<p>首先是node环境搭建和项目的初始化，这部分网上有很多教程，就不赘述了。本文选择抓取慕课网node教程的章节信息，话不多说，直接贴代码：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const http = require('http');
const fs = require('fs');
const cheerio = require('cheerio');
const url = "http://www.imooc.com/learn/348";

http.get(url, res =&gt; {
    let html = "";
    /* 获取页面数据 */
    res.on('data', data =&gt; {
        html += data
    })
    /* 数据获取结束 */
    res.on('end', () =&gt; {
        let courseData = filterChapters(html);
        printData(courseData);
        // fs.writeFile('./data/immoc.txt', printData(courseData), err =&gt; {
        //     if (err) throw new Error('写文件失败！' + err);
        //     console.log("写入文件成功！");
        // })
    })
}).on('error', () =&gt; {
    console.log("数据获取失败！");
})

/* 过滤章节信息来获取需要的课程信息 */
function filterChapters(html) {
    let $ = cheerio.load(html);    // cheerio -&gt; 相当于nodejs版的jQuery,可用其获取要获取的页面的dom节点信息
    let chapters = $(".chapter");
    let courseData = [];
    chapters.each(function (item) {
        let chapter = $(this);
        let chapterTitle = chapter.find('h3').text().trim().split('\n')[0];
        let videos = chapter.find('.video').children('li');
        let chapterData = {
            chapterTitle: chapterTitle,
            videos: []
        };
        videos.each(function (item) {
            let video = $(this).find('.J-media-item');
            let videoTitle = video.text().trim().split('\n')[0].trim();
            let videoTime = video.text().trim().split('\n')[1].trim();
            let id = String(video.attr('href')).split('/video/')[1];
            let address = "https://www.imooc.com" + String(video.attr('href'));
            chapterData.videos.push({
                title: videoTitle,
                time: videoTime,
                id: id,
                address: address
            })
        });

        courseData.push(chapterData)
    });
    return courseData;
}

/* 打印抓取的信息 */
function printData(courseData) {
    courseData.forEach(item =&gt; {
        let chapterTitle = item.chapterTitle;
        console.log(chapterTitle + '\n');
        item.videos.forEach(video =&gt; {
            console.log(' [' + video.id + '] ' + video.title + ' ' + video.time + '\t' + video.address + '\n');
        }
        )
    })
}

/* 将抓取到的信息作为字符串保存在文件中 */
// function printData(courseData) {
//     let fileStr = "";
//     courseData.forEach(item =&gt; {
//         let chapterTitle = item.chapterTitle;
//         fileStr += chapterTitle + '\n';
//         item.videos.forEach(video =&gt; {
//             fileStr += ' [' + video.id + '] ' + video.title + ' ' + video.time + '\t' + video.address + '\n';
//         }
//         )
//     })
//     return fileStr;
// }
</code></pre></div></div>
<p><br /><br />
运行后结果如下：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>第1章 前言

 [6687] 1-1 Node.js基础-前言 (01:20)    https://www.imooc.com/video/6687

 [6688] 1-2 为什么学习Nodejs (05:43)    https://www.imooc.com/video/6688

第2章 安装 Nodejs

 [6689] 2-1 Node.js基础-课程简介 (01:19)        https://www.imooc.com/video/6689

 [6690] 2-2 Nodejs版本常识 (01:02)      https://www.imooc.com/video/6690

 [6691] 2-3 Windows下安装Nodejs (04:43) https://www.imooc.com/video/6691

 [6692] 2-4 Linux下安装Nodejs (06:24)   https://www.imooc.com/video/6692

 [6693] 2-5 Mac下安装Nodejs (03:55)     https://www.imooc.com/video/6693

第3章 等不及了来尝鲜

 [6694] 3-1 Node.js基础-起一个web服务器 (05:14) https://www.imooc.com/video/6694

 [6695] 3-2 Node.js基础-命令行中体验 (02:47)    https://www.imooc.com/video/6695

第4章 模块与包管理工具

 [6697] 4-1 Node.js 的模块 与 Commonjs 规范 (03:44)     https://www.imooc.com/vi
deo/6697

 [6700] 4-2 模块的分类 (00:45)  https://www.imooc.com/video/6700

 [6701] 4-3 简单的Nodejs模块 (09:23)    https://www.imooc.com/video/6701

第5章 横扫 Nodejs API

 [6705] 5-1 Node.js-不要陷入版本选择的深渊 (02:32)      https://www.imooc.com/vi
deo/6705

 [6710] 5-2 URL网址解析的好帮手 (10:30) https://www.imooc.com/video/6710

 [6711] 5-3 QueryString参数处理小利器 (06:40)   https://www.imooc.com/video/6711

 [6712] 5-4 HTTP知识先填坑 (09:43)      https://www.imooc.com/video/6712

 [6713] 5-5 HTTP知识填坑之“以慕课网为例分析” (10:13)  https://www.imooc.com/vi
deo/6713

 [7557] 5-6 HTTP 事件回调进阶 (17:51)   https://www.imooc.com/video/7557

 [7558] 5-7 HTTP 源码解读之先了解作用域、上下文 (20:50) https://www.imooc.com/vi
deo/7558

 [7963] 5-8 HTTP 源码解读 (22:08)       https://www.imooc.com/video/7963

 [7964] 5-9 HTTP 性能测试 (09:15)       https://www.imooc.com/video/7964

 [7965] 5-10 HTTP 小爬虫 (17:33)        https://www.imooc.com/video/7965

 [8525] 5-11 事件模块小插曲 (15:15)     https://www.imooc.com/video/8525

 [8837] 5-12 Node.js：request方法 (17:56)       https://www.imooc.com/video/8837
</code></pre></div></div>
<p>若想写入文件中，可看上述代码的注释部分。</p>

<p>至此，一个简单的爬虫就完成了。当然，node的学习还是路漫漫兮修远兮~</p>

<p>PS：双11要来了，最近又温度骤降，别人都在剁手，我只能在办公室里跺跺脚了。。XD</p>]]></content><author><name>Ryan</name></author><category term="Javascript" /><category term="js" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">JS map()方法和parseInt函数</title><link href="https://yangjanx.github.io/2018/08/17/js-map(parseInt)" rel="alternate" type="text/html" title="JS map()方法和parseInt函数" /><published>2018-08-17T00:00:00+00:00</published><updated>2018-08-17T00:00:00+00:00</updated><id>https://yangjanx.github.io/2018/08/17/js-map(parseInt)</id><content type="html" xml:base="https://yangjanx.github.io/2018/08/17/js-map(parseInt)"><![CDATA[<!--more-->

<p>偶然间看到一道 js题：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[010,"10","10","3"].map(parseInt) = ?     // [8,NaN,2,NaN]
</code></pre></div></div>
<p>对于其中原理很感兴趣，故在搜集相关资料后写此文用以记录。</p>

<h2 id="map函数">map()函数</h2>
<ul>
  <li>返回一个新的数组，且不会改变原数组</li>
  <li>按照原始数组的顺序依次处理元素</li>
</ul>

<p><strong>语法：</strong> <code class="language-plaintext highlighter-rouge">array.map(function(currentValue,index,arr),thisValue)</code></p>

<p><strong>参数说明：</strong></p>
<table>
    <tr>
        <th>参数</th>
        <th>描述</th>
    </tr>
    <tr>
        <td>currentValue</td>
        <td>必须，当前元素的值</td>
    </tr>
    <tr>
        <td>index</td>
        <td>可选，当前元素的索引值</td>
    </tr>
    <tr>
        <td>arr</td>
        <td>可选，当前元素所属数组</td>
    </tr>
    <tr>
        <td>thisValue</td>
        <td>可选</td>
    </tr>
</table>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[1,3,5].map(function(item,index){
    return item + "(" + index + ")";
});  

// ["1(0)","3(1)","5(2)"]
</code></pre></div></div>

<h2 id="parseint函数">parseInt()函数</h2>
<ul>
  <li><code class="language-plaintext highlighter-rouge">parseInt(string,radix)</code>接收两个参数</li>
</ul>
<table>
    <tr>
        <th>参数</th>
        <th>描述</th>
    </tr>
    <tr>
        <td>currentValue</td>
        <td>需要被解析的字符串</td>
    </tr>
    <tr>
        <td>radix</td>
        <td>要解析的字符串的基数，介于2~36之间</td>
    </tr>
</table>

<p><strong>注意：</strong></p>
<ul>
  <li>若radix未设置或设置为0，根据string来判断基数</li>
  <li>若radix不在范围内，返回NaN
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>parseInt("010")             //10
parseInt(010)               //8
parseInt("10",2)            //2
parseInt("10",1)            //NaN
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="mapparseint">.map(parseInt)</h2>
<p>因为<code class="language-plaintext highlighter-rouge">parseInt</code>只接收2个参数，故map中的函数就只传递<code class="language-plaintext highlighter-rouge">(item,index)</code>给<code class="language-plaintext highlighter-rouge">parseInt</code></p>

<p>因此文章开头提到的题目<code class="language-plaintext highlighter-rouge">[010,"10","10","3"].map(parseInt)</code>实际上输出的是：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[parseInt(010,0),parseInt("10",1),parseInt("10",2),parseInt("3",3)]  
// [8,NaN,2,NaN]
</code></pre></div></div>]]></content><author><name>Ryan</name></author><category term="Javascript" /><category term="js" /><summary type="html"><![CDATA[]]></summary></entry></feed>