<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>青春样 | yangzj1992&#39;s blog</title>
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://qcyoung.com/"/>
  <updated>2017-08-11T14:05:18.000Z</updated>
  <id>http://qcyoung.com/</id>
  
  <author>
    <name>yangzj1992</name>
    <email>yangzj1992@qq.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>【译】对一行混淆 JS 代码的逆向分析过程</title>
    <link href="http://qcyoung.com/2017/08/11/%E3%80%90%E8%AF%91%E3%80%91%E5%AF%B9%E4%B8%80%E8%A1%8C%E6%B7%B7%E6%B7%86%20JS%20%E4%BB%A3%E7%A0%81%E7%9A%84%E9%80%86%E5%90%91%E5%88%86%E6%9E%90%E8%BF%87%E7%A8%8B/"/>
    <id>http://qcyoung.com/2017/08/11/【译】对一行混淆 JS 代码的逆向分析过程/</id>
    <published>2017-08-10T16:43:02.000Z</published>
    <updated>2017-08-11T14:05:18.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文链接 : <a href="https://www.alexkras.com/reverse-engineering-one-line-of-javascript/" target="_blank" rel="external">Reverse Engineering One Line of JavaScript</a><br>
原文作者 : <a href="https://www.alexkras.com/about/" target="_blank" rel="external">Alex</a><br>
译文出自 : <a href="http://zcfy.cc/" target="_blank" rel="external">众成翻译</a><br>
译者 : <a href="http://www.qcyoung.com" target="_blank" rel="external">yangzj1992</a><br>
首发于: <a href="http://zcfy.cc/article/reverse-engineering-one-line-of-javascript-3615.html?t=new" target="_blank" rel="external">众成翻译</a></p>
</blockquote>
<p>不久前，我看到有人提了个问题，能否有人将下面这行 JS 代码进行逆向分析。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">&lt;pre id=p&gt;&lt;script&gt;n=setInterval(&quot;for(n+=7,i=k,P=&apos;p.\\n&apos;;i-=1/k;P+=P[i%2?(i%2*j-j+n/k^j)&amp;1:2])j=k/i;p.innerHTML=P&quot;,k=64)&lt;/script&gt;</span>
</pre></td></tr></table></figure>
<p>这行代码的效果如下所示，它是一个大小仅为 128b 的光线追踪挡板 Demo 。你也可以访问<a href="https://codepen.io/yangzj1992/pen/owOyjr" target="_blank" rel="external">这里</a>查看效果。这行代码的作者是<a href="http://www.p01.org/128b_raytraced_checkboard/" target="_blank" rel="external">p01</a>, 发布于<a href="http://www.pouet.net/prod.php?which=59204" target="_blank" rel="external">Pouet.net</a>。你可以访问<a href="http://www.p01.org/" target="_blank" rel="external">他的网站</a>看到更多有趣的 Demo。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/ray.gif" alt="raytraced_checkboard"></p>
<p>下面我们试着对这行代码进行一下逆向分析。</p>
<h2 id="di-yi-bu-fen-shi-dai-ma-ke-du">第一部分，使代码可读。</h2>
<p>首先，我们将 HTML 和 JS 代码分离。这里我们保留相关的 id 指向。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
</pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"code.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">pre</span> <span class="attr">id</span>=<span class="string">"p"</span>&gt;</span><span class="tag">&lt;/<span class="name">pre</span>&gt;</span></span>
</pre></td></tr></table></figure>
<p>这里我们注意到有个变量 <code>k</code>。我们将它置顶申明，并重命名为 <code>delay</code>。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> delay = <span class="number">64</span>;</span>
<span class="line"><span class="keyword">var</span> draw = <span class="string">"for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P"</span>;</span>
<span class="line"><span class="keyword">var</span> n = setInterval(draw, delay);</span>
</pre></td></tr></table></figure>
<p><code>var draw</code> 由于是一个字符串，它会被 setInterval 用 eval() 方法执行（setInterval 可以接受一个 function 或是 string 来执行）。所以这里我们将它重写成一个真实的 function。</p>
<p>另外这里还对元素 p 进行了直接的 DOM 操作，这里我们用 JS 获取这个 id 来重新书写，让它更加易懂。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> delay = <span class="number">64</span>;</span>
<span class="line"><span class="keyword">var</span> p = <span class="built_in">document</span>.getElementById(<span class="string">"p"</span>); <span class="comment">// &lt; ---------------</span></span>
<span class="line"><span class="comment">// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P";</span></span>
<span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="keyword">for</span> (n += <span class="number">7</span>, i = delay, P = <span class="string">'p.\n'</span>; i -= <span class="number">1</span> / delay; P += P[i % <span class="number">2</span> ? (i % <span class="number">2</span> * j - j + n / delay ^ j) &amp; <span class="number">1</span> : <span class="number">2</span>]) &#123;</span>
<span class="line">        j = delay / i; p.innerHTML = P;</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">var</span> n = setInterval(draw, delay);</span>
</pre></td></tr></table></figure>
<p>接下来，我们将变量 <code>i</code>，<code>p</code>, <code>j</code> 也作提前声明。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> delay = <span class="number">64</span>;</span>
<span class="line"><span class="keyword">var</span> p = <span class="built_in">document</span>.getElementById(<span class="string">"p"</span>);</span>
<span class="line"><span class="comment">// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P";</span></span>
<span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> i = delay; <span class="comment">// &lt; ---------------</span></span>
<span class="line">    <span class="keyword">var</span> P =<span class="string">'p.\n'</span>;</span>
<span class="line">    <span class="keyword">var</span> j;</span>
<span class="line">    <span class="keyword">for</span> (n += <span class="number">7</span>; i &gt; <span class="number">0</span> ;P += P[i % <span class="number">2</span> ? (i % <span class="number">2</span> * j - j + n / delay ^ j) &amp; <span class="number">1</span> : <span class="number">2</span>]) &#123;</span>
<span class="line">        j = delay / i; p.innerHTML = P;</span>
<span class="line">        i -= <span class="number">1</span> / delay;</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">var</span> n = setInterval(draw, delay);</span>
</pre></td></tr></table></figure>
<p>然后，我们将 for 循环转为 while 循环，将 for 循环中间的条件语句作为条件，其他的语句放到 while 循环的内外部。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> delay = <span class="number">64</span>;</span>
<span class="line"><span class="keyword">var</span> p = <span class="built_in">document</span>.getElementById(<span class="string">"p"</span>);</span>
<span class="line"><span class="comment">// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P";</span></span>
<span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> i = delay;</span>
<span class="line">    <span class="keyword">var</span> P =<span class="string">'p.\n'</span>;</span>
<span class="line">    <span class="keyword">var</span> j;</span>
<span class="line">    n += <span class="number">7</span>;</span>
<span class="line">    <span class="keyword">while</span> (i &gt; <span class="number">0</span>) &#123; <span class="comment">// &lt;----------------------</span></span>
<span class="line">        <span class="comment">//Update HTML</span></span>
<span class="line">        p.innerHTML = P;</span>
<span class="line"> </span>
<span class="line">        j = delay / i;</span>
<span class="line">        i -= <span class="number">1</span> / delay;</span>
<span class="line">        P += P[i % <span class="number">2</span> ? (i % <span class="number">2</span> * j - j + n / delay ^ j) &amp; <span class="number">1</span> : <span class="number">2</span>];</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">var</span> n = setInterval(draw, delay);</span>
</pre></td></tr></table></figure>
<p>接下来我们展开三元表达式。</p>
<p>这里 <code>i % 2</code> 的作用是判断 i 是否为偶数，如果是偶数，那么将会返回 2，否则返回 <code>(i % 2 * j - j + n / delay ^ j) &amp; 1</code>。</p>
<p>而这一堆式子也将会作为 <code>P</code> 的 index 进行处理。 即 <code>P += P[index];</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> delay = <span class="number">64</span>;</span>
<span class="line"><span class="keyword">var</span> p = <span class="built_in">document</span>.getElementById(<span class="string">"p"</span>);</span>
<span class="line"><span class="comment">// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P";</span></span>
<span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> i = delay;</span>
<span class="line">    <span class="keyword">var</span> P =<span class="string">'p.\n'</span>;</span>
<span class="line">    <span class="keyword">var</span> j;</span>
<span class="line">    n += <span class="number">7</span>;</span>
<span class="line">    <span class="keyword">while</span> (i &gt; <span class="number">0</span>) &#123;</span>
<span class="line">        <span class="comment">//Update HTML</span></span>
<span class="line">        p.innerHTML = P;</span>
<span class="line"> </span>
<span class="line">        j = delay / i;</span>
<span class="line">        i -= <span class="number">1</span> / delay;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">let</span> index;</span>
<span class="line">        <span class="keyword">let</span> iIsOdd = (i % <span class="number">2</span> != <span class="number">0</span>); <span class="comment">// &lt;---------------</span></span>
<span class="line"> </span>
<span class="line">        <span class="keyword">if</span> (iIsOdd) &#123; <span class="comment">// &lt;---------------</span></span>
<span class="line">            index = (i % <span class="number">2</span> * j - j + n / delay ^ j) &amp; <span class="number">1</span>;</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            index = <span class="number">2</span>;</span>
<span class="line">        &#125;</span>
<span class="line"> </span>
<span class="line">        P += P[index];</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">var</span> n = setInterval(draw, delay);</span>
</pre></td></tr></table></figure>
<p>这里我们接下来注意到与运算符 <code>&amp; 1</code>。</p>
<p><code>(i % 2 * j - j + n / delay ^ j) &amp; 1</code>，这段代码可以巧妙地去比较一个数是否为奇偶，它的原理其实是数值转换为二进制进行与运算返回的十进制结果。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</pre></td><td class="code"><pre><span class="line"><span class="number">0</span> &amp; <span class="number">1</span> <span class="comment">// 0</span></span>
<span class="line"><span class="number">1</span> &amp; <span class="number">1</span> <span class="comment">// 1</span></span>
<span class="line"><span class="number">2</span> &amp; <span class="number">1</span> <span class="comment">// 0</span></span>
<span class="line"><span class="number">3</span> &amp; <span class="number">1</span> <span class="comment">// 1</span></span>
<span class="line"><span class="number">3</span> &amp; <span class="number">2</span> <span class="comment">// 2</span></span>
<span class="line"><span class="number">8</span> &amp; <span class="number">8</span> <span class="comment">// 8</span></span>
</pre></td></tr></table></figure>
<p>然后我们再次对变量进行重命名整合。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> delay = <span class="number">64</span>;</span>
<span class="line"><span class="keyword">var</span> p = <span class="built_in">document</span>.getElementById(<span class="string">"p"</span>);</span>
<span class="line"><span class="comment">// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P";</span></span>
<span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> i = delay;</span>
<span class="line">    <span class="keyword">var</span> P =<span class="string">'p.\n'</span>;</span>
<span class="line">    <span class="keyword">var</span> j;</span>
<span class="line">    n += <span class="number">7</span>;</span>
<span class="line">    <span class="keyword">while</span> (i &gt; <span class="number">0</span>) &#123;</span>
<span class="line">        <span class="comment">//Update HTML</span></span>
<span class="line">        p.innerHTML = P;</span>
<span class="line"> </span>
<span class="line">        j = delay / i;</span>
<span class="line">        i -= <span class="number">1</span> / delay;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">let</span> index;</span>
<span class="line">        <span class="keyword">let</span> iIsOdd = (i % <span class="number">2</span> != <span class="number">0</span>);</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">if</span> (iIsOdd) &#123;</span>
<span class="line">            <span class="keyword">let</span> magic = (i % <span class="number">2</span> * j - j + n / delay ^ j);</span>
<span class="line">            <span class="keyword">let</span> magicIsOdd = (magic % <span class="number">2</span> != <span class="number">0</span>); <span class="comment">// &amp;1 &lt; --------------------------</span></span>
<span class="line">            <span class="keyword">if</span> (magicIsOdd) &#123; <span class="comment">// &amp;1 &lt;--------------------------</span></span>
<span class="line">                index = <span class="number">1</span>;</span>
<span class="line">            &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">                index = <span class="number">0</span>;</span>
<span class="line">            &#125;</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            index = <span class="number">2</span>;</span>
<span class="line">        &#125;</span>
<span class="line"> </span>
<span class="line">        P += P[index];</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">var</span> n = setInterval(draw, delay);</span>
</pre></td></tr></table></figure>
<p>由于这里 P ='p.\n'; 而我们的 index 的值为：0, 1, 2。对应即有：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">P[<span class="number">0</span>] = <span class="string">'p'</span></span>
<span class="line">P[<span class="number">1</span>] = <span class="string">'.'</span></span>
<span class="line">P[<span class="number">2</span>] = <span class="string">'\n'</span></span>
</pre></td></tr></table></figure>
<p>所以这里我们还可以用 switch 重写代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> delay = <span class="number">64</span>;</span>
<span class="line"><span class="keyword">var</span> p = <span class="built_in">document</span>.getElementById(<span class="string">"p"</span>);</span>
<span class="line"><span class="comment">// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P";</span></span>
<span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> i = delay;</span>
<span class="line">    <span class="keyword">var</span> P =<span class="string">'p.\n'</span>;</span>
<span class="line">    <span class="keyword">var</span> j;</span>
<span class="line">    n += <span class="number">7</span>;</span>
<span class="line">    <span class="keyword">while</span> (i &gt; <span class="number">0</span>) &#123;</span>
<span class="line">        <span class="comment">//Update HTML</span></span>
<span class="line">        p.innerHTML = P;</span>
<span class="line"> </span>
<span class="line">        j = delay / i;</span>
<span class="line">        i -= <span class="number">1</span> / delay;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">let</span> index;</span>
<span class="line">        <span class="keyword">let</span> iIsOdd = (i % <span class="number">2</span> != <span class="number">0</span>);</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">if</span> (iIsOdd) &#123;</span>
<span class="line">            <span class="keyword">let</span> magic = (i % <span class="number">2</span> * j - j + n / delay ^ j);</span>
<span class="line">            <span class="keyword">let</span> magicIsOdd = (magic % <span class="number">2</span> != <span class="number">0</span>); <span class="comment">// &amp;1</span></span>
<span class="line">            <span class="keyword">if</span> (magicIsOdd) &#123; <span class="comment">// &amp;1</span></span>
<span class="line">                index = <span class="number">1</span>;</span>
<span class="line">            &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">                index = <span class="number">0</span>;</span>
<span class="line">            &#125;</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            index = <span class="number">2</span>;</span>
<span class="line">        &#125;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">switch</span> (index) &#123; <span class="comment">// P += P[index]; &lt;-----------------------</span></span>
<span class="line">            <span class="keyword">case</span> <span class="number">0</span>:</span>
<span class="line">                P += <span class="string">"p"</span>; <span class="comment">// aka P[0]</span></span>
<span class="line">                <span class="keyword">break</span>;</span>
<span class="line">            <span class="keyword">case</span> <span class="number">1</span>:</span>
<span class="line">                P += <span class="string">"."</span>; <span class="comment">// aka P[1]</span></span>
<span class="line">                <span class="keyword">break</span>;</span>
<span class="line">            <span class="keyword">case</span> <span class="number">2</span>:</span>
<span class="line">                P += <span class="string">"\n"</span>; <span class="comment">// aka P[2]</span></span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
<span class="line"> </span>
<span class="line"><span class="keyword">var</span> n = setInterval(draw, delay);</span>
</pre></td></tr></table></figure>
<p>现在我们来梳理 <code>var n = setInterval(draw, delay);</code>。因为 setInterval 返回一个从 1 开始的整数 ID 。并在每次 setInterval 方法被调用时依次递增。（这个 ID 可以被用于 clearInterval 等方法。）在我们的例子中，setInterval 只被调用了一次，所以 n 被设置为 1。</p>
<p>此外，我们把 <code>delay</code> 重命名为 <code>DELAY</code> 以作为常量。</p>
<p>最后，我们对 <code>i % 2 * j - j + n / DELAY ^ j</code> 进行排序，由于 <code>^</code> 位异或运算符的优先级较低。所以加上括号后整理如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
<span class="line">45</span>
<span class="line">46</span>
<span class="line">47</span>
<span class="line">48</span>
<span class="line">49</span>
<span class="line">50</span>
<span class="line">51</span>
<span class="line">52</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> DELAY = <span class="number">64</span>; <span class="comment">// approximately 15 frames per second</span></span>
<span class="line"><span class="keyword">var</span> n = <span class="number">1</span>;</span>
<span class="line"><span class="keyword">var</span> p = <span class="built_in">document</span>.getElementById(<span class="string">"p"</span>);</span>
<span class="line"><span class="comment">// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&amp;1:2])j=delay/i;p.innerHTML=P";</span></span>
<span class="line"> </span>
<span class="line"><span class="comment">/**</span>
<span class="line"> * Draws a picture</span>
<span class="line"> * 128 chars by 32 chars = total 4096 chars</span>
<span class="line"> */</span></span>
<span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> i = DELAY; <span class="comment">// 64</span></span>
<span class="line">    <span class="keyword">var</span> P =<span class="string">'p.\n'</span>; <span class="comment">// First line, reference for chars to use</span></span>
<span class="line">    <span class="keyword">var</span> j;</span>
<span class="line"> </span>
<span class="line">    n += <span class="number">7</span>;</span>
<span class="line"> </span>
<span class="line">    <span class="keyword">while</span> (i &gt; <span class="number">0</span>) &#123;</span>
<span class="line"> </span>
<span class="line">        j = DELAY / i;</span>
<span class="line">        i -= <span class="number">1</span> / DELAY;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">let</span> index;</span>
<span class="line">        <span class="keyword">let</span> iIsOdd = (i % <span class="number">2</span> != <span class="number">0</span>);</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">if</span> (iIsOdd) &#123;</span>
<span class="line">            <span class="keyword">let</span> magic = ((i % <span class="number">2</span> * j - j + n / DELAY) ^ j); <span class="comment">// &lt; ------------------</span></span>
<span class="line">            <span class="keyword">let</span> magicIsOdd = (magic % <span class="number">2</span> != <span class="number">0</span>); <span class="comment">// &amp;1</span></span>
<span class="line">            <span class="keyword">if</span> (magicIsOdd) &#123; <span class="comment">// &amp;1</span></span>
<span class="line">                index = <span class="number">1</span>;</span>
<span class="line">            &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">                index = <span class="number">0</span>;</span>
<span class="line">            &#125;</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            index = <span class="number">2</span>;</span>
<span class="line">        &#125;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">switch</span> (index) &#123; <span class="comment">// P += P[index];</span></span>
<span class="line">            <span class="keyword">case</span> <span class="number">0</span>:</span>
<span class="line">                P += <span class="string">"p"</span>;</span>
<span class="line">                <span class="keyword">break</span>;</span>
<span class="line">            <span class="keyword">case</span> <span class="number">1</span>:</span>
<span class="line">                P += <span class="string">"."</span>;</span>
<span class="line">                <span class="keyword">break</span>;</span>
<span class="line">            <span class="keyword">case</span> <span class="number">2</span>:</span>
<span class="line">                P += <span class="string">"\n"</span>;</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="comment">//Update HTML</span></span>
<span class="line">    p.innerHTML = P;</span>
<span class="line">&#125;;</span>
<span class="line"> </span>
<span class="line">setInterval(draw, <span class="number">64</span>);</span>
</pre></td></tr></table></figure>
<p>你可以在<a href="https://codepen.io/yangzj1992/pen/jLqNLY" target="_blank" rel="external">这里</a>看到最后的代码。</p>
<h2 id="di-er-bu-fen-li-jie-dai-ma">第二部分，理解代码。</h2>
<p>在 <code>draw()</code> 函数第一次执行时 i 被 <code>var i = DELAY;</code> 初始化为 64，并在每次循环中被 <code>i -= 1 / DELAY;</code> 进行逐次 1/64 的递减。直到 <code>i &gt; 0</code> 时结束（循环 64 * 64 次）</p>
<p>而我们的图像则是由 32 行组成，每行包含了 128 个字符。这里我们可以注意到我们会对 i 进行奇偶判断：<code>let iIsOdd = (i % 2 != 0)</code> 当 i 为偶数时，总计会发生 32 次。（即 64、62、60...等)。此时 index 将被赋值为 2。此时通过 <code>P += &quot;\n&quot;;</code> 来添加新的一行。剩下的 127 次循环产生的字符即为 <code>p</code> 或 <code>.</code>。</p>
<p>由代码可知，当 <code>((i % 2 * j - j + n / DELAY) ^ j);</code> 为奇数时。我们会添加 <code>.</code>，反之会添加 <code>p</code>。</p>
<p>这里问题的关键来了，这串式子什么时候分别为奇偶呢？在我们研究前，我们可以做一个实验，让我们从 <code>let magic = ((i % 2 * j - j + n / DELAY) ^ j);</code> 中移除 <code>+ n/DELAY</code>。刷新页面后，我们获得了一份静止的图像输出。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/static.png" alt=""></p>
<p>那么，我们就先在移除 <code>+ n/DELAY</code> 的情况下进行探讨。对于 <code>(i % 2 * j - j) ^ j</code> 因为在每次循环中有： <code>j = DELAY / i;</code> 所以我们可以将式子简化为一元式：<code>(i % 2 * 64/i - 64/i) ^ 64/i</code>。</p>
<p>这里我们借助一个<a href="https://www.desmos.com/calculator" target="_blank" rel="external">在线的图表生成工具</a>来帮忙绘制函数。</p>
<p>例如，首先我们要绘制的 <code>i % 2</code>，它的展示为下图所示的重复一次函数片段，y 值的范围在 0 到 2 之间。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/i-mod-2.png" alt=""></p>
<p>如果我们绘制 <code>64/i</code> ，所对应的图表展示如下：</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/64-div-i.png" alt=""></p>
<p>现在我们将式子结合起来，绘制图如下：</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/left-side.png" alt=""></p>
<p>将两个函数绘制在一张图上，绘制图如下：</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/side-by-side.png" alt=""></p>
<h3 id="zhe-xie-tu-biao-shi-shi-yao-yi-si">这些图表是什么意思</h3>
<p>现在让我们着力观察图表的前16行，即 <code>i</code> 值的范围是从 64 到 32。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/zoom-in-1024x412.png" alt=""></p>
<p>通过 JS 的 XOR (位异或）运算符的计算规则，当你的位运算两端都为 0 或 1 时，将返回 0 ，两端不同时为 1。同时如果你的数是小数的话，将会抛弃小数部分进行计算。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line">First     Second      Result</span>
<span class="line">1              1              1</span>
<span class="line">1              0              1</span>
<span class="line">0              1              1</span>
<span class="line">0              0              0</span>
</pre></td></tr></table></figure>
<blockquote>
<p>异或运算的窍门：对两个数进行三次异或运算，可以互换他们的值，不需要引入临时变量。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">var a=12;</span>
<span class="line">var b=23;</span>
<span class="line">a^=b,b^=a,a^=b;//a=23,b=12</span>
</pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p>异或运算也可以用来取整 <code>1.23^0 //1</code>，<code>3.5^0 //3</code>
当负数与整数进行异或运算时。负数先进行补码，取反再加一。正数的原码和补码一致。</p>
<p>如，对于 -2：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">源码：1000 0000 0000 0010 （负数，最高为是 1）</span>
<span class="line">反码：1111 1111 1111 1101 （按位取反）</span>
<span class="line">补码：1111 1111 1111 1110 （加一）</span>
</pre></td></tr></table></figure>
</blockquote>
<blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
</pre></td><td class="code"><pre><span class="line">-2 ^ 3 = </span>
<span class="line">  1111 1111 1111 1110</span>
<span class="line">^ 0000 0000 0000 0011</span>
<span class="line">= 1111 1111 1111 1101</span>
</pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p>再转回原码（负数最高位不变，其他位取反，+1，正数不变） = - 3</p>
</blockquote>
<p>在 <code>i</code> 值的这个范围内 <code>j</code> 将从 1 开始慢慢的走向 2（即基本为 1.XXX)。这样在另一端也为 1 时，我们将会得到异或计算结果为 0（偶数），最后获得 <code>p</code> 字符输出。</p>
<p>换句话说，每条蓝色的对角线代表着我们 Demo 图表中的一行。因为 <code>j</code> 在这 16 行里总是大于 1 而小于 2。我们得到奇数的唯一办法就是使式子 <code>(i % 2 * j - j)^ j</code> 即 <code>i % 2 * i/64 - i/64</code> 即蓝色的对角线应该处于大于 1 或小于 -1的范围。</p>
<p>通过图我们可以看到，在最右侧的对角线上很少有到大于 1 和小于 -1 的地方。随着对角线往左的描绘，对角线逐渐开始变长。到第 16 行位置对角线到达 -2 到 2 的位置。在 16 行以后，我们从静态 Demo 图上也可以看到图的展示规律变成了另一种模式。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/zoom-in-2.png" alt=""></p>
<p>在第 16 行后，<code>j</code> 的值开始大于 2 。这时我们的式子期望也发生了反转，在蓝色对角线大于 2 和小于 -2 时或是 -1 到 1 的范围时式子才能为偶数。这就是为什么在 17 行以后我们能看到更多组 <code>p</code> 的展示。此外如果你仔细观察 Demo 的底部几行，你会注意到它们也并没有遵循同样的展示规则，因为在后面图的波动也越来越大了。</p>
<p>让我们回到 <code>+ n/DELAY</code>，通过代码我们可以知道 n 是从 8 开始（从 1 开始并在每次执行 setInterval 时加 7）。</p>
<p>当 n 变成 64 时，此时绘图如下：</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/n-64.png" alt=""></p>
<p>现在 <code>j</code> 的值趋近于 1，x 轴在 62 - 63 上的值为 0.x , 63 - 64 的值为 1.x。可推得在 63 - 64 时对角线的值是 (1 ^ 1 = 0 // even) 添加一串 <code>p</code>，62 - 63 时对角线的值是（1^0 = 1 // odd) 添加一串 <code>.</code>。</p>
<p>此时呈现的 Demo 静态图像如下所示（在 codepen 的 demo 里你可以自行修改 <code>n</code> 值进行测试）。它的第一行正如我们所推测的那样。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/n-64-rendered.png" alt=""></p>
<p>当 n 在下一次执行 setInterval，图表产生了如下的轻微变化。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/n-647.png" alt=""></p>
<p>注意，第一行对角线此时增长了 7/64 ≈ 0.1 ，由于 Demo 中 1 行有 128 个字符(对应图表值范围为 2）。相应影响的字符数应该为 0.1 * (128 / 2) = 6.4。我们看一下对应静态图修改后的展示，在第一行中实际移动了 7 个字符，这与我们的猜想也吻合。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/n-647-rendered.png" alt=""></p>
<p>再来最后一个例子，这是当 setInterval 被调用 7 次时，n = 64 + 9 * 7。</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/n-6463.png" alt=""></p>
<p>此时第一行 <code>j</code> 依然等于 1。而 63 -- 64 值为 2.x，62 -- 63 值为 1.x。由于 <code>1^2 = 3 // odd - .</code>，<code>1 ^ 1 = 0 //even - p</code>。所以展示效果为一串 <code>.</code> 后面跟随了一串 <code>p</code>。如图所示：</p>
<p><img src="https://www.alexkras.com/wp-content/uploads/n-6463-rendered.png" alt=""></p>
<p>之后 Demo 将会按类似的规则反复进行渲染。</p>
<p>代码的原理大致如此，尽管亲手进行正向压缩简化代码到如此程度确实很难，但是去尝试着理解它也是很有趣的。希望这篇文章能对你有所帮助。</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文链接 : &lt;a href=&quot;https://www.alexkras.com/reverse-engineering-one-line-of-javascript/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Reverse
    
    </summary>
    
      <category term="JavaScript" scheme="http://qcyoung.com/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="http://qcyoung.com/tags/JavaScript/"/>
    
      <category term="源码解读" scheme="http://qcyoung.com/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB/"/>
    
  </entry>
  
  <entry>
    <title>360 &amp; 美团前端社招面经及部分面试题</title>
    <link href="http://qcyoung.com/2017/04/09/360%20&amp;%20%E7%BE%8E%E5%9B%A2%E5%89%8D%E7%AB%AF%E7%A4%BE%E6%8B%9B%E9%9D%A2%E7%BB%8F%E5%8F%8A%E9%83%A8%E5%88%86%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
    <id>http://qcyoung.com/2017/04/09/360 &amp; 美团前端社招面经及部分面试题/</id>
    <published>2017-04-09T14:37:26.000Z</published>
    <updated>2017-04-09T15:16:14.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="ji-ben-liu-cheng-ji-yao-dian">基本流程及要点</h2>
<p>一般是至少三轮的技术面 + 一轮 HR 面，三轮技术一定程度上可能是你未来的同事 + 架构 + 上级。</p>
<h2 id="mian-shi-ti-mu">面试题目</h2>
<h3 id="bi-bao-yuan-li-ji-ying-yong">闭包原理及应用</h3>
<h3 id="jquery-yuan-ma-ru-delegate-yuan-li">jquery 源码，如 delegate 原理</h3>
<p>原理利用了JS 事件的事件冒泡机制，在 document(或事件源的父层)进行监听，冒泡到监听点后，判断事件源是否自己设定的元素</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">delegate</span>(<span class="params">parent, eventType, selector, fn</span>)</span>&#123;</span>
<span class="line">  <span class="comment">//参数处理</span></span>
<span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span> parent === <span class="string">'string'</span>)&#123;</span>
<span class="line">    <span class="keyword">var</span> parent = <span class="built_in">document</span>.getElementById(parent);</span>
<span class="line">    !parent &amp;&amp; alert(<span class="string">'parent not found'</span>);</span>
<span class="line">  &#125;</span>
<span class="line"></span>
<span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span> selector !== <span class="string">'string'</span>)&#123;</span>
<span class="line">      alert(<span class="string">'selector is not string'</span>);</span>
<span class="line">  &#125;</span>
<span class="line">        </span>
<span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span> fn !== <span class="string">'function'</span>)&#123;</span>
<span class="line">       alert(<span class="string">'fn is not function'</span>);</span>
<span class="line">  &#125;</span>
<span class="line">        </span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">handle</span>(<span class="params">e</span>)</span>&#123;</span>
<span class="line">    <span class="comment">// 获取 event 对象</span></span>
<span class="line">    <span class="comment">// 标准 DOM 方法事件处理函数第一个参数是 event 对象</span></span>
<span class="line">    <span class="comment">// IE可以使用全局变量 window.event</span></span>
<span class="line">    <span class="keyword">var</span> evt = <span class="built_in">window</span>.event ? <span class="built_in">window</span>.event : e;</span>
<span class="line">  </span>
<span class="line">    <span class="comment">// 获取触发事件的原始事件源</span></span>
<span class="line">    <span class="comment">// 标准 DOM 方法是用 target 获取当前事件源</span></span>
<span class="line">    <span class="comment">// IE 使用 evt.srcElement 获取事件源</span></span>
<span class="line">    <span class="keyword">var</span> target = evt.target || evt.srcElement;</span>
<span class="line">  </span>
<span class="line">    <span class="comment">// 获取当前正在处理的事件源</span></span>
<span class="line">    <span class="comment">// 标准 DOM 方法是用 currentTarget 获取当前事件源</span></span>
<span class="line">    <span class="comment">// IE 中的 this 指向当前处理的事件源</span></span>
<span class="line">    <span class="keyword">var</span> currentTarget= e ? e.currentTarget : <span class="keyword">this</span>;</span>
<span class="line"></span>
<span class="line">    <span class="keyword">if</span>(target.id === selector || target.className.indexOf(selector) != <span class="number">-1</span>)&#123;</span>
<span class="line">      fn.call(target);</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">        </span>
<span class="line">  parent[eventType]=handle;</span>
<span class="line">&#125;</span>
<span class="line">    </span>
<span class="line">delegate(<span class="string">'container'</span>, <span class="string">'onclick'</span>, <span class="string">'listener'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    alert(<span class="keyword">this</span>);    </span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<h3 id="jian-shu-js-drag-shi-jian-yi-ji-mo-ni-drag-shi-jian-de-fang-an">简述 JS drag 事件以及模拟 drag 事件的方案</h3>
<p>一个完整的 drag and drop 流程通常包含以下几个步骤:</p>
<ol>
<li>
<p>设置可拖拽目标. 设置属性 draggable=&quot;true&quot; 实现元素的可拖拽.</p>
</li>
<li>
<p>监听 dragstart 设置拖拽数据</p>
</li>
<li>
<p>为拖拽操作设置反馈图标 (可选)</p>
</li>
<li>
<p>设置拖放效果, 如 copy, move, link</p>
</li>
<li>
<p>设置拖放目标, 默认情况下浏览器阻止所有的拖放操作, 所以需要监听 dragenter 或者 dragover 取消浏览器默认行为使元素可拖放.</p>
</li>
<li>
<p>监听 drop 事件执行所需操作</p>
</li>
</ol>
<p>实现 drag</p>
<ol>
<li>
<p>onmousedown + onmousemove → startDrag()</p>
</li>
<li>
<p>onmouseup → stopDrag()</p>
</li>
<li>
<p>偏移值，两次事件的鼠标位置记录：
e.pageX || e.clientX + scrollX;
e.pageY || e.clientY + scrollY;</p>
</li>
</ol>
<h3 id="get-can-shu-tong-guo-zheng-ze-huo-qu-window-location-search">get 参数通过正则获取 window.location.search</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.location.search.match(<span class="regexp">/(([^?&amp;=]+)(=([^?&amp;=]*))*)/g</span>)</span>
</pre></td></tr></table></figure>
<h3 id="jian-dan-jie-shao-css-3-dong-hua-shu-xing-ji-yong-fa-ru-he-you-hua-dong-hua-xing-neng">简单介绍 CSS3 动画属性及用法，如何优化动画性能</h3>
<ol>
<li>
<p>transition</p>
</li>
<li>
<p>animation</p>
</li>
</ol>
<p>transition 主要设置四个过渡属性：</p>
<ul>
<li>
<p>transition-property // 规定设置过渡效果的 CSS 属性的名称。</p>
</li>
<li>
<p>transition-duration // 规定完成过渡效果需要多少秒或毫秒。</p>
</li>
<li>
<p>transition-timing-function // 规定速度效果的速度曲线。</p>
</li>
<li>
<p>transition-delay  //  定义过渡效果何时开始。</p>
</li>
</ul>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span>&#123;</span>
<span class="line">  <span class="attribute">width</span>:<span class="number">100px</span>;</span>
<span class="line">  <span class="attribute">background</span>:blue;</span>
<span class="line">  <span class="attribute">transition</span>:width <span class="number">2s</span>;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="selector-tag">div</span><span class="selector-pseudo">:hover</span>&#123;</span>
<span class="line">  <span class="attribute">width</span>:<span class="number">300px</span>;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>注释：请始终设置 transition-duration 属性，否则时长为 0，就不会产生过渡效果。</p>
<ol start="2">
<li>animation 属性是一个简写属性，用于设置六个动画属性：</li>
</ol>
<ul>
<li>animation-name // 规定需要绑定到选择器的 keyframe 名称。</li>
<li>animation-duration // 规定完成动画所花费的时间，以秒或毫秒计。</li>
<li>animation-timing-function // 规定动画的速度曲线。</li>
<li>animation-delay // 规定在动画开始之前的延迟。</li>
<li>animation-iteration-count // 规定动画应该播放的次数。</li>
<li>animation-direction // 规定是否应该轮流反向播放动画。</li>
</ul>
<p>优化具体参考即为如下几点:</p>
<p>由于渲染三阶段分为：Layout—&gt;Paint—&gt;Composite，可针对此三者分别进行优化。
优化目标：15FPS 不流畅 ，30FPS+感觉流畅，60FPS舒适完美</p>
<p>触发Layout的方式有：</p>
<ul>
<li>改变 width, height, margin 等和大小、位置相关的属性</li>
<li>读取 size, position 相关得属性</li>
</ul>
<p>要点：</p>
<ul>
<li>使用 transform 代替 top, left 的动画</li>
<li>分离读写，减少 Layout</li>
<li>面对解耦代码，使用 rAF 推迟的方法分离读写。</li>
</ul>
<p>触发 Paint 的方式：</p>
<p>当修改 border-radius, box-shadow, color 等展示相关属性时，会触发 paint
要点：</p>
<ul>
<li>简化绘制的复杂度</li>
<li>避免不必要的绘制</li>
<li>减少绘制区域</li>
</ul>
<p>Composite 小结:</p>
<p>GPU 是有限度的，不要滥用 GPU 资源生成不必要的 Layer
留意意外生成的 Layer</p>
<p>可结合贝塞尔曲线(cubic-bezier)，开启硬件加速。will-change 属性</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span>&#123;</span>
<span class="line">  <span class="attribute">width</span>:<span class="number">100px</span>;</span>
<span class="line">  <span class="attribute">height</span>:<span class="number">100px</span>;</span>
<span class="line">  <span class="attribute">background</span>:red;</span>
<span class="line">  <span class="attribute">position</span>:relative;</span>
<span class="line">  <span class="attribute">animation</span>:mymove <span class="number">5s</span> infinite;</span>
<span class="line">  <span class="attribute">-webkit-animation</span>:mymove <span class="number">5s</span> infinite; <span class="comment">/*Safari and Chrome*/</span></span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">@<span class="keyword">keyframes</span> mymove&#123;</span>
<span class="line">  <span class="selector-tag">from</span> &#123;<span class="attribute">left</span>:<span class="number">0px</span>;&#125;</span>
<span class="line">  <span class="selector-tag">to</span> &#123;<span class="attribute">left</span>:<span class="number">200px</span>;&#125;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">@-<span class="keyword">webkit</span>-<span class="keyword">keyframes</span> mymove&#123;</span>
<span class="line">  <span class="selector-tag">from</span> &#123;<span class="attribute">left</span>:<span class="number">0px</span>;&#125;</span>
<span class="line">  <span class="selector-tag">to</span> &#123;<span class="attribute">left</span>:<span class="number">200px</span>;&#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="ke-li-hua-lian-jia-shi-xian">柯里化连加实现</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="keyword">var</span> sum = <span class="number">0</span>, i, len;</span>
<span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>, len = <span class="built_in">arguments</span>.length; i &lt; len; i++) &#123;</span>
<span class="line">    sum += <span class="built_in">arguments</span>[i];</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> sum;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> currying = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span>
<span class="line">        <span class="built_in">console</span>.log(<span class="built_in">arguments</span>)</span>
<span class="line">    <span class="keyword">var</span> _args = [];</span>
<span class="line"></span>
<span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">cb</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line"></span>
<span class="line">        <span class="keyword">if</span> (<span class="built_in">arguments</span>.length === <span class="number">0</span>) &#123;</span>
<span class="line">            <span class="keyword">return</span> fn.apply(<span class="keyword">this</span>, _args);</span>
<span class="line">        &#125;</span>
<span class="line"></span>
<span class="line">        <span class="built_in">Array</span>.prototype.push.apply(_args, <span class="built_in">arguments</span>);</span>
<span class="line"></span>
<span class="line">        <span class="keyword">return</span> cb;</span>
<span class="line">    &#125;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> curryingAdd = currying(add);</span>
<span class="line"></span>
<span class="line">curryingAdd(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>)(<span class="number">4</span>)(); <span class="comment">// 10</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> add321 = curryingAdd(<span class="number">3</span>)(<span class="number">2</span>, <span class="number">1</span>);</span>
<span class="line"></span>
<span class="line">add321(<span class="number">4</span>)(); <span class="comment">// 10</span></span>
</pre></td></tr></table></figure>
<h3 id="position-guan-jian-zi-zhi-you-na-ji-chong-yi-ji-xiang-ying-de-biao-xian-xing-shi">position 关键字值有哪几种，以及相应的表现形式</h3>
<p>static / relative / absolute / fixed / sticky(新特性)</p>
<p>粘性定位是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位，之后为固定定位。</p>
<p>其盒位置根据正常流计算，然后相对于该元素在流中的 flow root（BFC）和 containing block（最近的块级祖先元素）定位。在所有情况下，该元素定位均不对后续元素造成影响。当元素 B 被粘性定位时，后续元素的位置仍按照 B 未定位时的位置来确定。position: sticky 对 table 元素的效果与 position: relative 相同。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">#one &#123; position: sticky; top: 10px; &#125;</span>
</pre></td></tr></table></figure>
<h3 id="zhong-jian-jian-he-cha-jian-de-qu-bie">中间件和插件的区别</h3>
<p>中间件是一种独立的系统软件或服务程序，分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机 / 服务器的操作系统之上，管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统即使它们具有不同的接口，但通过中间件相互之间仍能交换信息。执行中间件的一个关键途径是信息传递。通过中间件，应用程序可以工作于多平台或 OS 环境。</p>
<p>插件是一种遵循一定规范的应用程序接口编写出来的程序。</p>
<h3 id="bootstrap-grid-shi-xian-yuan-li">Bootstrap grid 实现原理。</h3>
<p>浮动，宽度瓜分，媒体查询，响应式布局。</p>
<h3 id="ji-cheng-de-fang-shi">继承的方式。</h3>
<p>原型链继承（对象间的继承）
类式继承（构造函数间的继承）
ES6 Class</p>
<h3 id="canvas-svg-qu-bie">Canvas SVG 区别。</h3>
<p>Canvas 适用场景：</p>
<p>Canvas 提供的功能更原始，适合像素处理，动态渲染和大数据量绘制；
Canvas 是使用 JavaScript 程序绘图，提供画布标签和绘制 API(动态生成)；
Canvas 是基于位图的图像，它不能够改变大小，只能缩放显示；</p>
<p>SVG 适用场景：</p>
<p>SVG 功能更完善，适合静态图片展示，高保真文档查看和打印的应用场景
SVG 是使用 XML 文档描述来绘图，是一整套独立的矢量图形语言；
SVG 更适合用来做动态交互，而且 SVG 绘图很容易编辑，只需要增加或移除相应的元素就可以了。
SVG 是基于矢量的，所有它能够很好的处理图形大小的改变。</p>
<p>二者是有不同用途的，作为一个开发者，你应该做的是理解应用程序的具体需求并选择正确的技术来实现它。</p>
<h3 id="ri-qi-huan-suan-qiu-tian-shu-chai-deng">日期换算，求天数差等</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="string">'2014-04-23'</span>;</span>
<span class="line"><span class="keyword">var</span> date1 = <span class="keyword">new</span> <span class="built_in">Date</span>(a);</span>
<span class="line"><span class="keyword">var</span> b = <span class="string">'2014-04-24'</span>;</span>
<span class="line"><span class="keyword">var</span> date2 = <span class="keyword">new</span> <span class="built_in">Date</span>(b);</span>
<span class="line"><span class="keyword">var</span> days = <span class="built_in">Math</span>.floor((date2 - date1) / <span class="number">1000</span> / <span class="number">60</span> / <span class="number">60</span> / <span class="number">24</span>)</span>
</pre></td></tr></table></figure>
<h3 id="let-ju-ti-wei-shi-yao-hui-you-lin-shi-si-yu">let 具体为什么会有临时死域。</h3>
<p>详见<a href="http://www.qcyoung.com/2016/08/03/%E3%80%90%E8%AF%91%E3%80%91JavaScript%20%E5%8F%98%E9%87%8F%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%EF%BC%9A%E4%B8%BA%E4%BB%80%E4%B9%88%20let%20%E4%B8%8D%E5%AD%98%E5%9C%A8%E5%8F%98%E9%87%8F%E6%8F%90%E5%8D%87/" target="_blank" rel="external">JavaScript 变量的生命周期：为什么 let 不存在变量提升
</a></p>
<h3 id="jian-kong-yi-ge-dom-ti-huan-guo-cheng">监控一个 DOM 替换过程</h3>
<p>原生方法监听 DOM 结构改变事件
<a href="">https://developer.mozilla.org/en-US/docs/XUL/Events#Mutation_DOM_events</a></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="built_in">document</span>.addEventListener(<span class="string">'DOMNodeInserted'</span>,<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;alert(<span class="number">1</span>)&#125;,<span class="literal">false</span>);</span>
<span class="line"><span class="built_in">document</span>.addEventListener(<span class="string">'DOMAttrModified'</span>,<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;alert(<span class="number">1</span>)&#125;,<span class="literal">false</span>);</span>
<span class="line"><span class="built_in">document</span>.addEventListener(<span class="string">'DOMNodeRemoved'</span>,<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;alert(<span class="number">1</span>)&#125;,<span class="literal">false</span>);</span>
</pre></td></tr></table></figure>
<p>变动事件包括以下不同事件类型：</p>
<ul>
<li>
<p>DOMSubtreeModified: 在 DOM 结构中发生任何变化时触发</p>
</li>
<li>
<p>DOMNodeInserted: 在一个节点作为子节点被插入到另一个节点中时触发</p>
</li>
<li>
<p>DOMNodeRemoved: 在节点从其父节点中被移除时触发</p>
</li>
<li>
<p>DOMNodeRemovedFromDocument: 在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发</p>
</li>
<li>
<p>DOMNodeInsertedIntoDocument: 在一个节点被直接插入文档或通过子树间接插入文档之后触发</p>
</li>
<li>
<p>DOMAttrModified: 在属性被修改之后触发</p>
</li>
<li>
<p>DOMCharacterDataModified: 在文本节点的值发生变化时触发</p>
</li>
</ul>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver" target="_blank" rel="external">构造函数 MutationObserver</a></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Firefox和Chrome早期版本中带有前缀</span></span>
<span class="line"><span class="keyword">var</span> MutationObserver = <span class="built_in">window</span>.MutationObserver || <span class="built_in">window</span>.WebKitMutationObserver || <span class="built_in">window</span>.MozMutationObserver</span>
<span class="line"></span>
<span class="line"><span class="comment">// 选择目标节点</span></span>
<span class="line"><span class="keyword">var</span> target = <span class="built_in">document</span>.querySelector(<span class="string">'#some-id'</span>);</span>
<span class="line"> </span>
<span class="line"><span class="comment">// 创建观察者对象</span></span>
<span class="line"><span class="keyword">var</span> observer = <span class="keyword">new</span> MutationObserver(<span class="function"><span class="keyword">function</span>(<span class="params">mutations</span>) </span>&#123;</span>
<span class="line">  mutations.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">mutation</span>) </span>&#123;</span>
<span class="line">    <span class="built_in">console</span>.log(mutation.type);</span>
<span class="line">  &#125;);    </span>
<span class="line">&#125;);</span>
<span class="line"> </span>
<span class="line"><span class="comment">// 配置观察选项:</span></span>
<span class="line"><span class="keyword">var</span> config = &#123; <span class="attr">attributes</span>: <span class="literal">true</span>, <span class="attr">childList</span>: <span class="literal">true</span>, <span class="attr">characterData</span>: <span class="literal">true</span> &#125;</span>
<span class="line"> </span>
<span class="line"><span class="comment">// 传入目标节点和观察选项</span></span>
<span class="line">observer.observe(target, config);</span>
<span class="line"> </span>
<span class="line"><span class="comment">// 随后,你还可以停止观察</span></span>
<span class="line">observer.disconnect();</span>
</pre></td></tr></table></figure>
<h3 id="web-socket-jian-shu-yuan-li">WebSocket 简述原理</h3>
<p>WebSocket 协议基于 TCP，解决过去为了实现即时通讯而采用的轮询方案，解决了大量交换 HTTP header，信息交换效率低的问题。在浏览器和服务器之间建立双向连接。</p>
<h3 id="mou-pao-kuai-pai-cha-ru-pai-xu-yuan-li-deng">冒泡 + 快排 + 插入排序原理等</h3>
<p>详见：<a href="http://www.qcyoung.com/2016/12/18/JavaScript%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/" target="_blank" rel="external">JavaScript 排序算法汇总</a></p>
<h3 id="wang-luo-xie-yi-http-https-http-2-de-ji-ben-gai-nian-deng">网络协议 HTTP HTTPS HTTP2 的基本概念等</h3>
<h3 id="zi-gua-ying-bu-ju-yi-dong-duan-zi-gua-ying-deng-wen-ti">自适应布局，移动端自适应等问题</h3>
<h3 id="she-ji-mo-shi">设计模式</h3>
<p>这方面被问到的比较多的：观察者模式，工厂模式，职责链模式等等</p>
<p>主要是经常涉及应用于 js 开发组件。比如如何去设计一个前端UI组件，应该公开出哪些方法，应该提供哪些接口，应该提供哪些事件。哪部分逻辑流程应该开放出去让用户自行编写，如何实现组件与组件之间的通信，如何实现高内聚低耦合，如何实现组件的高复用等等。</p>
<h3 id="ni-zen-yao-kan-xxx">你怎么看 XXX</h3>
<p>结合框架原理，社区环境，技术选型（技术、业务、人）等</p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;ji-ben-liu-cheng-ji-yao-dian&quot;&gt;基本流程及要点&lt;/h2&gt;
&lt;p&gt;一般是至少三轮的技术面 + 一轮 HR 面，三轮技术一定程度上可能是你未来的同事 + 架构 + 上级。&lt;/p&gt;
&lt;h2 id=&quot;mian-shi-ti-mu&quot;&gt;面试题目&lt;/
    
    </summary>
    
      <category term="面试" scheme="http://qcyoung.com/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
      <category term="职业生涯" scheme="http://qcyoung.com/tags/%E8%81%8C%E4%B8%9A%E7%94%9F%E6%B6%AF/"/>
    
      <category term="前端知识" scheme="http://qcyoung.com/tags/%E5%89%8D%E7%AB%AF%E7%9F%A5%E8%AF%86/"/>
    
      <category term="题目" scheme="http://qcyoung.com/tags/%E9%A2%98%E7%9B%AE/"/>
    
  </entry>
  
  <entry>
    <title>Babel 基础及代码转换简单探究</title>
    <link href="http://qcyoung.com/2017/02/06/Babel%20%E5%9F%BA%E7%A1%80%E5%8F%8A%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2%E7%AE%80%E5%8D%95%E6%8E%A2%E7%A9%B6/"/>
    <id>http://qcyoung.com/2017/02/06/Babel 基础及代码转换简单探究/</id>
    <published>2017-02-06T03:52:03.000Z</published>
    <updated>2017-02-06T03:52:03.000Z</updated>
    
    <content type="html"><![CDATA[<p>本文基于 <code>babel-preset-es2015 : 6.18.0</code> ，<code>babel-core : 6.21.0</code>版本进行。</p>
<h2 id="ji-chu-zhi-shi">基础知识</h2>
<p><a href="https://babeljs.io/" target="_blank" rel="external">Babel</a> 是 JavaScript 编译器，更确切地说是源码到源码的编译器，通常也叫做“转换编译器(transpiler)”，它的工作流可以用下面一张图来表示，代码首先经由 babylon 解析成抽象语法树（AST），后经一些遍历和分析转换（主要过程），最后根据转换后的 AST 生成新的常规代码。</p>
<p><img src="https://img.alicdn.com/tps/TB1nP2ONpXXXXb_XpXXXXXXXXXX-1958-812.png" alt=""></p>
<h3 id="chou-xiang-yu-fa-shu-ast">抽象语法树(AST)</h3>
<p>在这其中，理解 AST 十分重要，为了让计算机能够更好地理解，所以需要将代码转换为 AST 。我们可以来看看下面这段代码被解析成 AST 后对应的结构图：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">abs</span>(<span class="params">number</span>) </span>&#123;</span>
<span class="line">  <span class="keyword">if</span> (number &gt;= <span class="number">0</span>) &#123;  <span class="comment">// test</span></span>
<span class="line">    <span class="keyword">return</span> number;  <span class="comment">// consequent</span></span>
<span class="line">  &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">    <span class="keyword">return</span> -number; <span class="comment">// alternate</span></span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p><img src="https://img.alicdn.com/tps/TB1NnDQNpXXXXXmXFXXXXXXXXXX-1015-756.png" alt=""></p>
<p>在 <a href="https://github.com/babel/babylon/blob/master/ast/spec.md" target="_blank" rel="external">Babylon 的 AST 规范文档中</a>对每个节点类型都做了详细的说明，你可以对照各个节点类型在这查找到所需要的信息。在这个例子中，我们主要关注函数声明里的内容， <code>IfStatement</code> 对应代码中的 <code>if...else</code> 区块的内容，我们先对条件（test）进行判断，这里是个简单的二元表达式，我们的分支也会从这个条件继续进行下去，consequent 代表条件值为 true 的分支，alternate 代表条件值为 false 的分支，最后两条分支各自在 ReturnStatement 节点进行返回。</p>
<p>了解 AST 各个节点的类型是理解 Babel 、编写插件的关键，AST 通常情况下都是比较复杂的，上述一段简单的函数定义也生成了比较大的 AST，对于一些复杂的程序，我们可以借助 <a href="http://astexplorer.net/" target="_blank" rel="external">astexplorer</a> 来帮我们分析 AST 的结构。 <a href="http://astexplorer.net/#/Z1exs6BWMq/36" target="_blank" rel="external">这里</a>是上述代码的一个示例链接。</p>
<h3 id="babel-de-chu-li-bu-zou">Babel 的处理步骤</h3>
<p>Babel 的三个主要处理步骤分别是： <code>解析（parse）</code>，<code>转换（transform）</code>，<code>生成（generate）</code>。</p>
<h4 id="jie-xi">解析</h4>
<p>解析步骤接收代码并输出 AST。 这个步骤分为两个阶段：<a href="https://zh.wikipedia.org/wiki/%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90" target="_blank" rel="external">词法分析（Lexical Analysis）</a>(把字符串形式的代码转换为 令牌（tokens） 流) 和 <a href="https://zh.wikipedia.org/wiki/%E8%AA%9E%E6%B3%95%E5%88%86%E6%9E%90%E5%99%A8" target="_blank" rel="external">语法分析（Syntactic Analysis）</a>(令牌流转换成 AST 的形式)。</p>
<h4 id="zhuan-huan">转换</h4>
<p><a href="https://en.wikipedia.org/wiki/Program_transformation" target="_blank" rel="external">程序转换(Program transformation)</a>步骤接收 AST 并对其进行遍历，在此过程中对节点进行添加、更新及移除等操作。 这是 Babel 或是其他编译器中最复杂的过程 同时也是插件将要介入工作的部分。</p>
<h4 id="sheng-cheng">生成</h4>
<p><a href="https://en.wikipedia.org/wiki/Code_generation_(compiler)" target="_blank" rel="external">代码生成(Code generation)</a>步骤把最终（经过一系列转换之后）的 AST 转换成字符串形式的代码，同时还会创建源码映射（source maps）。.</p>
<p>代码生成其实很简单：深度优先遍历(DFS) 整个 AST，然后构建可以表示转换后代码的字符串。</p>
<h3 id="bian-li">遍历</h3>
<p>想要转换 AST 你需要进行递归的树形遍历。在进行节点遍历前还需要先了解 visitor 和 path 的概念，前者相当于从众多节点类型中选择开发者所需要的节点，后者相当于对节点之间的关系的访问。</p>
<h4 id="visitor">visitor</h4>
<p>Babel 使用 <code>babel-traverse</code> 进行树状的遍历，基本的树的遍历分为 DFS 和 BFS。AST 树的遍历使用 DFS，这里 Babel 提供了一个 visitor 对象来供我们获取 AST 里的具体节点，比如在我只想访问 if...else 生成的节点，我们可以在 visitor 里指定获取它所对应的节点：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> visitor = &#123;</span>
<span class="line">  IfStatement() &#123;</span>
<span class="line">    <span class="built_in">console</span>.log(<span class="string">'get if'</span>);</span>
<span class="line">  &#125;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>之所以使用这样的术语是因为有一个<a href="https://zh.wikipedia.org/wiki/%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F" target="_blank" rel="external">访问者模式（visitor）</a>的概念。对于每个结点，有向下遍历进入结点（enter)和向上退出结点(exit)两个时刻（对应递归调用的入栈、出栈），我们可以在此时来访问结点。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> visitor = &#123;</span>
<span class="line">  <span class="attr">IfStatement</span>: &#123;</span>
<span class="line">    enter() &#123;&#125;,</span>
<span class="line">    exit() &#123;&#125;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h4 id="path">path</h4>
<p>visitor 模式中我们对节点的访问实际上是对节点路径的访问，在这个模式中我们一般把path 当作参数传入节点选择器中。path 表示两个节点之间的连接，通过这个对象我们可以访问到节点、父节点以及进行一系列跟节点操作相关的方法（类似 DOM 的操作）。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> babel = <span class="built_in">require</span>(<span class="string">'babel-core'</span>);</span>
<span class="line"><span class="keyword">var</span> t = <span class="built_in">require</span>(<span class="string">'babel-types'</span>);</span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> code = <span class="string">`d = a + b + c`</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> visitor = &#123;</span>
<span class="line">  Identifier(path) &#123;</span>
<span class="line">    <span class="built_in">console</span>.log(path.node.name); <span class="comment">// d a b c</span></span>
<span class="line">  &#125;</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> result = babel.transform(code, &#123;</span>
<span class="line">  <span class="attr">plugins</span>: [&#123;</span>
<span class="line">    <span class="attr">visitor</span>: visitor</span>
<span class="line">  &#125;]</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p>以上面的例子，我们有一个 <code>FunctionDeclaration</code> 类型如下。它有几个属性：<code>id</code>，<code>params</code>，和 <code>body</code>，每一个都有一些内嵌节点。我们依次遍历每个节点即可。Babel 的转换步骤就是循环这样的遍历过程。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
<span class="line">45</span>
<span class="line">46</span>
<span class="line">47</span>
<span class="line">48</span>
<span class="line">49</span>
<span class="line">50</span>
<span class="line">51</span>
<span class="line">52</span>
<span class="line">53</span>
<span class="line">54</span>
</pre></td><td class="code"><pre><span class="line">&#123;</span>
<span class="line">  <span class="attr">type</span>: <span class="string">"FunctionDeclaration"</span>,</span>
<span class="line">  <span class="attr">id</span>: &#123;</span>
<span class="line">    <span class="attr">type</span>: <span class="string">"Identifier"</span>,</span>
<span class="line">    <span class="attr">name</span>: <span class="string">"abs"</span></span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">params</span>: [&#123;</span>
<span class="line">    <span class="attr">type</span>: <span class="string">"Identifier"</span>,</span>
<span class="line">    <span class="attr">name</span>: <span class="string">"number"</span></span>
<span class="line">  &#125;],</span>
<span class="line">  <span class="attr">body</span>: &#123;</span>
<span class="line">    <span class="attr">type</span>: <span class="string">"BlockStatement"</span>,</span>
<span class="line">    <span class="attr">body</span>: [&#123;</span>
<span class="line">      <span class="attr">type</span>: <span class="string">"IfStatement"</span>,</span>
<span class="line">      <span class="attr">test</span>: &#123;</span>
<span class="line">        <span class="attr">type</span>: <span class="string">"BinaryExpression"</span>,</span>
<span class="line">        <span class="attr">left</span>: &#123;</span>
<span class="line">          <span class="attr">type</span>: <span class="string">"Identifier"</span>,</span>
<span class="line">          <span class="attr">name</span>: <span class="string">"number"</span></span>
<span class="line">        &#125;,</span>
<span class="line">        <span class="attr">operator</span>: <span class="string">"&gt;="</span>,</span>
<span class="line">        <span class="attr">right</span>: &#123;</span>
<span class="line">          <span class="attr">type</span>: <span class="string">"Literal"</span>,</span>
<span class="line">          <span class="attr">value</span>: <span class="string">"0"</span></span>
<span class="line">        &#125;</span>
<span class="line">      &#125;,</span>
<span class="line">      <span class="attr">consequent</span>:&#123;</span>
<span class="line">        <span class="attr">type</span>: <span class="string">"BlockStatement"</span>,</span>
<span class="line">        <span class="attr">body</span>: [&#123;</span>
<span class="line">          <span class="attr">type</span>: <span class="string">"ReturnStatement"</span>,</span>
<span class="line">          <span class="attr">argument</span>: &#123;</span>
<span class="line">            <span class="attr">type</span>: <span class="string">"Identifier"</span>,</span>
<span class="line">            <span class="attr">name</span>: <span class="string">"number"</span></span>
<span class="line">          &#125;</span>
<span class="line">        &#125;]</span>
<span class="line">      &#125;,</span>
<span class="line">      <span class="attr">alternate</span>: &#123;</span>
<span class="line">        <span class="attr">type</span>: <span class="string">"BlockStatement"</span>,</span>
<span class="line">        <span class="attr">body</span>: [&#123;</span>
<span class="line">          <span class="attr">type</span>: <span class="string">"ReturnStatement"</span>,</span>
<span class="line">          <span class="attr">argument</span>: &#123;</span>
<span class="line">            <span class="attr">type</span>: <span class="string">"UnaryExpression"</span>,</span>
<span class="line">            <span class="attr">opertaor</span>: <span class="string">"-"</span>,</span>
<span class="line">            <span class="attr">argument</span>: &#123;</span>
<span class="line">              <span class="attr">type</span>: <span class="string">"Identifier"</span>,</span>
<span class="line">              <span class="attr">name</span>: <span class="string">"number"</span></span>
<span class="line">            &#125;,</span>
<span class="line">            <span class="attr">name</span>: <span class="string">"number"</span></span>
<span class="line">          &#125;</span>
<span class="line">        &#125;]</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;]</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="dui-ying-dai-ma">对应代码</h3>
<p>我们统称的 babel 源码实质上对应于 npm 上的多个包，具体可以参考对应的说明文件，
主要包括 <a href="https://www.npmjs.com/package/babel-core" target="_blank" rel="external">核心代码(babel-core)</a>、工具包（<a href="https://www.npmjs.com/package/babel-cli" target="_blank" rel="external">babel-cli</a> 等）、Preset（<a href="https://www.npmjs.com/package/babel-preset-es2015" target="_blank" rel="external">babel-preset-es2015</a> 等），另外还有一系列 helper 包。</p>
<h4 id="babel-core-he-xin-bao">babel-core 核心包</h4>
<p>babel 中最核心的是 babel-core 这个包，它向外暴露出 <code>babel.transform</code> 接口，供类似 <code>babel.transform(code, options);</code> 的方式调用，而核心代码位于 <code>transformation/pipeline.js</code> 文件中，所有信息都会挂在到 <code>transformation/file</code> 所暴露出来的数据结构 <code>File</code> 上。</p>
<p><code>File</code> 维护的是一个文件的所有信息，包括 babel 处理所用的插件等信息。babel 的繁荣与其强大的插件管理机制是密不可分的，而插件主要由 <code>pluginPasses</code> 和 <code>pluginVisitors</code> 来维护。</p>
<p>为了保证在遍历路径的时候能够快速访问对应的插件处理方法，babel 对 <code>pluginVisitors</code> 做了一定的预处理，将所有同类型 Identifier 的处理流程合并到了一起。具体用代码的角度来看，可以简化成这样：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> PluginA = &#123;</span>
<span class="line">  Identifier() &#123;&#125;,</span>
<span class="line">  FunctionDeclaration() &#123;&#125;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">const</span> PluginB = &#123;</span>
<span class="line">  BinaryExpression() &#123;&#125;,</span>
<span class="line">  FunctionDeclaration() &#123;&#125;</span>
<span class="line">&#125;</span>
<span class="line"><span class="comment">// 进行处理后得到(源码可见 babel-traverse/lib/visitors.js 中的 function merge())</span></span>
<span class="line"><span class="keyword">let</span> rootVisitor = &#123;</span>
<span class="line">  <span class="attr">Identifier</span>: [PluginA.Identifier],</span>
<span class="line">  <span class="attr">BinaryExpression</span>: [PluginB.Identifier],</span>
<span class="line">  <span class="attr">FunctionDeclaration</span>: [PluginA.FunctionDeclaration, PluginB.FunctionDeclaration]</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>再看一下后续的内部转码流程，其最外层 pipeline 中的代码很简短：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line">file.wrap(code, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">  file.addCode(code);</span>
<span class="line">  file.parseCode(code); <span class="comment">// 去除顶部的 #!/usr/bin/env node 信息，之后使用 babylon 解析出 ast，使用 babel-traverse 进行遍历</span></span>
<span class="line">  <span class="keyword">return</span> file.transform();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p>其过程分解用语言描述的话，对应上文步骤如下：</p>
<ol>
<li>使用 babylon 解析器对输入的源代码字符串进行解析并生成初始 AST（<code>File.prototype.parse</code>）</li>
<li><code>set AST</code> 过程：利用 <code>babel-traverse</code> 对 AST 进行遍历，并解析出整个树的 path，通过挂载的 <code>metadataVisitor</code> 读取对应的元信息。</li>
<li><code>transform</code> 过程：遍历 AST 树并应用各 transformers(plugin) 生成转换后的 AST 树</li>
<li>利用 babel-generator 将 AST 树生成为转码后的代码字符串。</li>
</ol>
<blockquote>
<p>注：以上面的代码片断为例，为了详细了解到整个编译过程，可以使用 <code>DEBUG=babel node main.js</code> 运行代码，这样就可以看到整个过程中的输出日志了。</p>
</blockquote>
<h2 id="zhuan-huan-jie-guo">转换结果</h2>
<p>由于 Babel 默认只转换新的 JavaScript 语法，而不转换新的 API，比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象，以及一些定义在全局对象上的方法（比如 <code>Object.assign</code>）都不会转码。
Babel 默认不转码的 API 非常多，具体可以查看<a href="https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-runtime/src/definitions.js" target="_blank" rel="external">详细清单</a></p>
<p>这里参考 进行补充</p>
<h3 id="babel-preset-2015">babel-preset-2015</h3>
<p>目前版本下插件列表如下：</p>
<ul>
<li><code>babel-plugin-check-es2015-constants</code> =&gt; 验证 es2015 常量</li>
<li><code>babel-plugin-transform-es2015-arrow-functions</code> =&gt; 箭头函数</li>
<li><code>babel-plugin-transform-es2015-block-scoped-functions</code> =&gt; 函数块级作用域</li>
<li><code>babel-plugin-transform-es2015-block-scoping</code> =&gt; let 和 const 块级作用域</li>
<li><code>babel-plugin-transform-es2015-classes</code> =&gt; class类</li>
<li><code>babel-plugin-transform-es2015-computed-properties</code> =&gt; 动态计算属性，如 <code>var x = 1;var obj = {[x]: 3};</code></li>
<li><code>babel-plugin-transform-es2015-destructuring</code> =&gt; 解构赋值</li>
<li><code>babel-plugin-transform-es2015-duplicate-keys</code> =&gt; 对象中重复的 key 转换成计算属性</li>
<li><code>babel-plugin-transform-es2015-for-of</code> =&gt; 对象 <code>for ... of</code> 遍历</li>
<li><code>babel-plugin-transform-es2015-function-name</code> =&gt; 函数 name 属性</li>
<li><code>babel-plugin-transform-es2015-literals</code> =&gt; unicode 字符串和数字字面值</li>
<li><code>babel-plugin-transform-es2015-modules-amd</code> =&gt; amd 模块转换</li>
<li><code>babel-plugin-transform-es2015-modules-commonjs</code> =&gt; commonjs 模块转换</li>
<li><code>babel-plugin-transform-es2015-modules-systemjs</code> =&gt; systemjs 模块转换</li>
<li><code>babel-plugin-transform-es2015-modules-umd</code> =&gt; umd 模块转换</li>
<li><code>babel-plugin-transform-es2015-object-super</code> =&gt; super 方法调用 prototype</li>
<li><code>babel-plugin-transform-es2015-parameters</code> =&gt; 函数参数默认值及扩展运算符</li>
<li><code>babel-plugin-transform-es2015-shorthand-properties</code> =&gt; 对象属性的快捷定义，如obj = { x, y }</li>
<li><code>babel-plugin-transform-es2015-spread</code> =&gt; 对象扩展运算符属性，如 <code>...foobar</code></li>
<li><code>babel-plugin-transform-es2015-sticky-regex</code> =&gt; 粘连修饰符 y.</li>
<li><code>babel-plugin-transform-es2015-template-literals</code> =&gt; es2015 模板</li>
<li><code>babel-plugin-transform-es2015-typeof-symbol</code> =&gt; symbol 特性</li>
<li><code>babel-plugin-transform-es2015-unicode-regex</code> =&gt; unicode 正则</li>
<li><code>babel-plugin-transform-regenerator</code> =&gt; generator特性</li>
</ul>
<h3 id="var-const-and-let">var, const and let</h3>
<h4 id="var-const-let-js">var_const_let.js</h4>
<p>首先基础的，转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span>
<span class="line"><span class="keyword">let</span> b = <span class="number">2</span>;</span>
<span class="line"><span class="keyword">const</span> c = <span class="number">3</span>;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span>
<span class="line"><span class="keyword">var</span> b = <span class="number">2</span>;</span>
<span class="line"><span class="keyword">var</span> c = <span class="number">3</span>;</span>
</pre></td></tr></table></figure>
<h4 id="let-js">let.js</h4>
<p>let的块级作用域怎么体现呢？转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a1 = <span class="number">1</span>;</span>
<span class="line"><span class="keyword">let</span> a2 = <span class="number">6</span>;</span>
<span class="line">&#123;</span>
<span class="line">    <span class="keyword">let</span> a1 = <span class="number">2</span>;</span>
<span class="line">    <span class="keyword">let</span> a2 = <span class="number">5</span>;</span>
<span class="line">    &#123;</span>
<span class="line">        <span class="keyword">let</span> a1 = <span class="number">4</span>;</span>
<span class="line">        <span class="keyword">let</span> a2 = <span class="number">5</span>;</span>
<span class="line">    &#125;</span>
<span class="line">&#125;</span>
<span class="line">a1 = <span class="number">3</span>;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="number">1</span>;</span>
<span class="line"><span class="keyword">var</span> a2 = <span class="number">6</span>;</span>
<span class="line">&#123;</span>
<span class="line">    <span class="keyword">var</span> _a = <span class="number">2</span>;</span>
<span class="line">    <span class="keyword">var</span> _a2 = <span class="number">5</span>;</span>
<span class="line">    &#123;</span>
<span class="line">        <span class="keyword">var</span> _a3 = <span class="number">4</span>;</span>
<span class="line">        <span class="keyword">var</span> _a4 = <span class="number">5</span>;</span>
<span class="line">    &#125;</span>
<span class="line">&#125;</span>
<span class="line">a1 = <span class="number">3</span>;</span>
</pre></td></tr></table></figure>
<p>可见这样的例子实质就是改变一下变量名，使之与外层不同。</p>
<h4 id="let-for-js">let_for.js</h4>
<p>那么看一下经典的 let for 场景，这里大家都知道如果 let 换成 var ，那么输出将会是 10，那么这样 babel 怎么处理呢？转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = [];</span>
<span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span>
<span class="line">  a[i] = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="built_in">console</span>.log(i);</span>
<span class="line">  &#125;;</span>
<span class="line">&#125;</span>
<span class="line">a[<span class="number">6</span>](); <span class="comment">// 6</span></span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = [];</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _loop = <span class="function"><span class="keyword">function</span> <span class="title">_loop</span>(<span class="params">i</span>) </span>&#123;</span>
<span class="line">  a[i] = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="built_in">console</span>.log(i);</span>
<span class="line">  &#125;;</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span>
<span class="line">  _loop(i);</span>
<span class="line">&#125;</span>
<span class="line">a[<span class="number">6</span>](); <span class="comment">// 6</span></span>
</pre></td></tr></table></figure>
<p>可见这里用了闭包做了处理。经典的 for 循环闭包处理方式。</p>
<h3 id="jie-gou-fu-zhi">解构赋值</h3>
<h4 id="destructuring-js">destructuring.js</h4>
<p>对于普通的解构赋值，转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> [foo, [[bar], baz]] = [<span class="number">1</span>, [[<span class="number">2</span>], <span class="number">3</span>]];</span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> [ , , third] = [<span class="string">"foo"</span>, <span class="string">"bar"</span>, <span class="string">"baz"</span>];</span>
<span class="line"><span class="comment">// third = "baz"</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> [x, , y] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span>
<span class="line"><span class="comment">// x = 1 y = 3</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> [head, ...tail] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span>
<span class="line"><span class="comment">// head = 1 tail = [2, 3, 4]</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> [i, j, ...k] = [<span class="string">'a'</span>];</span>
<span class="line"><span class="comment">// i = "a" j = undefined k = []</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> [m, n] = [<span class="number">1</span>];</span>
<span class="line"><span class="comment">// n = undefined</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> [a, [b], d] = [<span class="number">1</span>, [<span class="number">2</span>, <span class="number">3</span>], <span class="number">4</span>];</span>
<span class="line"><span class="comment">// a = 1 b = 2 d = 4</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> [a, b, c, d, e] = <span class="string">'hello'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> &#123;<span class="attr">length</span> : len&#125; = <span class="string">'hello'</span>;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> _slicedToArray = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="function"><span class="keyword">function</span> <span class="title">sliceIterator</span>(<span class="params">arr, i</span>) </span>&#123; <span class="keyword">var</span> _arr = []; <span class="keyword">var</span> _n = <span class="literal">true</span>; <span class="keyword">var</span> _d = <span class="literal">false</span>; <span class="keyword">var</span> _e = <span class="literal">undefined</span>; <span class="keyword">try</span> &#123; <span class="keyword">for</span> (<span class="keyword">var</span> _i = arr[<span class="built_in">Symbol</span>.iterator](), _s; !(_n = (_s = _i.next()).done); _n = <span class="literal">true</span>) &#123; _arr.push(_s.value); <span class="keyword">if</span> (i &amp;&amp; _arr.length === i) <span class="keyword">break</span>; &#125; &#125; <span class="keyword">catch</span> (err) &#123; _d = <span class="literal">true</span>; _e = err; &#125; <span class="keyword">finally</span> &#123; <span class="keyword">try</span> &#123; <span class="keyword">if</span> (!_n &amp;&amp; _i[<span class="string">"return"</span>]) _i[<span class="string">"return"</span>](); &#125; <span class="keyword">finally</span> &#123; <span class="keyword">if</span> (_d) <span class="keyword">throw</span> _e; &#125; &#125; <span class="keyword">return</span> _arr; &#125; <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">arr, i</span>) </span>&#123; <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(arr)) &#123; <span class="keyword">return</span> arr; &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">Symbol</span>.iterator <span class="keyword">in</span> <span class="built_in">Object</span>(arr)) &#123; <span class="keyword">return</span> sliceIterator(arr, i); &#125; <span class="keyword">else</span> &#123; <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"Invalid attempt to destructure non-iterable instance"</span>); &#125; &#125;; &#125;();</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> foo = <span class="number">1</span>,</span>
<span class="line">    bar = <span class="number">2</span>,</span>
<span class="line">    baz = <span class="number">3</span>;</span>
<span class="line">    </span>
<span class="line"><span class="keyword">var</span> _ref = [<span class="string">"foo"</span>, <span class="string">"bar"</span>, <span class="string">"baz"</span>],</span>
<span class="line">    third = _ref[<span class="number">2</span>];</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _ref2 = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],</span>
<span class="line">    x = _ref2[<span class="number">0</span>],</span>
<span class="line">    y = _ref2[<span class="number">2</span>];</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> head = <span class="number">1</span>,</span>
<span class="line">    tail = [<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _ref3 = [<span class="string">'a'</span>],</span>
<span class="line">    i = _ref3[<span class="number">0</span>],</span>
<span class="line">    j = _ref3[<span class="number">1</span>],</span>
<span class="line">    k = _ref3.slice(<span class="number">2</span>);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _ref4 = [<span class="number">1</span>],</span>
<span class="line">    m = _ref4[<span class="number">0</span>],</span>
<span class="line">    n = _ref4[<span class="number">1</span>];</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a = <span class="number">1</span>,</span>
<span class="line">    _ref5 = [<span class="number">2</span>, <span class="number">3</span>],</span>
<span class="line">    b = _ref5[<span class="number">0</span>],</span>
<span class="line">    d = <span class="number">4</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _hello = <span class="string">'hello'</span>,</span>
<span class="line">    _hello2 = _slicedToArray(_hello, <span class="number">5</span>),</span>
<span class="line">    x1 = _hello2[<span class="number">0</span>],</span>
<span class="line">    x2 = _hello2[<span class="number">1</span>],</span>
<span class="line">    x3 = _hello2[<span class="number">2</span>],</span>
<span class="line">    x4 = _hello2[<span class="number">3</span>],</span>
<span class="line">    x5 = _hello2[<span class="number">4</span>];</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _hello3 = <span class="string">'hello'</span>,</span>
<span class="line">    len = _hello3.length;</span>
</pre></td></tr></table></figure>
<p>可以看到，这里 babel 就是很正常的采用一一赋值的方式进行的，对于匿名数组等情况，babel 会帮你先定义一个变量存放这个数组，然后再对需要赋值的变量进行赋值。</p>
<h4 id="destructuring-object-js">destructuring_object.js</h4>
<p>还有一种对象深层次的解构赋值，转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span>
<span class="line">  <span class="attr">p</span>: [</span>
<span class="line">    <span class="string">'Hello'</span>,</span>
<span class="line">    &#123; <span class="attr">y</span>: <span class="string">'World'</span> &#125;</span>
<span class="line">  ]</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> &#123; <span class="attr">p</span>: [x, &#123; y &#125;] &#125; = obj;</span>
<span class="line"><span class="comment">// x = "Hello" y = "World"</span></span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> _slicedToArray = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">sliceIterator</span>(<span class="params">arr, i</span>) </span>&#123;</span>
<span class="line">        <span class="keyword">var</span> _arr = [];</span>
<span class="line">        <span class="keyword">var</span> _n = <span class="literal">true</span>;</span>
<span class="line">        <span class="keyword">var</span> _d = <span class="literal">false</span>;</span>
<span class="line">        <span class="keyword">var</span> _e = <span class="literal">undefined</span>;</span>
<span class="line">        <span class="keyword">try</span> &#123;</span>
<span class="line">           <span class="comment">// 用 Symbol.iterator 造了一个可遍历对象，然后进去遍历。</span></span>
<span class="line">            <span class="keyword">for</span> (<span class="keyword">var</span> _i = arr[<span class="built_in">Symbol</span>.iterator](), _s; !(_n = (_s = _i.next()).done); _n = <span class="literal">true</span>) &#123;</span>
<span class="line">                _arr.push(_s.value);</span>
<span class="line">                <span class="keyword">if</span> (i &amp;&amp; _arr.length === i) <span class="keyword">break</span>;</span>
<span class="line">            &#125;</span>
<span class="line">        &#125; <span class="keyword">catch</span> (err) &#123;</span>
<span class="line">            _d = <span class="literal">true</span>;</span>
<span class="line">            _e = err;</span>
<span class="line">        &#125; <span class="keyword">finally</span> &#123;</span>
<span class="line">            <span class="keyword">try</span> &#123;</span>
<span class="line">                <span class="keyword">if</span> (!_n &amp;&amp; _i[<span class="string">"return"</span>]) _i[<span class="string">"return"</span>]();</span>
<span class="line">            &#125; <span class="keyword">finally</span> &#123;</span>
<span class="line">                <span class="keyword">if</span> (_d) <span class="keyword">throw</span> _e;</span>
<span class="line">            &#125;</span>
<span class="line">        &#125;</span>
<span class="line">        <span class="keyword">return</span> _arr;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">arr, i</span>) </span>&#123;</span>
<span class="line">        <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(arr)) &#123;</span>
<span class="line">            <span class="keyword">return</span> arr;</span>
<span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">Symbol</span>.iterator <span class="keyword">in</span> <span class="built_in">Object</span>(arr)) &#123;</span>
<span class="line">            <span class="keyword">return</span> sliceIterator(arr, i);</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"Invalid attempt to destructure non-iterable instance"</span>);</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;;</span>
<span class="line">&#125;)();</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> obj = &#123;</span>
<span class="line">  <span class="attr">p</span>: [<span class="string">'Hello'</span>, &#123; <span class="attr">y</span>: <span class="string">'World'</span> &#125;]</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _obj$p = _slicedToArray(obj.p, <span class="number">2</span>),</span>
<span class="line">    x = _obj$p[<span class="number">0</span>],</span>
<span class="line">    y = _obj$p[<span class="number">1</span>].y;</span>
<span class="line"><span class="comment">// x = "Hello" y = "World"</span></span>
</pre></td></tr></table></figure>
<p>这里和上一个示例中对字符串进行赋值解构时 babel 都在代码顶部生产了一个公共的代码 <code>_slicedToArray</code>。它的作用主要是对对象里的属性转换成数组形式，并依靠 i 变量来取出我们真正需要的值，方便解构赋值的进行。</p>
<h4 id="destructuring-function-js">destructuring_function.js</h4>
<p>转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">move</span>(<span class="params">&#123;x, y&#125; = &#123; x: <span class="number">0</span>, y: <span class="number">0</span> &#125;</span>) </span>&#123;</span>
<span class="line">  <span class="keyword">return</span> [x, y];</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">move(&#123;<span class="attr">x</span>: <span class="number">3</span>, <span class="attr">y</span>: <span class="number">8</span>&#125;); <span class="comment">// [3, 8]</span></span>
<span class="line">move(&#123;<span class="attr">x</span>: <span class="number">3</span>&#125;); <span class="comment">// [3, undefined]</span></span>
<span class="line">move(&#123;&#125;); <span class="comment">// [undefined, undefined]</span></span>
<span class="line">move(); <span class="comment">// [0, 0]</span></span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">move</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="keyword">var</span> _ref = <span class="built_in">arguments</span>.length &gt; <span class="number">0</span> &amp;&amp; <span class="built_in">arguments</span>[<span class="number">0</span>] !== <span class="literal">undefined</span> ? <span class="built_in">arguments</span>[<span class="number">0</span>] : &#123; <span class="attr">x</span>: <span class="number">0</span>, <span class="attr">y</span>: <span class="number">0</span> &#125;,</span>
<span class="line">      x = _ref.x,</span>
<span class="line">      y = _ref.y;</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> [x, y];</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">move(&#123; <span class="attr">x</span>: <span class="number">3</span>, <span class="attr">y</span>: <span class="number">8</span> &#125;); <span class="comment">// [3, 8]</span></span>
<span class="line">move(&#123; <span class="attr">x</span>: <span class="number">3</span> &#125;); <span class="comment">// [3, undefined]</span></span>
<span class="line">move(&#123;&#125;); <span class="comment">// [undefined, undefined]</span></span>
<span class="line">move(); <span class="comment">// [0, 0]</span></span>
</pre></td></tr></table></figure>
<h3 id="han-shu-de-kuo-zhan">函数的扩展</h3>
<p>转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// default parameter values</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log</span>(<span class="params">x, y = <span class="string">'World'</span></span>) </span>&#123;</span>
<span class="line">  <span class="built_in">console</span>.log(x, y);</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">log(<span class="string">'Hello'</span>) <span class="comment">// Hello World</span></span>
<span class="line">log(<span class="string">'Hello'</span>, <span class="string">'China'</span>) <span class="comment">// Hello China</span></span>
<span class="line">log(<span class="string">'Hello'</span>, <span class="string">''</span>) <span class="comment">// Hello</span></span>
<span class="line"></span>
<span class="line"><span class="comment">// rest parameter</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">push</span>(<span class="params">array, ...items</span>) </span>&#123;</span>
<span class="line">  items.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</span>
<span class="line">    array.push(item);</span>
<span class="line">    <span class="built_in">console</span>.log(item);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a = [];</span>
<span class="line">push(a, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span>
<span class="line"></span>
<span class="line"><span class="comment">// arrow function</span></span>
<span class="line"><span class="keyword">var</span> obj = &#123;</span>
<span class="line">    <span class="attr">prop</span>: <span class="number">1</span>,</span>
<span class="line">    <span class="attr">func</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">        <span class="keyword">var</span> _this = <span class="keyword">this</span>;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">var</span> innerFunc = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span>
<span class="line">            <span class="keyword">this</span>.prop = <span class="number">1</span>;</span>
<span class="line">        &#125;;</span>
<span class="line"> </span>
<span class="line">        <span class="keyword">var</span> innerFunc1 = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">            <span class="keyword">this</span>.prop = <span class="number">1</span>;</span>
<span class="line">        &#125;;</span>
<span class="line">    &#125;,</span>
<span class="line"> </span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// default parameter values</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log</span>(<span class="params">x</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> y = <span class="built_in">arguments</span>.length &gt; <span class="number">1</span> &amp;&amp; <span class="built_in">arguments</span>[<span class="number">1</span>] !== <span class="literal">undefined</span> ? <span class="built_in">arguments</span>[<span class="number">1</span>] : <span class="string">'World'</span>;</span>
<span class="line"></span>
<span class="line">    <span class="built_in">console</span>.log(x, y);</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">log(<span class="string">'Hello'</span>); <span class="comment">// Hello World</span></span>
<span class="line">log(<span class="string">'Hello'</span>, <span class="string">'China'</span>); <span class="comment">// Hello China</span></span>
<span class="line">log(<span class="string">'Hello'</span>, <span class="string">''</span>); <span class="comment">// Hello</span></span>
<span class="line"></span>
<span class="line"><span class="comment">// rest parameter</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">push</span>(<span class="params">array</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> _len = <span class="built_in">arguments</span>.length, items = <span class="built_in">Array</span>(_len &gt; <span class="number">1</span> ? _len - <span class="number">1</span> : <span class="number">0</span>), _key = <span class="number">1</span>; _key &lt; _len; _key++) &#123;</span>
<span class="line">        items[_key - <span class="number">1</span>] = <span class="built_in">arguments</span>[_key];</span>
<span class="line">    &#125;</span>
<span class="line"></span>
<span class="line">    items.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">item</span>) </span>&#123;</span>
<span class="line">        array.push(item);</span>
<span class="line">        <span class="built_in">console</span>.log(item);</span>
<span class="line">    &#125;);</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a = [];</span>
<span class="line">push(a, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span>
<span class="line"></span>
<span class="line"><span class="comment">// arrow function</span></span>
<span class="line"><span class="keyword">var</span> obj = &#123;</span>
<span class="line">    <span class="attr">prop</span>: <span class="number">1</span>,</span>
<span class="line">    <span class="attr">func</span>: <span class="function"><span class="keyword">function</span> <span class="title">func</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">        <span class="keyword">var</span> _this2 = <span class="keyword">this</span>;</span>
<span class="line"></span>
<span class="line">        <span class="keyword">var</span> _this = <span class="keyword">this</span>;</span>
<span class="line"></span>
<span class="line">        <span class="keyword">var</span> innerFunc = <span class="function"><span class="keyword">function</span> <span class="title">innerFunc</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">            _this2.prop = <span class="number">1</span>;</span>
<span class="line">        &#125;;</span>
<span class="line"></span>
<span class="line">        <span class="keyword">var</span> innerFunc1 = <span class="function"><span class="keyword">function</span> <span class="title">innerFunc1</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">            <span class="keyword">this</span>.prop = <span class="number">1</span>;</span>
<span class="line">        &#125;;</span>
<span class="line">    &#125;</span>
<span class="line"></span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>这里通过默认参数的转换方式并结合上例中解构赋值函数的例子就看的很明白了，主要使用 arguments 来做处理。</p>
<p>而 rest 参数则同样依靠 arguments 来遍历处理既定参数之后的所有参数。</p>
<p>而箭头函数主要是省了写 function 的代码，同时能够直接用使外层的 this 而不用担心 context 切换的问题。以前我们一般都要在外层多写一个 _this/self 直向 this。babel 的转换方法跟大家平时所了解的基本一致。</p>
<h3 id="dui-xiang-de-kuo-zhan">对象的扩展</h3>
<p>转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> prop2 = <span class="string">"PROP2"</span>;</span>
<span class="line"><span class="keyword">var</span> obj = &#123;</span>
<span class="line">    [<span class="string">'prop'</span>]: <span class="number">1</span>,</span>
<span class="line">    [<span class="string">'func'</span>]: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">        <span class="built_in">console</span>.log(<span class="string">'func'</span>);</span>
<span class="line">    &#125;,</span>
<span class="line">        [prop2]: <span class="number">3</span></span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> obj = &#123;</span>
<span class="line">    toString() &#123;</span>
<span class="line">     <span class="comment">// Super calls</span></span>
<span class="line">     <span class="keyword">return</span> <span class="string">"d "</span> + <span class="keyword">super</span>.toString();</span>
<span class="line">    &#125;,</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
<span class="line">45</span>
<span class="line">46</span>
<span class="line">47</span>
<span class="line">48</span>
<span class="line">49</span>
<span class="line">50</span>
<span class="line">51</span>
<span class="line">52</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> _get = <span class="function"><span class="keyword">function</span> <span class="title">get</span>(<span class="params">object, property, receiver</span>) </span>&#123;</span>
<span class="line">    <span class="comment">// 如果 prototype 为空，则往 Function 的 prototype 上寻找</span></span>
<span class="line">    <span class="keyword">if</span> (object === <span class="literal">null</span>) object = <span class="built_in">Function</span>.prototype;</span>
<span class="line">    <span class="keyword">var</span> desc = <span class="built_in">Object</span>.getOwnPropertyDescriptor(object, property);</span>
<span class="line">    <span class="keyword">if</span> (desc === <span class="literal">undefined</span>) &#123;</span>
<span class="line">        <span class="keyword">var</span> parent = <span class="built_in">Object</span>.getPrototypeOf(object);</span>
<span class="line">        <span class="comment">// 如果在本层 prototype 找不到，再往更深层的 prototype 上找</span></span>
<span class="line">        <span class="keyword">if</span> (parent === <span class="literal">null</span>) &#123;</span>
<span class="line">            <span class="keyword">return</span> <span class="literal">undefined</span>;</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            <span class="keyword">return</span> get(parent, property, receiver);</span>
<span class="line">        &#125;</span>
<span class="line">    <span class="comment">// 如果是属性，则直接返回</span></span>
<span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">"value"</span> <span class="keyword">in</span> desc) &#123;</span>
<span class="line">        <span class="keyword">return</span> desc.value;</span>
<span class="line">    <span class="comment">// 如果是方法，则用 call 来调用，receiver 是调用的对象 </span></span>
<span class="line">    &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">        <span class="keyword">var</span> getter = desc.get;<span class="comment">// getOwnPropertyDescriptor 返回的 getter 方法</span></span>
<span class="line">        <span class="keyword">if</span> (getter === <span class="literal">undefined</span>) &#123;</span>
<span class="line">            <span class="keyword">return</span> <span class="literal">undefined</span>;</span>
<span class="line">        &#125;</span>
<span class="line">        <span class="keyword">return</span> getter.call(receiver);</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _obj, _obj2;</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_defineProperty</span>(<span class="params">obj, key, value</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">if</span> (key <span class="keyword">in</span> obj) &#123;</span>
<span class="line">        <span class="built_in">Object</span>.defineProperty(obj, key, &#123;</span>
<span class="line">            <span class="attr">value</span>: value,</span>
<span class="line">            <span class="attr">enumerable</span>: <span class="literal">true</span>,</span>
<span class="line">            <span class="attr">configurable</span>: <span class="literal">true</span>,</span>
<span class="line">            <span class="attr">writable</span>: <span class="literal">true</span></span>
<span class="line">        &#125;);</span>
<span class="line">    &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">        obj[key] = value;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">return</span> obj;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> prop2 = <span class="string">"PROP2"</span>;</span>
<span class="line"><span class="keyword">var</span> obj = (_obj = &#123;&#125;, _defineProperty(_obj, <span class="string">'prop'</span>, <span class="number">1</span>), _defineProperty(_obj, <span class="string">'func'</span>, <span class="function"><span class="keyword">function</span> <span class="title">func</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    <span class="built_in">console</span>.log(<span class="string">'func'</span>);</span>
<span class="line">&#125;), _defineProperty(_obj, prop2, <span class="number">3</span>), _obj);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> obj = _obj2 = &#123;</span>
<span class="line">    <span class="attr">toString</span>: <span class="function"><span class="keyword">function</span> <span class="title">toString</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">        <span class="comment">// Super calls</span></span>
<span class="line">        <span class="keyword">return</span> <span class="string">"d "</span> + _get(_obj2.__proto__ || <span class="built_in">Object</span>.getPrototypeOf(_obj2), <span class="string">'toString'</span>, <span class="keyword">this</span>).call(<span class="keyword">this</span>);</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>对于对象中用中括号解释属性的功能，babel 新增了一个 <code>_defineProperty</code> 函数，给新建的 <code>_obj = {}</code>进行属性定义。除此之外使用小括号包住一系列从左到右的运算使整个定义更简洁。</p>
<p>对于对象字面量中使用 <code>super</code> 去调用 prototype，babel 通过 <code>_get</code> 方法来在 prototype 链上寻找方法/属性。</p>
<h3 id="zi-fu-chuan-mo-ban">字符串模板</h3>
<p>转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">5</span>;</span>
<span class="line"><span class="keyword">var</span> b = <span class="number">10</span>;</span>
<span class="line"> </span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">tag</span>(<span class="params">strings, ...values</span>) </span>&#123;</span>
<span class="line">  <span class="built_in">console</span>.log(strings[<span class="number">0</span>]); <span class="comment">// "Hello "</span></span>
<span class="line">  <span class="built_in">console</span>.log(strings[<span class="number">1</span>]); <span class="comment">// " world "</span></span>
<span class="line">  <span class="built_in">console</span>.log(values[<span class="number">0</span>]);  <span class="comment">// 15</span></span>
<span class="line">  <span class="built_in">console</span>.log(values[<span class="number">1</span>]);  <span class="comment">// 50</span></span>
<span class="line">&#125;</span>
<span class="line"> </span>
<span class="line">tag<span class="string">`Hello <span class="subst">$&#123; a + b &#125;</span> world <span class="subst">$&#123; a * b &#125;</span>`</span>;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> _templateObject = _taggedTemplateLiteral([<span class="string">"Hello "</span>, <span class="string">" world "</span>, <span class="string">""</span>], [<span class="string">"Hello "</span>, <span class="string">" world "</span>, <span class="string">""</span>]);</span>
<span class="line"></span>
<span class="line"><span class="comment">// _templateObject = ["Hello ", " world ", "" , raw: Array[3]]</span></span>
<span class="line"></span>
<span class="line"><span class="comment">// 给传入的 object 定义了 strings 和 raw 两个不可变的属性。</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_taggedTemplateLiteral</span>(<span class="params">strings, raw</span>) </span>&#123;</span>
<span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.freeze(<span class="built_in">Object</span>.defineProperties(strings, &#123;</span>
<span class="line">    <span class="attr">raw</span>: &#123;</span>
<span class="line">      <span class="attr">value</span>: <span class="built_in">Object</span>.freeze(raw)</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;));</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a = <span class="number">5</span>;</span>
<span class="line"><span class="keyword">var</span> b = <span class="number">10</span>;</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">tag</span>(<span class="params">strings</span>) </span>&#123;</span>
<span class="line">  <span class="comment">// strings = ["Hello ", " world ", "", raw: Array[3]]    arguments = [Array[3], 15, 50]</span></span>
<span class="line">  <span class="built_in">console</span>.log(strings[<span class="number">0</span>]); <span class="comment">// "Hello "</span></span>
<span class="line">  <span class="built_in">console</span>.log(strings[<span class="number">1</span>]); <span class="comment">// " world "</span></span>
<span class="line">  <span class="built_in">console</span>.log(<span class="built_in">arguments</span>.length &lt;= <span class="number">1</span> ? <span class="literal">undefined</span> : <span class="built_in">arguments</span>[<span class="number">1</span>]); <span class="comment">// 15</span></span>
<span class="line">  <span class="built_in">console</span>.log(<span class="built_in">arguments</span>.length &lt;= <span class="number">2</span> ? <span class="literal">undefined</span> : <span class="built_in">arguments</span>[<span class="number">2</span>]); <span class="comment">// 50</span></span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">tag(_templateObject, a + b, a * b);</span>
<span class="line"></span>
<span class="line">es6 的这种新特性给模板处理赋予更强大的功能，一改以往对模板进行各种 replace 的处理办法，用一个统一的 handler 去处理。babel 的转换主要是添加了 <span class="number">2</span> 个属性，通过捕获传参和 <span class="built_in">arguments</span> 变量来获取具体的值。</span>
</pre></td></tr></table></figure>
<h3 id="mo-kuai-hua-yu-lei">模块化与类</h3>
<h4 id="lei-class">类 Class</h4>
<p>js 实现 oo 一直是非常热门的话题。从最原始时代的手动维护构造函数来调用父类构造函数,到后来封装好函数进行 extend 继承，再到 babel 出现之后可以像其它面向对象的语言一样直接写 class。es2015 的类方案仍然算是过渡方案，它所支持的特性仍然没有涵盖类的所有特性。目前主要支持的有：</p>
<ul>
<li>constructor</li>
<li>static 方法</li>
<li>get 方法</li>
<li>set 方法</li>
<li>类继承</li>
<li>super 调用父类方法。</li>
</ul>
<p>转换前：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
</pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Animal</span> </span>&#123;</span>
<span class="line">  <span class="keyword">constructor</span>(name, type) &#123;</span>
<span class="line">    <span class="keyword">this</span>.name = name;</span>
<span class="line">    <span class="keyword">this</span>.type = type;</span>
<span class="line">  &#125;</span>
<span class="line">  walk() &#123;</span>
<span class="line">    <span class="built_in">console</span>.log(<span class="string">'walk'</span>);</span>
<span class="line">  &#125;</span>
<span class="line">  run() &#123;</span>
<span class="line">    <span class="built_in">console</span>.log(<span class="string">'run'</span>)</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">static</span> getType() &#123;</span>
<span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.type;</span>
<span class="line">  &#125;</span>
<span class="line">  get getName() &#123;</span>
<span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.name;</span>
<span class="line">  &#125;</span>
<span class="line">  set setName(name) &#123;</span>
<span class="line">    <span class="keyword">this</span>.name = name;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dog</span> <span class="keyword">extends</span> <span class="title">Animal</span> </span>&#123;</span>
<span class="line">  <span class="keyword">constructor</span>(name, type) &#123;</span>
<span class="line">    <span class="keyword">super</span>(name, type);</span>
<span class="line">  &#125;</span>
<span class="line">  get getName() &#123;</span>
<span class="line">    <span class="keyword">return</span> <span class="keyword">super</span>.getName();</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
<span class="line">45</span>
<span class="line">46</span>
<span class="line">47</span>
<span class="line">48</span>
<span class="line">49</span>
<span class="line">50</span>
<span class="line">51</span>
<span class="line">52</span>
<span class="line">53</span>
<span class="line">54</span>
<span class="line">55</span>
<span class="line">56</span>
<span class="line">57</span>
<span class="line">58</span>
<span class="line">59</span>
<span class="line">60</span>
<span class="line">61</span>
<span class="line">62</span>
<span class="line">63</span>
<span class="line">64</span>
<span class="line">65</span>
<span class="line">66</span>
<span class="line">67</span>
<span class="line">68</span>
<span class="line">69</span>
<span class="line">70</span>
<span class="line">71</span>
<span class="line">72</span>
<span class="line">73</span>
<span class="line">74</span>
<span class="line">75</span>
<span class="line">76</span>
<span class="line">77</span>
<span class="line">78</span>
<span class="line">79</span>
<span class="line">80</span>
<span class="line">81</span>
<span class="line">82</span>
<span class="line">83</span>
<span class="line">84</span>
<span class="line">85</span>
<span class="line">86</span>
<span class="line">87</span>
<span class="line">88</span>
<span class="line">89</span>
<span class="line">90</span>
<span class="line">91</span>
<span class="line">92</span>
<span class="line">93</span>
<span class="line">94</span>
<span class="line">95</span>
<span class="line">96</span>
<span class="line">97</span>
<span class="line">98</span>
<span class="line">99</span>
<span class="line">100</span>
<span class="line">101</span>
<span class="line">102</span>
<span class="line">103</span>
<span class="line">104</span>
<span class="line">105</span>
<span class="line">106</span>
<span class="line">107</span>
<span class="line">108</span>
<span class="line">109</span>
<span class="line">110</span>
<span class="line">111</span>
<span class="line">112</span>
<span class="line">113</span>
<span class="line">114</span>
<span class="line">115</span>
<span class="line">116</span>
<span class="line">117</span>
<span class="line">118</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// 与上同</span></span>
<span class="line"><span class="keyword">var</span> _get = <span class="function"><span class="keyword">function</span> <span class="title">get</span>(<span class="params">object, property, receiver</span>) </span>&#123; <span class="keyword">if</span> (object === <span class="literal">null</span>) object = <span class="built_in">Function</span>.prototype; <span class="keyword">var</span> desc = <span class="built_in">Object</span>.getOwnPropertyDescriptor(object, property); <span class="keyword">if</span> (desc === <span class="literal">undefined</span>) &#123; <span class="keyword">var</span> parent = <span class="built_in">Object</span>.getPrototypeOf(object); <span class="keyword">if</span> (parent === <span class="literal">null</span>) &#123; <span class="keyword">return</span> <span class="literal">undefined</span>; &#125; <span class="keyword">else</span> &#123; <span class="keyword">return</span> get(parent, property, receiver); &#125; &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">"value"</span> <span class="keyword">in</span> desc) &#123; <span class="keyword">return</span> desc.value; &#125; <span class="keyword">else</span> &#123; <span class="keyword">var</span> getter = desc.get; <span class="keyword">if</span> (getter === <span class="literal">undefined</span>) &#123; <span class="keyword">return</span> <span class="literal">undefined</span>; &#125; <span class="keyword">return</span> getter.call(receiver); &#125; &#125;;</span>
<span class="line"></span>
<span class="line"><span class="comment">// _creatClass 用于创建类及其对应的方法</span></span>
<span class="line"><span class="keyword">var</span> _createClass = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">defineProperties</span>(<span class="params">target, props</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; props.length; i++) &#123;</span>
<span class="line">      <span class="keyword">var</span> descriptor = props[i];</span>
<span class="line">      <span class="comment">// es6 规范要求类方法为 non-enumerable</span></span>
<span class="line">      descriptor.enumerable = descriptor.enumerable || <span class="literal">false</span>;</span>
<span class="line">      descriptor.configurable = <span class="literal">true</span>;</span>
<span class="line">      <span class="comment">// 对于 setter 和 getter 方法，writable 为 false</span></span>
<span class="line">      <span class="keyword">if</span> (<span class="string">"value"</span> <span class="keyword">in</span> descriptor) descriptor.writable = <span class="literal">true</span>;</span>
<span class="line">      <span class="built_in">Object</span>.defineProperty(target, descriptor.key, descriptor);</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">Constructor, protoProps, staticProps</span>) </span>&#123;</span>
<span class="line">    <span class="comment">// 非静态方法定义在原型链上</span></span>
<span class="line">    <span class="keyword">if</span> (protoProps) defineProperties(Constructor.prototype, protoProps);</span>
<span class="line">    <span class="comment">// 静态方法直接定义在 constructor 函数上</span></span>
<span class="line">    <span class="keyword">if</span> (staticProps) defineProperties(Constructor, staticProps);</span>
<span class="line">    <span class="keyword">return</span> Constructor;</span>
<span class="line">  &#125;;</span>
<span class="line">&#125;();</span>
<span class="line"></span>
<span class="line"><span class="comment">// 子类实现 constructor</span></span>
<span class="line"><span class="comment">// babel 会强制子类在 constructor 中使用 super，否则编译会报错</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_possibleConstructorReturn</span>(<span class="params">self, call</span>) </span>&#123;</span>
<span class="line">  <span class="keyword">if</span> (!self) &#123;</span>
<span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">ReferenceError</span>(<span class="string">"this hasn't been initialised - super() hasn't been called"</span>);</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="comment">// 若call是函数/对象则返回</span></span>
<span class="line">  <span class="keyword">return</span> call &amp;&amp; (<span class="keyword">typeof</span> call === <span class="string">"object"</span> || <span class="keyword">typeof</span> call === <span class="string">"function"</span>) ? call : self;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="comment">// 子类继承父类</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_inherits</span>(<span class="params">subClass, superClass</span>) </span>&#123;</span>
<span class="line">  <span class="comment">// 父类一定要是 function 类型</span></span>
<span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> superClass !== <span class="string">"function"</span> &amp;&amp; superClass !== <span class="literal">null</span>) &#123;</span>
<span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"Super expression must either be null or a function, not "</span> + <span class="keyword">typeof</span> superClass);</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="comment">// 使原型链 subClass.prototype.__proto__ 指向父类 superClass，同时保证 constructor 是 subClass 自己</span></span>
<span class="line">  subClass.prototype = <span class="built_in">Object</span>.create(superClass &amp;&amp; superClass.prototype, &#123;</span>
<span class="line">    <span class="attr">constructor</span>: &#123;</span>
<span class="line">      <span class="attr">value</span>: subClass,</span>
<span class="line">      <span class="attr">enumerable</span>: <span class="literal">false</span>,</span>
<span class="line">      <span class="attr">writable</span>: <span class="literal">true</span>,</span>
<span class="line">      <span class="attr">configurable</span>: <span class="literal">true</span></span>
<span class="line">    &#125;</span>
<span class="line">  &#125;);</span>
<span class="line">  <span class="comment">// 保证 subClass.__proto__ 指向父类 superClass</span></span>
<span class="line">  <span class="keyword">if</span> (superClass) <span class="built_in">Object</span>.setPrototypeOf ? <span class="built_in">Object</span>.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="comment">// 检测 constructor 正确与否</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_classCallCheck</span>(<span class="params">instance, Constructor</span>) </span>&#123;</span>
<span class="line">  <span class="keyword">if</span> (!(instance <span class="keyword">instanceof</span> Constructor)) &#123;</span>
<span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"Cannot call a class as a function"</span>);</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> Animal = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Animal</span>(<span class="params">name, type</span>) </span>&#123;</span>
<span class="line">    <span class="comment">// 此处是 constructor 的实现</span></span>
<span class="line">    _classCallCheck(<span class="keyword">this</span>, Animal);</span>
<span class="line"></span>
<span class="line">    <span class="keyword">this</span>.name = name;</span>
<span class="line">    <span class="keyword">this</span>.type = type;</span>
<span class="line">  &#125;</span>
<span class="line"></span>
<span class="line">  _createClass(Animal, [&#123;</span>
<span class="line">    <span class="attr">key</span>: <span class="string">'walk'</span>,</span>
<span class="line">    <span class="attr">value</span>: <span class="function"><span class="keyword">function</span> <span class="title">walk</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">      <span class="built_in">console</span>.log(<span class="string">'walk'</span>);</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;, &#123;</span>
<span class="line">    <span class="attr">key</span>: <span class="string">'run'</span>,</span>
<span class="line">    <span class="attr">value</span>: <span class="function"><span class="keyword">function</span> <span class="title">run</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">      <span class="built_in">console</span>.log(<span class="string">'run'</span>);</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;, &#123;</span>
<span class="line">    <span class="attr">key</span>: <span class="string">'getName'</span>,</span>
<span class="line">    <span class="attr">get</span>: <span class="function"><span class="keyword">function</span> <span class="title">get</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.name;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;, &#123;</span>
<span class="line">    <span class="attr">key</span>: <span class="string">'setName'</span>,</span>
<span class="line">    <span class="attr">set</span>: <span class="function"><span class="keyword">function</span> <span class="title">set</span>(<span class="params">name</span>) </span>&#123;</span>
<span class="line">      <span class="keyword">this</span>.name = name;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;], [&#123;</span>
<span class="line">    <span class="attr">key</span>: <span class="string">'getType'</span>,</span>
<span class="line">    <span class="attr">value</span>: <span class="function"><span class="keyword">function</span> <span class="title">getType</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.type;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;]);</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> Animal;</span>
<span class="line">&#125;();</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> Dog = <span class="function"><span class="keyword">function</span> (<span class="params">_Animal</span>) </span>&#123;</span>
<span class="line">  _inherits(Dog, _Animal);</span>
<span class="line"></span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Dog</span>(<span class="params">name, type</span>) </span>&#123;</span>
<span class="line">    _classCallCheck(<span class="keyword">this</span>, Dog);</span>
<span class="line"></span>
<span class="line">    <span class="keyword">return</span> _possibleConstructorReturn(<span class="keyword">this</span>, (Dog.__proto__ || <span class="built_in">Object</span>.getPrototypeOf(Dog)).call(<span class="keyword">this</span>, name, type));</span>
<span class="line">  &#125;</span>
<span class="line"></span>
<span class="line">  _createClass(Dog, [&#123;</span>
<span class="line">    <span class="attr">key</span>: <span class="string">'getName'</span>,</span>
<span class="line">    <span class="attr">get</span>: <span class="function"><span class="keyword">function</span> <span class="title">get</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">      <span class="keyword">return</span> _get(Dog.prototype.__proto__ || <span class="built_in">Object</span>.getPrototypeOf(Dog.prototype), <span class="string">'getName'</span>, <span class="keyword">this</span>).call(<span class="keyword">this</span>);</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;]);</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> Dog;</span>
<span class="line">&#125;(Animal);</span>
</pre></td></tr></table></figure>
<h4 id="mo-kuai-hua">模块化</h4>
<p>示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// module.js</span></span>
<span class="line"><span class="keyword">import</span> &#123; Animal <span class="keyword">as</span> Ani, catwalk &#125; <span class="keyword">from</span> <span class="string">"./t1"</span>;</span>
<span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> All <span class="keyword">from</span> <span class="string">"./t2"</span>;</span>
<span class="line"> </span>
<span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Cat</span> <span class="keyword">extends</span> <span class="title">Ani</span> </span>&#123;</span>
<span class="line">  <span class="keyword">constructor</span>() &#123;</span>
<span class="line">    <span class="keyword">super</span>();</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
<span class="line"> </span>
<span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dog</span> <span class="keyword">extends</span> <span class="title">Ani</span> </span>&#123;</span>
<span class="line">  <span class="keyword">constructor</span>() &#123;</span>
<span class="line">    <span class="keyword">super</span>();</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// t1.js</span></span>
<span class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">Animal</span> </span>&#123;</span>
<span class="line">  <span class="keyword">constructor</span>() &#123;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
<span class="line"> </span>
<span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">catwal</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="built_in">console</span>.log(<span class="string">'cat walk'</span>);</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// t2.js</span></span>
<span class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span>
<span class="line">  <span class="keyword">constructor</span>() &#123;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
<span class="line"> </span>
<span class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">Plane</span> </span>&#123;</span>
<span class="line">  <span class="keyword">constructor</span>() &#123;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>转换后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
<span class="line">45</span>
<span class="line">46</span>
<span class="line">47</span>
<span class="line">48</span>
<span class="line">49</span>
<span class="line">50</span>
<span class="line">51</span>
<span class="line">52</span>
<span class="line">53</span>
<span class="line">54</span>
<span class="line">55</span>
<span class="line">56</span>
<span class="line">57</span>
<span class="line">58</span>
<span class="line">59</span>
<span class="line">60</span>
<span class="line">61</span>
<span class="line">62</span>
<span class="line">63</span>
<span class="line">64</span>
<span class="line">65</span>
<span class="line">66</span>
<span class="line">67</span>
<span class="line">68</span>
<span class="line">69</span>
<span class="line">70</span>
<span class="line">71</span>
<span class="line">72</span>
<span class="line">73</span>
<span class="line">74</span>
<span class="line">75</span>
<span class="line">76</span>
<span class="line">77</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// t1.js 模块</span></span>
<span class="line"></span>
<span class="line"><span class="built_in">Object</span>.defineProperty(exports, <span class="string">"__esModule"</span>, &#123;</span>
<span class="line">  <span class="attr">value</span>: <span class="literal">true</span></span>
<span class="line">&#125;);</span>
<span class="line">exports.catwal = catwal;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> Animal = exports.Animal = <span class="function"><span class="keyword">function</span> <span class="title">Animal</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  _classCallCheck(<span class="keyword">this</span>, Animal);</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">catwal</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="built_in">console</span>.log(<span class="string">'cat walk'</span>);</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="comment">// t2.js 模块</span></span>
<span class="line"></span>
<span class="line"><span class="built_in">Object</span>.defineProperty(exports, <span class="string">"__esModule"</span>, &#123;</span>
<span class="line">  <span class="attr">value</span>: <span class="literal">true</span></span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> Person = exports.Person = <span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  _classCallCheck(<span class="keyword">this</span>, Person);</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> Plane = exports.Plane = <span class="function"><span class="keyword">function</span> <span class="title">Plane</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  _classCallCheck(<span class="keyword">this</span>, Plane);</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="comment">// module.js</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _t = <span class="built_in">require</span>(<span class="string">"./t1"</span>);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> _t2 = <span class="built_in">require</span>(<span class="string">"./t2"</span>); <span class="comment">// 返回的都是exports上返回的对象属性</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> All = _interopRequireWildcard(_t2);</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_interopRequireWildcard</span>(<span class="params">obj</span>) </span>&#123;</span>
<span class="line">  <span class="comment">// 发现是babel编译的， 直接返回</span></span>
<span class="line">  <span class="keyword">if</span> (obj &amp;&amp; obj.__esModule) &#123;</span>
<span class="line">    <span class="keyword">return</span> obj;</span>
<span class="line">  <span class="comment">// 非 babel 编译， 猜测可能是第三方模块，为了不报错，让 default 指向它自己</span></span>
<span class="line">  &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">    <span class="keyword">var</span> newObj = &#123;&#125;;</span>
<span class="line">    <span class="keyword">if</span> (obj != <span class="literal">null</span>) &#123;</span>
<span class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> obj) &#123;</span>
<span class="line">        <span class="keyword">if</span> (<span class="built_in">Object</span>.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    newObj.default = obj;</span>
<span class="line">    <span class="keyword">return</span> newObj;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> Cat = <span class="function"><span class="keyword">function</span> (<span class="params">_Ani</span>) </span>&#123;</span>
<span class="line">  _inherits(Cat, _Ani);</span>
<span class="line"></span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Cat</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    _classCallCheck(<span class="keyword">this</span>, Cat);</span>
<span class="line"></span>
<span class="line">    <span class="keyword">return</span> _possibleConstructorReturn(<span class="keyword">this</span>, (Cat.__proto__ || <span class="built_in">Object</span>.getPrototypeOf(Cat)).call(<span class="keyword">this</span>));</span>
<span class="line">  &#125;</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> Cat;</span>
<span class="line">&#125;(_t.Animal);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> Dog = <span class="function"><span class="keyword">function</span> (<span class="params">_Ani2</span>) </span>&#123;</span>
<span class="line">  _inherits(Dog, _Ani2);</span>
<span class="line"></span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Dog</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">    _classCallCheck(<span class="keyword">this</span>, Dog);</span>
<span class="line"></span>
<span class="line">    <span class="keyword">return</span> _possibleConstructorReturn(<span class="keyword">this</span>, (Dog.__proto__ || <span class="built_in">Object</span>.getPrototypeOf(Dog)).call(<span class="keyword">this</span>));</span>
<span class="line">  &#125;</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> Dog;</span>
<span class="line">&#125;(_t.Animal);</span>
</pre></td></tr></table></figure>
<p>es6 的模块加载是属于多对象多加载，而 commonjs 则属于单对象单加载。babel 需要做一些手脚才能将 es6 的模块写法写成 commonjs 的写法。主要是通过定义 <code>__esModule</code> 这个属性来判断这个模块是否经过 babel 的编译。然后通过 <code>_interopRequireWildcard</code> 对各个模块的引用进行相应的处理。</p>
<h2 id="can-kao-zi-liao">参考资料</h2>
<p><a href="http://taobaofed.org/blog/2016/09/29/babel-plugins/" target="_blank" rel="external">理解 Babel 插件</a></p>
<p><a href="https://github.com/lcxfs1991/blog/issues/9" target="_blank" rel="external">babel到底将代码转换成什么鸟样？</a></p>
<p><a href="https://github.com/thejameskyle/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md" target="_blank" rel="external">Babel 插件手册</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文基于 &lt;code&gt;babel-preset-es2015 : 6.18.0&lt;/code&gt; ，&lt;code&gt;babel-core : 6.21.0&lt;/code&gt;版本进行。&lt;/p&gt;
&lt;h2 id=&quot;ji-chu-zhi-shi&quot;&gt;基础知识&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;h
    
    </summary>
    
      <category term="JavaScript" scheme="http://qcyoung.com/categories/JavaScript/"/>
    
    
      <category term="Babel" scheme="http://qcyoung.com/tags/Babel/"/>
    
      <category term="ES6" scheme="http://qcyoung.com/tags/ES6/"/>
    
      <category term="JavaScript" scheme="http://qcyoung.com/tags/JavaScript/"/>
    
      <category term="编译" scheme="http://qcyoung.com/tags/%E7%BC%96%E8%AF%91/"/>
    
  </entry>
  
  <entry>
    <title>我的 2016 总结暨 2017 新年祈愿</title>
    <link href="http://qcyoung.com/2017/01/02/%E6%88%91%E7%9A%84%202016%20%E6%80%BB%E7%BB%93%E6%9A%A8%202017%20%E6%96%B0%E5%B9%B4%E7%A5%88%E6%84%BF/"/>
    <id>http://qcyoung.com/2017/01/02/我的 2016 总结暨 2017 新年祈愿/</id>
    <published>2017-01-02T13:48:25.000Z</published>
    <updated>2017-01-02T13:49:59.000Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://ww4.sinaimg.cn/large/006y8lVajw1fb6bof5tjej31hc0u0tf0.jpg" alt=""></p>
<p>这一年发生了不少的事，但在抬笔时却又不知从何处细谈起。这一年让我对很多事有了更深的认识。下面还是按简单的分类简谈一下吧。</p>
<h2 id="gong-zuo">工作</h2>
<p>这一年是我司工作环境变化巨大的一年。总体上来讲公司依然是在较好的发展，公司搬了家，让办公环境有了很大的改善。整体技术团队也扩张了许多。可以说在硬件环境方面，这一年还是提升了许多的。</p>
<p>然而从个人角度而言，硬件环境改善的背面却是入职以来所认同的好几名前辈和同事的相继离职。尤其是所在的商城组，可以说是流水的老大 + 组员。尽管目前所在的前端组的小气候相比来说还算稳定。但是由于大气候的原因，导致在今年的一段时间内还是很难保持一个稳定的开发环境。。</p>
<p>此外随着公司的发展，在我看来公司的环境和公司文化间也存在了一定的撕裂。不少公司都或多或少存在上面的撕裂现象，例如今年<a href="https://zh.wikipedia.org/zh-hans/%E9%AD%8F%E5%88%99%E8%A5%BF%E4%BA%8B%E4%BB%B6" target="_blank" rel="external">百度的魏则西事件</a>，<a href="https://www.zhihu.com/question/52987944" target="_blank" rel="external">阿里的校园日记事件</a>等等。这些事态的发展肯定是与公司所宣扬、认同的文化、价值观相悖的。但最后是如何执行成这样的？多少都是因为这样的撕裂导致的。此外，这一年里对上下级管理、加班文化、公司人文关怀等也有了更多深刻的看法。这一年里真的需要对公司里负责的同事，老大报以十分的感谢。</p>
<p>这一年里在商城我们从年初用 Vue 开始重构，到目前年底总算将商城主流程的大多数页面改造成了由 Vue 全家桶开发的单页面应用（期间遇到的各种业务更替、拆分和插入需求而影响的进度不表，但这里并不是说公司不重视重构，这里的确存在着公司成立尚 2 年的客观现实，可以说相比其他主流电商，我们的商城的功能和复杂度还远远不够。商城很多新的需求的优先级确实要比重构要高许多，急需增加的很多功能对解决商户的需求是很重要的，所以技术债的解决也只能是随着需求的变化来适时的同步推进。）</p>
<p>此外参与了许多公司项目的开发，个人觉得印象比较深或是说有难度、特色的技术项目包括：</p>
<ul>
<li>商城 Vue 全家桶重构(Vue + Webpack + ES6)</li>
<li>商城数据采集与埋点代码搭建</li>
<li>CDN 资源部署方案(Koa + Mongodb + Vue)等。</li>
</ul>
<p>业务需求项目主要包括：</p>
<ul>
<li>商城抽奖系统(砸金蛋 + 刮刮卡 + 大转盘..)</li>
<li>商城 CMS</li>
<li>会员系统及会员特卖</li>
<li>感恩节 H5 (D3 + jq)</li>
<li>采购工具等项目。</li>
</ul>
<p>在这些项目中除了熟悉应用相关框架、库的方法外，在代码之外还深刻认识到了如下道理，简要列举如下：</p>
<ol>
<li>在 <a href="https://book.douban.com/subject/1152111/" target="_blank" rel="external">程序员修炼之道</a> 一书中对代码重构方法的基本原则提炼：</li>
</ol>
<blockquote>
<p>重构是一项需要慎重、深思熟虑、小心进行的活动。关于怎样进行利大于弊的重构， Martin Fowler 给出了以下提示。</p>
<ol>
<li>不要试图在重构的同时增加大量工能。</li>
<li>在开始重构之前，确保你拥有良好的测试。</li>
<li>对重构任务尽可能的划分为短小、深思熟虑的步骤方案。重构常常涉及到许多局部的改动，在代码复杂度达到一定程度时，这样的改动可能产生很大的影响。如果你的重构步骤能够保持短小，并且每次改动都有良好的测试方案，你将能够避免长时间的调试和隐含的巨大错误。</li>
</ol>
</blockquote>
<p>此外还包括重构项目实施的若干方法，在针对不同用户群或项目情况时，重构可以采取不同的方案，来尽可能的做到科学并达到最大化的项目效果。一般公司都是由于技术债的原因导致代码需要重构来保证代码的质量、稳定、功能拓展性等。这里还可以结合我今年 11 月的总结博文里推荐的两篇讨论技术债的文章来更加深入的了解相关的内容(<a href="http://www.qcyoung.com/2016/12/05/2016.11%20-%20Do%20whatever%20you%20do%20intensely/" target="_blank" rel="external">链接</a>)</p>
<ol start="2">
<li>
<p>在职位分工明确的大环境下，工作压力不小的情况中，也要时刻警醒自己的工作计划状态。当你过多的被动工作生活时，就是你需要停下来思考一下的时候了。这里可以参考阮一峰的这篇博文<a href="http://www.ruanyifeng.com/blog/2016/06/your-destiny-is-not-like-a-mule.html" target="_blank" rel="external">你的命运不是一头骡子</a> 、</p>
</li>
<li>
<p>这一年的 Alpha Go 、无人驾驶等都展示了深度学习下的计算机应用的加速推进。在愈演愈烈的 AI 浪潮和职业环境背景下，个人更需不断提升自己的核心竞争力来提高自身工作的价值。这就如工厂制造业机械化的演进一样，相信在未来十几年各行各业都将面临新一步的劳动力革命。</p>
</li>
<li>
<p>做事的时候经常换一个角度想想，会有更多更深刻更有意思的发现。方法可以参考这篇博文<a href="https://mp.weixin.qq.com/s?__biz=MjM5MzE3MDQ3Mw==&amp;mid=402884722&amp;idx=1&amp;sn=f043571eb59b31addba833059c0cf777" target="_blank" rel="external">做卧底，如何不动声色的毁掉对手的产品</a></p>
</li>
<li>
<p>正常加班的原则也应当是救急不救穷。</p>
</li>
</ol>
<h2 id="ge-ren">个人</h2>
<ul>
<li>这一年简单的做了几个完整的个人项目:</li>
</ul>
<ol>
<li>
<p>对个人网站 <a href="http://qcyoung.com">qcyoung.com</a> 的<a href="https://github.com/yangzj1992/TKL-REVISION" target="_blank" rel="external">主题</a>进行完善、增加了新的功能和样式。</p>
</li>
<li>
<p>一个简单的基于网易云音乐 API 的在线音乐播放器 <a href="https://github.com/yangzj1992/yPlayer" target="_blank" rel="external">yPlayer</a></p>
</li>
<li>
<p>一个基于 Koa 的个人题库系统 <a href="https://github.com/yangzj1992/koa-test-middleware" target="_blank" rel="external">Koa Test</a></p>
</li>
<li>
<p>一个基于总结有趣题目 &amp; 面试题 &amp; 算法、数据结构等基础的库<a href="https://github.com/yangzj1992/FE-Questions" target="_blank" rel="external">FE-Questions</a></p>
</li>
</ol>
<ul>
<li>在开源项目和社区中参与了一些贡献。</li>
</ul>
<p>在<a href="https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8" target="_blank" rel="external">掘金翻译计划</a>、<a href="http://zcfy.cc/@yangzj1992/article" target="_blank" rel="external">众成翻译</a>参与了十多篇文章的翻译和校对。参与了 <a href="http://cn.vuejs.org/about/" target="_blank" rel="external">Vue2</a> 的中文文档翻译校对等。</p>
<h2 id="qi-ta">其他</h2>
<p>这一年坚持了扇贝打卡的习惯，没有缺勤一天(<code>Unbelievable!</code>)。今日办了新的扇贝新年打卡计划。希望能继续保持。</p>
<p>然而今年的 Keep 和吉他计划却夭折了。。这里十分不满意。。毕竟也是真想有生之年有个好身<s>材</s>体和会一门拿的出手的乐器。</p>
<h2 id="ge-ren-zong-jie">个人总结</h2>
<p>按照传统，后面推荐下今年的一些内容</p>
<p><a href="http://music.163.com/#/song?id=26118113" target="_blank" rel="external">All Alone With You - EGOIST</a></p>
<p><a href="http://music.163.com/#/song?id=28018269" target="_blank" rel="external">Daisy - STEREO DIVE FOUNDATION</a></p>
<p><a href="http://music.163.com/#/song?id=26608941" target="_blank" rel="external">空も飛べるはず - 高杉さと美</a></p>
<p><a href="http://music.163.com/#/song?id=357421" target="_blank" rel="external">花 - 花儿</a></p>
<p><a href="http://music.163.com/#/song?id=357424" target="_blank" rel="external">静止 - 花儿</a></p>
<p><a href="http://music.163.com/#/song?id=29785472" target="_blank" rel="external">シルシ - LiSA</a></p>
<p><a href="http://music.163.com/#/song?id=28949422" target="_blank" rel="external">Startear - 春奈るな</a></p>
<p><a href="http://music.163.com/#/song?id=409872504" target="_blank" rel="external">ninelie - Aimer / EGOIST</a></p>
<p><a href="http://music.163.com/#/song?id=426502173" target="_blank" rel="external">Stay Alive - 高橋李依</a></p>
<p><a href="http://music.163.com/#/song?id=426881480" target="_blank" rel="external">夢灯籠 - RADWIMPS</a></p>
<p>今年很喜欢的几本书：</p>
<p><a href="https://book.douban.com/subject/4262627/" target="_blank" rel="external">重构</a>：通过多样的重构方法，来达到合适的设计模式。分辨那些具有 Bad smell 的代码，提高自己的代码质量。</p>
<p><a href="https://book.douban.com/subject/1477390/" target="_blank" rel="external">代码大全</a>：参与工作进行更多实践之后看这本书，更是大有所悟。很多经典的准则都是数代人的经历所总结的经验。</p>
<p><a href="https://book.douban.com/subject/1152111/" target="_blank" rel="external">程序员修炼之道</a>：讲的很泛，但每个方向都有一些收获。</p>
<p><a href="https://book.douban.com/subject/26290085/" target="_blank" rel="external">手把手教你读财报</a>： 讲解十分落地，面向国内市场。很适合入门级。</p>
<p><a href="https://book.douban.com/subject/21323941/" target="_blank" rel="external">具体数学</a>：很多习题，配合英文版的书籍还能参看翻译。了解并温固了很多数学技巧。</p>
<p><a href="https://book.douban.com/subject/25739928/" target="_blank" rel="external">累</a>：分镜很不错，目前关注无奈而扭曲的女主如何收场。</p>
<p><a href="https://book.douban.com/subject/26294929/" target="_blank" rel="external">GrandBlue</a>：这真的是一本以青春、潜水为主题的故事。</p>
<p><a href="https://book.douban.com/subject/25799687/" target="_blank" rel="external">亞人</a>：漫画的剧情，分镜相当高水准，人物性格塑造的也很优秀。赞</p>
<p><a href="https://book.douban.com/subject/5906052/" target="_blank" rel="external">進擊的巨人</a>： 16 年里剧情又掀起了最后的高潮，目前来看漫画剧情应该也快要完结了，第二季动画在 17 年也将上映（虽然应该会被墙）。结局应该是像《大剑》那样结束，但希望最后还能有新的别出心裁。</p>
<h2 id="2017-de-xi-wang">2017 的希望</h2>
<ol>
<li>继续学习更多知识，提升自己的实力，争取经济上早日更自由。。</li>
<li>年年说要努力减肥,今年自然还是不例外。。（年年到最后依然保持 80kg 左右的身材..这句话真是有毒！！！）</li>
<li>和女朋友继续友好相处，在她读完研后争取一起生活。</li>
<li>家人朋友生活平安幸福。</li>
</ol>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://ww4.sinaimg.cn/large/006y8lVajw1fb6bof5tjej31hc0u0tf0.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;这一年发生了不少的事，但在抬笔时却又不知从何处细谈起。这一年让我对很多事有了更深的认识。下
    
    </summary>
    
      <category term="我的生活" scheme="http://qcyoung.com/categories/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="我的生活" scheme="http://qcyoung.com/tags/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript 排序算法汇总</title>
    <link href="http://qcyoung.com/2016/12/18/JavaScript%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB-NSConflict-yangzj1992/"/>
    <id>http://qcyoung.com/2016/12/18/JavaScript 排序算法汇总-NSConflict-yangzj1992/</id>
    <published>2016-12-18T10:40:02.000Z</published>
    <updated>2018-11-25T09:09:09.201Z</updated>
    
    <content type="html"><![CDATA[<h2 id="qian-yan">前言</h2>
<p>关于排序算法的有关文章已经很多了，然而网络上用 Javascript 语言来作为示例并详实介绍的文章貌似还是不太多。这里主要是我来尝试自己针对网上各式的排序算法进行一份详实的个人总结，从而温故知新。</p>
<h2 id="ji-ben-gai-nian">基本概念</h2>
<h3 id="suan-fa-you-lie-ping-jie-zhu-yu">算法优劣评价术语</h3>
<h4 id="wen-ding-xing">稳定性</h4>
<ul>
<li>稳定：如果 <code>a</code> 原本在 <code>b</code> 前面，而 <code>a = b</code>，排序之后 <code>a</code> 仍然在 <code>b</code> 的前面；</li>
<li>不稳定：如果 <code>a</code> 原本在 <code>b</code> 的前面，而 <code>a = b</code>，排序之后 <code>a</code> 可能会出现在 <code>b</code> 的后面；</li>
</ul>
<h4 id="pai-xu-fang-shi">排序方式</h4>
<ul>
<li>内排序：所有排序操作都在内存中完成，占用常数内存，不占用额外内存。</li>
<li>外排序：由于数据太大，因此把数据放在磁盘中，而排序通过磁盘和内存的数据传输才能进行，占用额外内存。</li>
</ul>
<h4 id="fu-za-du">复杂度</h4>
<ul>
<li>时间复杂度: 一个算法执行所耗费的时间。</li>
<li>空间复杂度: 运行完一个程序所需内存的大小。</li>
</ul>
<h4 id="pai-xu-suan-fa-tu-pian-zong-jie">排序算法图片总结</h4>
<p>名词解释：
<code>n</code> : 数据规模
<code>k</code> : 桶的个数</p>
<table>
<thead>
<tr>
<th style="text-align:left">排序算法</th>
<th style="text-align:center">平均时间复杂度</th>
<th style="text-align:center">最好情况</th>
<th style="text-align:center">最坏情况</th>
<th style="text-align:center">空间复杂度</th>
<th style="text-align:center">排序方式</th>
<th style="text-align:center">稳定性</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">冒泡排序</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">选择排序</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">插入排序</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">希尔排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n(\log_2^2 n))$</td>
<td style="text-align:center">$O(n(\log_2^2 n))$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">归并排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">快速排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(\log n)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">堆排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">计数排序</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(k)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">桶排序</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">基数排序</td>
<td style="text-align:center">$O(n × k)$</td>
<td style="text-align:center">$O(n × k)$</td>
<td style="text-align:center">$O(n × k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
</tbody>
</table>
<p>从分类上来讲：</p>
<table>
    <tr>
        <td rowspan="5">排序算法</td>
        <td><b>交换排序</b></td>
        <td>冒泡排序</td>
        <td>快速排序</td>
    </tr>
    <tr>
        <td><b>选择排序</b></td>
        <td>选择排序</td>
        <td>堆排序</td>
    </tr>
    <tr>
        <td><b>插入排序</b></td>
        <td>插入排序</td>
        <td>希尔排序</td>
    </tr>
    <tr>
        <td><b>归并排序</b></td>
        <td>归并排序</td>
    </tr>
    <tr>
        <td><b>分布排序</b></td>
        <td>计数排序</td>
        <td>桶排序</td>
        <td>基数排序</td>
    </tr>
</table>
<h2 id="mou-pao-pai-xu-bubble-sort-sup-class-footnote-ref-a-href-fn-1-id-fnref-1-1-a-sup">冒泡排序（Bubble Sort）<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></h2>
<h3 id="suan-fa-yuan-li">算法原理</h3>
<blockquote>
<p>冒泡排序（Bubble Sort）是一种简单的排序算法。它重复地走访要排序的数列，一次比较两个元素，如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换时，此时该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。</p>
</blockquote>
<p>图解如下:</p>
<p><img src="http://img.blog.csdn.net/20160916160748389" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>比较相邻的元素。如果第一个比第二个大，就交换它们两个；</li>
<li>对每一对相邻元素作同样的工作，从开始第一对到结尾的最后一对，这样在最后的元素应该会是最大的数；</li>
<li>针对所有的元素重复以上的步骤，除了最后一个；</li>
<li>重复步骤 $1~3$，直到排序完成。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> bubbleSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> len = arr.length;</span>
<span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>;i &lt; len;i++)&#123;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = <span class="number">0</span>;j &lt; len - <span class="number">1</span> - i;j++)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[j] &gt; arr[j+<span class="number">1</span>])&#123;</span>
<span class="line">        <span class="keyword">let</span> temp = arr[j+<span class="number">1</span>];</span>
<span class="line">        arr[j+<span class="number">1</span>] = arr[j]</span>
<span class="line">        arr[j] = temp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> arr = [<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.log(bubbleSort(arr)); <span class="comment">//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]</span></span>
</pre></td></tr></table></figure>
<blockquote>
<p>改进冒泡排序： 我们设置一个标志性变量 <code>pos</code>，用于记录每趟排序中最后一次进行交换的位置。由于 <code>pos</code> 位置之后的元素均已交换到位，故在进行下一趟排序时只要扫描到 <code>pos</code> 位置即可。 这样的优化方式可以在最好情况下把复杂度降到 $O(n)$。</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> bubbleSort2 = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> i = arr.length - <span class="number">1</span>;</span>
<span class="line">  <span class="keyword">while</span>(i &gt; <span class="number">0</span>)&#123;</span>
<span class="line">    <span class="keyword">let</span> pos = <span class="number">0</span>;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; i; j++)&#123;</span>
<span class="line">      <span class="keyword">if</span> (arr[j] &gt; arr[j+<span class="number">1</span>])&#123;</span>
<span class="line">        pos = j; <span class="comment">//记录交换的位置</span></span>
<span class="line">        <span class="keyword">let</span> tmp = arr[j];</span>
<span class="line">        arr[j] = arr[j+<span class="number">1</span>];</span>
<span class="line">        arr[j+<span class="number">1</span>] = tmp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    i = pos; <span class="comment">//为下一趟排序作准备</span></span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<blockquote>
<p>另外传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值，我们可以考虑利用在每趟排序中进行正向和反向两遍冒泡的方法来一次得到两个最终值(最大者和最小者)，从而继续优化使排序趟数几乎减少一半。（这就是<a href="https://zh.wikipedia.org/wiki/%E9%B8%A1%E5%B0%BE%E9%85%92%E6%8E%92%E5%BA%8F" target="_blank" rel="external">鸡尾酒排序</a>）</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cooktailSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> min = <span class="number">0</span>;</span>
<span class="line">  <span class="keyword">let</span> max = arr.length - <span class="number">1</span>;</span>
<span class="line">  <span class="keyword">while</span>(min &lt; max)&#123;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = min;j &lt; max;j++)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[j] &gt; arr[j+<span class="number">1</span>])&#123;</span>
<span class="line">        <span class="keyword">let</span> tmp = arr[j];</span>
<span class="line">        arr[j] = arr[j+<span class="number">1</span>];</span>
<span class="line">        arr[j+<span class="number">1</span>] = tmp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    -- max;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = max;j &gt; min;j--)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[j] &lt; arr[j<span class="number">-1</span>])&#123;</span>
<span class="line">        <span class="keyword">let</span> tmp = arr[j]</span>
<span class="line">        arr[j] = arr[j<span class="number">-1</span>];</span>
<span class="line">        arr[j<span class="number">-1</span>] = tmp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    ++ min;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi">算法分析</h3>
<p>冒泡排序对有 $n$ 个元素的项目平均需要 $O(n^2)$ 次比较次数，它可以原地排序，并且是能简单实现的几种排序算法之一，但是它对于少数元素之外的数列排序是很没有效率的。</p>
<ul>
<li>最佳情况： $T(n) = O(n)$</li>
<li>最差情况： $T(n) = O(n^2)$</li>
<li>平均情况： $T(n) = O(n^2)$</li>
</ul>
<h2 id="xuan-ze-pai-xu-selection-sort-sup-class-footnote-ref-a-href-fn-2-id-fnref-2-2-a-sup">选择排序（Selection sort）<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></h2>
<h3 id="suan-fa-yuan-li-1">算法原理</h3>
<blockquote>
<p>选择排序（Selection sort）是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置，然后，再从剩余未排序元素中继续寻找最小（大）元素，然后放到已排序序列的末尾。以此类推，直到所有元素均排序完毕。</p>
</blockquote>
<p>图解如下:</p>
<p><img src="http://img.blog.csdn.net/20160916164754013" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-1">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>$n$ 个记录的直接选择排序可经过 $n - 1$ 趟直接选择排序得到有序结果。具体算法描述如下：</p>
<ol>
<li>初始状态：无序区为 <code>R[1 ... n]</code>，有序区为空；</li>
<li>第 $i$ 趟排序$(i = 1, 2, 3 ... n - 1)$开始时，当前有序区和无序区分别为 $R[1 ... i - 1]$ 和 $R(i ... n)$。该趟排序从当前无序区中选出关键字最小的记录 $R[k]$，将它与无序区的第 1 个记录 $R$ 交换，使 $R[1 ... i]$ 和 $R[i + 1 ... n]$ 分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区；</li>
<li>$n - 1$ 趟结束后，数组有序化。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> SelectionSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> len = arr.length;</span>
<span class="line">  <span class="keyword">let</span> minIndex, tmp;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'选择排序耗时'</span>);</span>
<span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>;i &lt; len - <span class="number">1</span>; i++)&#123;</span>
<span class="line">    minIndex = i;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = i + <span class="number">1</span>;j &lt; len;j++)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[minIndex] &gt; arr[j])&#123;</span>
<span class="line">        minIndex = j;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    tmp = arr[i];</span>
<span class="line">    arr[i] = arr[minIndex];</span>
<span class="line">    arr[minIndex] = tmp;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'选择排序耗时'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-1">算法分析</h3>
<p>选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上，则它不会被移动。选择排序每次交换一对元素，它们当中至少有一个将被移到其最终位置上，因此对 $n$ 个元素的表进行排序总共进行至多 $n - 1$ 次交换。在所有的<em>完全依靠交换</em>去移动元素的排序方法中，选择排序属于非常好的一种。
但原地操作几乎是选择排序的唯一优点，当空间复杂度要求较高时，可以考虑选择排序；实际适用的场合非常罕见。</p>
<ul>
<li>最佳情况： $T(n) = O(n^2)$</li>
<li>最差情况： $T(n) = O(n^2)$</li>
<li>平均情况： $T(n) = O(n^2)$</li>
</ul>
<h2 id="cha-ru-pai-xu-insertion-sort-sup-class-footnote-ref-a-href-fn-3-id-fnref-3-3-a-sup">插入排序（Insertion sort）<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></h2>
<h3 id="suan-fa-yuan-li-2">算法原理</h3>
<blockquote>
<p>插入排序（Insertion Sort）是一种简单直观的排序算法。它的工作原理是通过构建有序序列，对于未排序数据，在已排序序列中从后向前s扫描，找到相应位置并插入。插入排序在实现上，通常采用 in-place 排序（即只需用到 $O(1)$ 的额外空间的排序），因而在从后向前的扫描过程中，需要反复把已排序元素逐步向后挪位，为最新元素提供插入空间。</p>
</blockquote>
<p>图解如下:</p>
<p><img src="http://img.blog.csdn.net/20160916173802597" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-2">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>一般来说，插入排序都采用in-place在数组上实现。具体算法描述如下：</p>
<ol>
<li>从第一个元素开始，该元素可以认为已经被排序</li>
<li>取出下一个元素，在已经排序的元素序列中从后向前扫描</li>
<li>如果该元素（已排序）大于新元素，将该元素移到下一位置</li>
<li>重复步骤 3，直到找到已排序的元素小于或者等于新元素的位置</li>
<li>将新元素插入到该位置后</li>
<li>重复步骤 $2~5$</li>
</ol>
<p>如果<em>比较操作</em>的代价比<em>交换操作大</em>的话，可以采用<em>二分查找法</em>来减少比较操作的数目。该算法可以认为是插入排序的一个变种，称为<a href=""><em>二分查找插入排序</em></a>。</p>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> insertionSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> len = arr.length;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'插入排序耗时'</span>);</span>
<span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>;i &lt; len; i++)&#123;</span>
<span class="line">    <span class="keyword">let</span> key = arr[i]; </span>
<span class="line">    <span class="keyword">let</span> j = i - <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">while</span>(j &gt;= <span class="number">0</span> &amp;&amp; arr[j] &gt; key)&#123;</span>
<span class="line">      arr[j+<span class="number">1</span>] = arr[j];</span>
<span class="line">      j--;</span>
<span class="line">    &#125;</span>
<span class="line">    arr[j+<span class="number">1</span>] = key;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'插入排序耗时'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<blockquote>
<p>改进插入排序：查找插入位置时使用二分查找的方式。</p>
</blockquote>
<p>具体思路如下：</p>
<ol>
<li>在插入第 $i$ 个元素时，对前面的 $0 ~ i-1$ 元素进行折半。</li>
<li>与前面的 $0 ~ i-1$ 个元素中间的元素进行比较，如果小，则对前半再进行折半，否则对后半进行折半。</li>
<li>直到 left &gt; right，再把第 $i$ 个元素前 1 位和目标位置间的所有元素后移，把第 $i$ 个元素放在目标位置上。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> binaryInsertionSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'二分插入排序耗时：'</span>);</span>
<span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; arr.length; i++)&#123;</span>
<span class="line">    <span class="keyword">let</span> key = arr[i], left = <span class="number">0</span>, right = i - <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">while</span> (left &lt;= right)&#123;</span>
<span class="line">      <span class="keyword">let</span> middle = <span class="built_in">parseInt</span>((left + right) / <span class="number">2</span>);</span>
<span class="line">      <span class="keyword">if</span> (key &lt; arr[middle])&#123;</span>
<span class="line">        right = middle - <span class="number">1</span>;</span>
<span class="line">      &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">        left = middle + <span class="number">1</span>;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = i - <span class="number">1</span>; j &gt;= left; j--)&#123;</span>
<span class="line">      arr[j + <span class="number">1</span>] = arr[j];</span>
<span class="line">    &#125;</span>
<span class="line">    arr[left] = key;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'二分插入排序耗时：'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-2">算法分析</h3>
<ul>
<li>最佳情况： $T(n) = O(n)$</li>
<li>最差情况： $T(n) = O(n^2)$</li>
<li>平均情况： $T(n) = O(n^2)$</li>
</ul>
<h2 id="xi-er-pai-xu-shell-sort-sup-class-footnote-ref-a-href-fn-4-id-fnref-4-4-a-sup">希尔排序（Shell sort）<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup></h2>
<h3 id="suan-fa-yuan-li-3">算法原理</h3>
<blockquote>
<p>希尔排序，也称递减增量排序算法，是插入排序的一种更高效的改进版本。它是非稳定排序算法。</p>
</blockquote>
<p>希尔排序基于插入排序的以下两点性质提出了改进方法：</p>
<ol>
<li>插入排序在对几乎已经排好序的数据操作时，效率高，即可以达到线性排序的效率。</li>
<li>但插入排序一般来说是低效的，因为插入排序每次只能将数据移动一位。</li>
</ol>
<p>它与插入排序的不同之处在于，它会优先比较距离较远的元素。因此希尔排序又叫缩小增量排序。</p>
<p>图解如下:</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/JavaScript%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/shellsort.jpeg" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-3">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序，具体算法描述：</p>
<ol>
<li>选择一个增量序列$t1，t2，…，tk$，其中 $ti &gt; tj(i &gt; j)$，$tk = 1$；</li>
<li>按增量序列个数 $k$，对序列进行 $k$ 趟排序；</li>
<li>每趟排序，根据对应的增量 $ti$，将待排序列分割成若干长度为 $m$ 的子序列，分别对各子表进行直接插入排序。仅增量因子为 1 时，整个序列作为一个表来处理，表长度即为整个序列的长度。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> shellSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'希尔排序耗时:'</span>);</span>
<span class="line">  <span class="keyword">let</span> len = arr.length,temp,gap = <span class="number">1</span>;</span>
<span class="line">  <span class="keyword">while</span>(gap &lt; len / <span class="number">5</span>) &#123; <span class="comment">// 动态定义间隔序列步长为 5</span></span>
<span class="line">      gap = gap * <span class="number">5</span> + <span class="number">1</span>;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">for</span> (gap; gap &gt; <span class="number">0</span>; gap = <span class="built_in">Math</span>.floor(gap / <span class="number">5</span>)) &#123;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = gap; i &lt; len; i++) &#123;</span>
<span class="line">      temp = arr[i];</span>
<span class="line">      <span class="keyword">let</span> j;</span>
<span class="line">      <span class="keyword">for</span> (j = i - gap; j &gt;= <span class="number">0</span> &amp;&amp; arr[j] &gt; temp; j -= gap) &#123;</span>
<span class="line">        arr[j + gap] = arr[j];</span>
<span class="line">      &#125;</span>
<span class="line">      arr[j + gap] = temp;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'希尔排序耗时:'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-3">算法分析</h3>
<ul>
<li>最佳情况： $T_(n) = O_(n\log_2 n)$</li>
<li>最差情况： $T_(n) = O_(n\log_2 n)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="gui-bing-pai-xu-merge-sort-sup-class-footnote-ref-a-href-fn-5-id-fnref-5-5-a-sup">归并排序（Merge sort）<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup></h2>
<h3 id="suan-fa-yuan-li-4">算法原理</h3>
<blockquote>
<p>归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法（Divide and Conquer）的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并，得到完全有序的序列；即先使每个子序列有序，再使子序列段间有序。若将两个有序表合并成一个有序表，称为二路归并。</p>
</blockquote>
<p>图解如下：</p>
<p><img src="http://img.blog.csdn.net/20160917001326254" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-4">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>把长度为 n 的输入序列分成两个长度为 n/2 的子序列；</li>
<li>对这两个子序列分别采用归并排序；</li>
<li>将两个排序好的子序列合并成一个最终的排序序列。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span>
<span class="line"><span class="keyword">const</span> mergeSort = <span class="function">(<span class="params">arr</span>) =&gt;</span>&#123;  <span class="comment">//采用自上而下的递归方法</span></span>
<span class="line">    <span class="keyword">let</span> len = arr.length;</span>
<span class="line">    <span class="keyword">if</span>(len &lt; <span class="number">2</span>) &#123;</span>
<span class="line">        <span class="keyword">return</span> arr;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">let</span> middle = <span class="built_in">Math</span>.floor(len / <span class="number">2</span>),</span>
<span class="line">        left = arr.slice(<span class="number">0</span>, middle),</span>
<span class="line">        right = arr.slice(middle);</span>
<span class="line">    <span class="keyword">return</span> merge(mergeSort(left), mergeSort(right));</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> merge = <span class="function">(<span class="params">left, right</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">let</span> result = [];</span>
<span class="line">    <span class="keyword">while</span> (left.length &amp;&amp; right.length) &#123;</span>
<span class="line">        <span class="keyword">if</span> (left[<span class="number">0</span>] &lt;= right[<span class="number">0</span>]) &#123;</span>
<span class="line">            result.push(left.shift());</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            result.push(right.shift());</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">while</span> (left.length)</span>
<span class="line">        result.push(left.shift());</span>
<span class="line">    <span class="keyword">while</span> (right.length)</span>
<span class="line">        result.push(right.shift());</span>
<span class="line">    <span class="keyword">return</span> result;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> arr=[<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.time(<span class="string">'归并排序耗时'</span>);</span>
<span class="line"><span class="built_in">console</span>.log(mergeSort(arr));</span>
<span class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'归并排序耗时'</span>);</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-4">算法分析</h3>
<p>和选择排序一样，归并排序的性能不受输入数据的影响，但它的表现比选择排序要好，因为它始终都是 $O_(n\log n)$ 的时间复杂度。但代价是需要额外的内存空间。</p>
<ul>
<li>最佳情况： $T_(n) = O_(n)$</li>
<li>最差情况： $T_(n) = O_(n\log n)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="kuai-su-pai-xu-quick-sort-sup-class-footnote-ref-a-href-fn-6-id-fnref-6-6-a-sup">快速排序（Quick sort）<sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup></h2>
<h3 id="suan-fa-yuan-li-5">算法原理</h3>
<blockquote>
<p>通过一趟排序将待排记录分隔成独立的两部分，其中一部分记录的关键字均比另一部分的关键字小，则可分别对这两部分记录继续进行排序，以达到整个序列有序。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917003004906" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-5">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>快速排序使用分治法来把一个串（list）分为两个子串（sub-lists）。具体算法描述如下：</p>
<ol>
<li>从数列中挑出一个元素，称为 “基准”（pivot）；</li>
<li>重新排序数列，所有元素比基准值小的摆放在基准前面，所有元素比基准值大的摆在基准的后面（相同的数可以到任一边）。在这个分区退出之后，该基准就处于数列的中间位置。这个称为分区（partition）操作；</li>
<li>递归地（recursive）把小于基准值元素的子数列和大于基准值元素的子数列排序。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span>
<span class="line"><span class="keyword">const</span> quickSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">if</span> (arr.length &lt;= <span class="number">1</span>) &#123; <span class="keyword">return</span> arr; &#125;</span>
<span class="line">    <span class="keyword">let</span> pivotIndex = <span class="built_in">Math</span>.floor(arr.length / <span class="number">2</span>);</span>
<span class="line">    <span class="keyword">let</span> pivot = arr.splice(pivotIndex, <span class="number">1</span>)[<span class="number">0</span>];</span>
<span class="line">    <span class="keyword">let</span> left = [];</span>
<span class="line">    <span class="keyword">let</span> right = [];</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++)&#123;</span>
<span class="line">    　　<span class="keyword">if</span> (arr[i] &lt; pivot) &#123;</span>
<span class="line">    　　　　left.push(arr[i]);</span>
<span class="line">    　　&#125; <span class="keyword">else</span> &#123;</span>
<span class="line">    　　　　right.push(arr[i]);</span>
<span class="line">    　　&#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">return</span> quickSort(left).concat([pivot], quickSort(right));</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">let</span> arr=[<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.time(<span class="string">'快速排序耗时'</span>);</span>
<span class="line"><span class="built_in">console</span>.log(quickSort(arr));</span>
<span class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'快速排序耗时'</span>);</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-5">算法分析</h3>
<ul>
<li>最佳情况： $T_(n) = O_(n\log n)$</li>
<li>最差情况： $T_(n) = O_(n^2)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="dui-pai-xu-heap-sort-sup-class-footnote-ref-a-href-fn-7-id-fnref-7-7-a-sup">堆排序（Heap sort）<sup class="footnote-ref"><a href="#fn7" id="fnref7">[7]</a></sup></h2>
<h3 id="suan-fa-yuan-li-6">算法原理</h3>
<blockquote>
<p>堆排序可以说是一种利用堆的概念来排序的选择排序。它利用堆这种数据结构所设计。堆积是一个近似完全二叉树的结构，并同时满足堆积的性质：即子结点的键值或索引总是小于（或者大于）它的父节点。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917105502853" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-6">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>将初始待排序关键字序列 $(R1,R2…,Rn)$ 构建成大顶堆，此堆为初始的无序区；</li>
<li>将堆顶元素 $R[1]$ 与最后一个元素 $R[n]$ 交换，此时得到新的无序区 $(R1,R2,……,Rn-1)$ 和新的有序区 $(Rn)$ ，且满足 $R[1,2…,n-1] &lt;= R[n]$；</li>
<li>由于交换后新的堆顶 $R[1]$ 可能违反堆的性质，因此需要对当前无序区 $(R1,R2,……,Rn-1)$ 调整为新堆，然后再次将 $R[1]$ 与无序区最后一个元素交换，得到新的无序区 $(R1,R2…,Rn-2)$ 和新的有序区 $(Rn-1,Rn)$。不断重复此过程直到有序区的元素个数为 n-1，则整个排序过程完成。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span>
<span class="line"><span class="keyword">const</span> heapSort = <span class="function">(<span class="params">array</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'堆排序耗时'</span>);</span>
<span class="line">    <span class="comment">//建堆</span></span>
<span class="line">    <span class="keyword">let</span> heapSize = array.length, temp;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="built_in">Math</span>.floor(heapSize / <span class="number">2</span>) - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span>
<span class="line">        heapify(array, i, heapSize);</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="comment">//堆排序</span></span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = heapSize - <span class="number">1</span>; j &gt;= <span class="number">1</span>; j--) &#123;</span>
<span class="line">        temp = array[<span class="number">0</span>];</span>
<span class="line">        array[<span class="number">0</span>] = array[j];</span>
<span class="line">        array[j] = temp;</span>
<span class="line">        heapify(array, <span class="number">0</span>, --heapSize);</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'堆排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> array;</span>
<span class="line">&#125;</span>
<span class="line"><span class="comment">/*方法说明：维护堆的性质</span>
<span class="line">@param  arr 数组</span>
<span class="line">@param  x   数组下标</span>
<span class="line">@param  len 堆大小*/</span></span>
<span class="line"><span class="keyword">const</span> heapify = <span class="function">(<span class="params">arr, x, len</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">let</span> l = <span class="number">2</span> * x + <span class="number">1</span>, r = <span class="number">2</span> * x + <span class="number">2</span>, largest = x, temp;</span>
<span class="line">    <span class="keyword">if</span> (l &lt; len &amp;&amp; arr[l] &gt; arr[largest]) &#123;</span>
<span class="line">        largest = l;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">if</span> (r &lt; len &amp;&amp; arr[r] &gt; arr[largest]) &#123;</span>
<span class="line">        largest = r;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">if</span> (largest != x) &#123;</span>
<span class="line">        temp = arr[x];</span>
<span class="line">        arr[x] = arr[largest];</span>
<span class="line">        arr[largest] = temp;</span>
<span class="line">        heapify(arr, largest, len);</span>
<span class="line">    &#125;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">let</span> arr=[<span class="number">91</span>,<span class="number">60</span>,<span class="number">96</span>,<span class="number">13</span>,<span class="number">35</span>,<span class="number">65</span>,<span class="number">46</span>,<span class="number">65</span>,<span class="number">10</span>,<span class="number">30</span>,<span class="number">20</span>,<span class="number">31</span>,<span class="number">77</span>,<span class="number">81</span>,<span class="number">22</span>];</span>
<span class="line"><span class="built_in">console</span>.log(heapSort(arr));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-6">算法分析</h3>
<ul>
<li>最佳情况： $T_(n) = O_(n\log n)$</li>
<li>最差情况： $T_(n) = O_(n\log n)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="ji-shu-pai-xu-counting-sort-sup-class-footnote-ref-a-href-fn-8-id-fnref-8-8-a-sup">计数排序（Counting sort）<sup class="footnote-ref"><a href="#fn8" id="fnref8">[8]</a></sup></h2>
<h3 id="suan-fa-yuan-li-7">算法原理</h3>
<blockquote>
<p>计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组 C，其中第 i 个元素是待排序数组 A 中值等于 i 的元素的个数。然后根据数组 C 来将 A 中的元素排到正确的位置。它只能对整数进行排序。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917110641479" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-7">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>找出待排序的数组中最大和最小的元素；</li>
<li>统计数组中每个值为 i 的元素出现的次数，存入数组 C 的第 i 项；</li>
<li>对所有的计数累加（从 C 中的第一个元素开始，每一项和前一项相加）；</li>
<li>反向填充目标数组：将每个元素 i 放在新数组的第 $C(i)$ 项，每放一个元素就将 $C(i)$ 减去 1。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span>
<span class="line"><span class="keyword">const</span> countingSort = <span class="function">(<span class="params">array</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">let</span> len = array.length,</span>
<span class="line">        result = [],</span>
<span class="line">        C = [],</span>
<span class="line">        min,max;</span>
<span class="line">        min = max = array[<span class="number">0</span>];</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'计数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; len; i++) &#123;</span>
<span class="line">        min = min &lt;= array[i] ? min : array[i];</span>
<span class="line">        max = max &gt;= array[i] ? max : array[i];</span>
<span class="line">        C[array[i]] = C[array[i]] ? C[array[i]] + <span class="number">1</span> : <span class="number">1</span>;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = min; j &lt; max; j++) &#123;</span>
<span class="line">        C[j + <span class="number">1</span>] = (C[j + <span class="number">1</span>] || <span class="number">0</span>) + (C[j] || <span class="number">0</span>);</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> k = len - <span class="number">1</span>; k &gt;= <span class="number">0</span>; k--) &#123;</span>
<span class="line">        result[C[array[k]] - <span class="number">1</span>] = array[k];</span>
<span class="line">        C[array[k]]--;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'计数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> result;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">let</span> arr = [<span class="number">2</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">8</span>, <span class="number">7</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">7</span>, <span class="number">3</span>, <span class="number">9</span>, <span class="number">8</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">9</span>, <span class="number">2</span>];</span>
<span class="line"><span class="built_in">console</span>.log(countingSort(arr));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-7">算法分析</h3>
<p>计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。
作为一种线性时间复杂度的排序，计数排序要求输入的数据必须是有确定范围的整数。</p>
<p>当输入的元素是 n 个 0 到 k 之间的整数时，它的运行时间是 O(n + k)。计数排序不是比较排序，排序的速度快于任何比较排序算法。由于用来计数的数组 C 的长度取决于待排序数组中数据的范围（等于待排序数组的最大值与最小值的差加上 1），这使得计数排序对于数据范围很大的数组，需要大量时间和内存。</p>
<ul>
<li>最佳情况： $T_(n) = O_(n+k)$</li>
<li>最差情况： $T_(n) = O_(n+k)$</li>
<li>平均情况： $T_(n) = O_(n+k)$</li>
</ul>
<h2 id="tong-pai-xu-bucket-sort-sup-class-footnote-ref-a-href-fn-9-id-fnref-9-9-a-sup">桶排序（Bucket sort）<sup class="footnote-ref"><a href="#fn9" id="fnref9">[9]</a></sup></h2>
<h3 id="suan-fa-yuan-li-8">算法原理</h3>
<blockquote>
<p>工作的原理是将数组分到有限数量的桶里。每个桶再个别排序（有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序）</p>
</blockquote>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/JavaScript%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/bucketsort.jpeg" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-8">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>设置一个定量的数组当作空桶；</li>
<li>遍历输入数据，并且把数据一个一个放到对应的桶里去；</li>
<li>对每个不是空的桶进行排序；</li>
<li>从不是空的桶里把排好序的数据拼接起来。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span>
<span class="line"><span class="keyword">const</span> bucketSort = <span class="function">(<span class="params">array, num</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">if</span> (array.length &lt;= <span class="number">1</span>) &#123;</span>
<span class="line">        <span class="keyword">return</span> array;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">var</span> len = array.length, buckets = [], result = [], min = max = array[<span class="number">0</span>], regex = <span class="string">'/^[1-9]+[0-9]*$/'</span>, space, n = <span class="number">0</span>;</span>
<span class="line">    num = num || ((num &gt; <span class="number">1</span> &amp;&amp; regex.test(num)) ? num : <span class="number">10</span>);</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'桶排序耗时'</span>);</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i &lt; len; i++) &#123;</span>
<span class="line">        min = min &lt;= array[i] ? min : array[i];</span>
<span class="line">        max = max &gt;= array[i] ? max : array[i];</span>
<span class="line">    &#125;</span>
<span class="line">    space = (max - min + <span class="number">1</span>) / num;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; len; j++) &#123;</span>
<span class="line">        <span class="keyword">var</span> index = <span class="built_in">Math</span>.floor((array[j] - min) / space);</span>
<span class="line">        <span class="keyword">if</span> (buckets[index]) &#123;   <span class="comment">//  非空桶，插入排序</span></span>
<span class="line">            <span class="keyword">var</span> k = buckets[index].length - <span class="number">1</span>;</span>
<span class="line">            <span class="keyword">while</span> (k &gt;= <span class="number">0</span> &amp;&amp; buckets[index][k] &gt; array[j]) &#123;</span>
<span class="line">                buckets[index][k + <span class="number">1</span>] = buckets[index][k];</span>
<span class="line">                k--;</span>
<span class="line">            &#125;</span>
<span class="line">            buckets[index][k + <span class="number">1</span>] = array[j];</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;    <span class="comment">//空桶，初始化</span></span>
<span class="line">            buckets[index] = [];</span>
<span class="line">            buckets[index].push(array[j]);</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">while</span> (n &lt; num) &#123;</span>
<span class="line">        result = result.concat(buckets[n]);</span>
<span class="line">        n++;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'桶排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> result;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">var</span> arr=[<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.log(bucketSort(arr,<span class="number">4</span>));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-8">算法分析</h3>
<p>桶排序是计数排序的升级版。它利用了函数的映射关系，高效与否的关键就在于这个映射函数的确定。
桶排序最好情况下使用线性时间 $O(n)$，桶排序的时间复杂度，取决与对各个桶之间数据进行排序的时间复杂度，因为其它部分的时间复杂度都为 $O(n)$。很显然，桶划分的越小，各个桶之间的数据越少，排序所用的时间也会越少。但相应的空间消耗就会增大。</p>
<ul>
<li>最佳情况： $T_(n) = O_(n+k)$</li>
<li>最差情况： $T_(n) = O_(n+k)$</li>
<li>平均情况： $T_(n) = O_(n^2)$</li>
</ul>
<h2 id="ji-shu-pai-xu-radix-sort-sup-class-footnote-ref-a-href-fn-10-id-fnref-10-10-a-sup">基数排序（Radix sort）<sup class="footnote-ref"><a href="#fn10" id="fnref10">[10]</a></sup></h2>
<h3 id="suan-fa-yuan-li-9">算法原理</h3>
<blockquote>
<p>基数排序是按照低位先排序，然后收集；再按照高位排序，然后再收集；依次类推，直到最高位。有时候有些属性是有优先级顺序的，先按低优先级排序，再按高优先级排序。最后的次序就是高优先级高的在前，高优先级相同的低优先级高的在前。基数排序基于分别排序，分别收集，所以是稳定的。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917123313659" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-9">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>取得数组中的最大数，并取得位数；</li>
<li>arr 为原始数组，从最低位开始取每个位组成 radix 数组；</li>
<li>对 radix 进行计数排序（利用计数排序适用于小范围数的特点）；</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span>
<span class="line"><span class="keyword">const</span> radixSort = <span class="function">(<span class="params">arr, maxDigit</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">var</span> mod = <span class="number">10</span>;</span>
<span class="line">    <span class="keyword">var</span> dev = <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">var</span> counter = [];</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'基数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; maxDigit; i++, dev *= <span class="number">10</span>, mod *= <span class="number">10</span>) &#123;</span>
<span class="line">        <span class="keyword">for</span>(<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; arr.length; j++) &#123;</span>
<span class="line">            <span class="keyword">var</span> bucket = <span class="built_in">parseInt</span>((arr[j] % mod) / dev);</span>
<span class="line">            <span class="keyword">if</span>(counter[bucket]== <span class="literal">null</span>) &#123;</span>
<span class="line">                counter[bucket] = [];</span>
<span class="line">            &#125;</span>
<span class="line">            counter[bucket].push(arr[j]);</span>
<span class="line">        &#125;</span>
<span class="line">        <span class="keyword">var</span> pos = <span class="number">0</span>;</span>
<span class="line">        <span class="keyword">for</span>(<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; counter.length; j++) &#123;</span>
<span class="line">            <span class="keyword">var</span> value = <span class="literal">null</span>;</span>
<span class="line">            <span class="keyword">if</span>(counter[j]!=<span class="literal">null</span>) &#123;</span>
<span class="line">                <span class="keyword">while</span> ((value = counter[j].shift()) != <span class="literal">null</span>) &#123;</span>
<span class="line">                      arr[pos++] = value;</span>
<span class="line">                &#125;</span>
<span class="line">          &#125;</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'基数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">var</span> arr = [<span class="number">3</span>, <span class="number">44</span>, <span class="number">38</span>, <span class="number">5</span>, <span class="number">47</span>, <span class="number">15</span>, <span class="number">36</span>, <span class="number">26</span>, <span class="number">27</span>, <span class="number">2</span>, <span class="number">46</span>, <span class="number">4</span>, <span class="number">19</span>, <span class="number">50</span>, <span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.log(radixSort(arr,<span class="number">2</span>));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-9">算法分析</h3>
<p>基数排序有两种方法：</p>
<p>MSD 从高位开始进行排序
LSD 从低位开始进行排序</p>
<ul>
<li>最佳情况： $T_(n) = O_(n^2)$</li>
<li>最差情况： $T_(n) = O_(n^2)$</li>
<li>平均情况： $T_(n) = O_(n^2)$</li>
</ul>
<p>基数排序 vs 计数排序 vs 桶排序</p>
<p>这三种排序算法都利用了桶的概念，但对桶的使用方法上有明显差异：</p>
<p>基数排序：根据键值的每位数字来分配桶
计数排序：每个桶只存储单一键值
桶排序：每个桶存储一定范围的数值</p>
<p><strong>以上代码可访问 <a href="https://github.com/yangzj1992/Use-Javascript-Solve-Logic-Questions/tree/master/data-structure/Sort" target="_blank" rel="external">GitHub 具体查看</a></strong></p>
<h2 id="js-yuan-sheng-han-shu-zhong-de-pai-xu">JS 原生函数中的排序</h2>
<p>说到前端排序，自然首先会想到 JavaScript 的原生接口 <code>Array.prototype.sort</code></p>
<p>这个接口自 <code>ECMAScript 1st Edition</code> 起就被设计存在。而在规范中关于它的描述是这样的：<sup class="footnote-ref"><a href="#fn11" id="fnref11">[11]</a></sup></p>
<p><code>Array.prototype.sort(compareFn)</code></p>
<blockquote>
<p>The elements of this array are sorted. The sort is not necessarily stable (that is, elements that compare equal do not necessarily remain in their original order). If comparefn is not undefined, it should be a function that accepts two arguments x and y and returns a negative value if x &lt; y, zero if x = y, or a positive value if x &gt; y.</p>
</blockquote>
<p>显然，规范里并没有限定 <code>sort</code> 内部需要用什么排序算法。在这样的背景下，前端排序这件事完全取决于各家浏览器内核的具体实现。</p>
<h3 id="chrome-de-shi-xian">Chrome 的实现</h3>
<p>Chrome 的 JavaScript 引擎是 v8。由于它是开源的，所以可以直接看<a href="https://github.com/v8/v8" target="_blank" rel="external">源代码</a>。</p>
<p>整个 <a href="https://github.com/v8/v8/blob/master/src/js/array.js" target="_blank" rel="external">array.js</a> 都是用 JavaScript 语言实现的。排序方法部分很明显比通常看到的快速排序要复杂得多，但显然核心算法还是快速排序的思想。算法复杂的原因在于 v8 出于性能考虑进行了很多优化。(后续会展开说)</p>
<h3 id="firefox-zhong-de-shi-xian">Firefox中的实现</h3>
<p>暂时无法确定 Firefox 的 JavaScript 引擎即将使用的数组排序算法会是什么。</p>
<p>按照现有的信息，SpiderMoney 内部实现了归并排序。（这里不多做叙述）</p>
<h3 id="microsoft-edge-zhong-de-shi-xian">Microsoft Edge中的实现</h3>
<p>Microsoft Edge 的 JavaScript 引擎 Chakra 的核心部分代码已经于 2016 年初在 Github 开源。</p>
<p>通过<a href="https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Library/JavascriptArray.cpp" target="_blank" rel="external">源代码</a>可以发现，Chakra 的数组排序算法也主要是以快速排序为主。并针对其他具体特殊情况进行具体优化。</p>
<h3 id="pai-xu-de-chai-yi">排序的差异</h3>
<p>如上所述，快速排序是一种不稳定的排序算法，而归并排序是一种稳定的排序算法。由于不同引擎在算法选择上可能存在差异，导致前端如果依赖 <code>Array.prototype.sort</code> 接口实现的 JavaScript 代码，在浏览器中实际执行的排序效果是不一致的。</p>
<p>而排序稳定性的差异其实也只是在特定的场景才会体现出问题；在很多情况下，不稳定的排序也并不会造成什么影响。所以假如实际项目开发中，对于数组的排序没有稳定性的需求，那么看到这里就可以了。</p>
<p>但是若项目要求排序必须是稳定的，那么这些差异的存在将无法满足需求。我们需要为此进行一些额外的工作。</p>
<p>举个例子：</p>
<p>某市的机动车牌照拍卖系统，最终中标的规则为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
</pre></td><td class="code"><pre><span class="line">1. 按价格进行倒排序；</span>
<span class="line">2. 相同价格则按照竞标顺位（即价格提交时间）进行正排序。</span>
</pre></td></tr></table></figure>
<p>排序若是在前端进行，那么采用快速排序的浏览器中显示的中标者很可能是不符合预期的。</p>
<p>此外例如：
<a href="http://www.cnblogs.com/cchust/p/5304594.html" target="_blank" rel="external">MySQL 5.6 的 <code>limit M,N</code> 语句实现</a>
等情况都是不稳定排序的特征。</p>
<h3 id="bei-hou-de-yuan-yin">背后的原因</h3>
<h4 id="chrome-wei-shi-yao-cai-yong-kuai-su-pai-xu">Chrome为什么采用快速排序</h4>
<p>其实这个情况从一开始便存在。</p>
<p>Chrome测试版于<a href="https://zh.wikipedia.org/wiki/Google_Chrome#.E7.99.BC.E5.B8.83" target="_blank" rel="external">2008年9月2日发布</a> (这里附上当时随版本发布的漫画，还是很有意思的<a href="http://blogoscoped.com/google-chrome/" target="_blank" rel="external">Google on Google Chrome - comic book</a>)，然而发布后不久，就有开发者向 Chromium 开发组提交 <a href="https://bugs.chromium.org/p/v8/issues/detail?id=90" target="_blank" rel="external">#90 Bug V8 doesn't stable sort</a> 反馈 v8 的数组排序实现不是稳定排序的。</p>
<p>这个 Bug ISSUE 讨论的时间跨度很大。时至今日，仍然有开发者对 v8 的数组排序的实现提出评论。</p>
<p>同时我们还注意到，该 ISSUE 曾经已被关闭。但是于 2013 年 6 月被开发组成员重新打开，作为 ECMAScript Next 规范讨论的参考。</p>
<p>而 <a href="https://mail.mozilla.org/pipermail/es-discuss/2013-June/031276.html" target="_blank" rel="external">es-discuss</a> 的最后结论是这样的</p>
<blockquote>
<p>It does not change. Stable is a subset of unstable. And vice versa, every unstable algorithm returns a stable result for some inputs. Mark’s point is that requiring “always unstable” has no meaning, no matter what language you chose.</p>
</blockquote>
<p>这也正如本文前段所引用的已定稿 <code>ECMAScript 2015</code> 规范中的描述一样。</p>
<h4 id="shi-dai-te-dian">时代特点</h4>
<p>IMHO，Chrome 发布之初即被报告出这个问题可能是有其特殊的时代特点。</p>
<p>上文已经说到，Chrome 第一版是 2008 年 9 月发布的。根据 <a href="http://gs.statcounter.com/#browser-ww-monthly-200809-200809-bar" target="_blank" rel="external">statcounter</a> 的统计数据，那个时期市场占有率最高的两款浏览器分别是 IE(那时候只有 IE6 和 IE7) 和 Firefox，市场占有率分别达到了 67.16% 和 25.77%。也就是说，两个浏览器加起来的市场占有率超过了 90%。</p>
<p>而根据另一份<a href="http://ofb.net/~sethml/is-sort-stable.html" target="_blank" rel="external">浏览器排序算法稳定性的统计</a>数据显示，这两款超过了 90% 市场占有率的浏览器都采用了稳定的数组排序。所以 Chrome 发布之初被开发者质疑也是合情合理的。</p>
<p>我们从 ISSUE 讨论的过程中，可以大概理解开发组成员对于引擎实现采用快速排序的一些考量。他们认为引擎必须遵守 ECMAScript 规范。由于规范不要求稳定排序的描述，故他们认为 v8 的实现也是完全符合规范的。</p>
<h4 id="xing-neng-kao-lu">性能考虑</h4>
<p>另外，他们认为 v8 设计的一个重要考量在于引擎的性能。</p>
<p><strong>快速排序</strong>相比较于<strong>归并排序</strong>，在整体性能上表现更好：</p>
<ul>
<li>更高的计算效率。快速排序在实际计算机执行环境中比同等时间复杂度的其他排序算法更快（不命中最差组合的情况下）</li>
<li>更低的空间成本。前者仅有 $O(logn)$ 的空间复杂度，相比较后者 $O(n)$ 的空间复杂度在运行时的内存消耗更少</li>
</ul>
<h4 id="v-8-zai-shu-zu-pai-xu-suan-fa-zhong-de-xing-neng-you-hua">v8 在数组排序算法中的性能优化</h4>
<p>既然说 v8 非常看中引擎的性能，那么在数组排序中它做了哪些事呢？</p>
<p>通过阅读源代码，还是粗浅地学习了一些皮毛。</p>
<ul>
<li>混合插入排序
快速排序是分治的思想，将大数组分解，逐层往下递归。但是若递归深度太大，为了维持递归，调用栈的内存资源消耗也会很大。优化不好甚至可能造成栈溢出。</li>
</ul>
<p>目前 v8 的实现是设定一个阈值，对最下层的 10 个及以下长度的小数组使用插入排序。</p>
<p>根据代码注释以及 Wikipedia 中的描述，虽然插入排序的平均时间复杂度为 $O(n^2)$ 差于快速排序的 $O(nlogn)$。但是在运行环境，小数组使用插入排序的效率反而比快速排序会更高，这里不再展开。</p>
<p><a href="https://github.com/v8/v8/blob/master/src/js/array.js" target="_blank" rel="external">v8 代码示例</a>:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> QuickSort = <span class="function"><span class="keyword">function</span> <span class="title">QuickSort</span>(<span class="params">a, from, to</span>) </span>&#123;</span>
<span class="line">    ......</span>
<span class="line">    while (<span class="literal">true</span>) &#123;</span>
<span class="line">        <span class="comment">// Insertion sort is faster for short arrays.</span></span>
<span class="line">        <span class="keyword">if</span> (to - <span class="keyword">from</span> &lt;= <span class="number">10</span>) &#123;</span>
<span class="line">            InsertionSort(a, <span class="keyword">from</span>, to);</span>
<span class="line">            <span class="keyword">return</span>;</span>
<span class="line">        &#125;</span>
<span class="line">        ......</span>
<span class="line">    &#125;</span>
<span class="line">    ......</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<ul>
<li>三数取中</li>
</ul>
<p>正如已知的，快速排序的阿克琉斯之踵在于，最差数组组合情况下会算法退化。</p>
<p>快速排序的算法核心在于选择一个基准(<strong>pivot</strong>)，将经过比较交换的数组按基准分解为两个数区进行后续递归。试想如果对一个已经有序的数组，每次选择基准元素时总是选择第一个或者最后一个元素，那么每次都会有一个数区是空的，递归的层数将达到 <strong>n</strong>，最后导致算法的时间复杂度退化为 $O(n^2)$。因此 pivot 的选择非常重要。</p>
<p>v8采用的是**三数取中(median-of-three)**的优化：除了头尾两个元素再额外选择一个元素参与基准元素的竞争。</p>
<p>第三个元素的选取策略大致为：</p>
<ol>
<li>当数组长度小于等于 <strong>1000</strong> 时，选择折半位置的元素作为目标元素。</li>
<li>当数组长度超过 <strong>1000</strong> 时，每隔 <strong>200-215</strong> 个(<em>非固定，跟着数组长度而变化</em>)左右选择一个元素来先确定一批候选元素。接着在这批候选元素中进行一次排序，将所得的中位值作为目标元素</li>
</ol>
<p>最后取三个元素的中位值作为 <strong>pivot</strong>。</p>
<p>v8 代码示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> GetThirdIndex = <span class="function"><span class="keyword">function</span>(<span class="params">a, from, to</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> t_array = <span class="keyword">new</span> InternalArray();</span>
<span class="line">    <span class="comment">// Use both 'from' and 'to' to determine the pivot candidates.</span></span>
<span class="line">    <span class="keyword">var</span> increment = <span class="number">200</span> + ((to - <span class="keyword">from</span>) &amp; <span class="number">15</span>);</span>
<span class="line">    <span class="keyword">var</span> j = <span class="number">0</span>;</span>
<span class="line">    <span class="keyword">from</span> += <span class="number">1</span>;</span>
<span class="line">    to -= <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="keyword">from</span>; i &lt; to; i += increment) &#123;</span>
<span class="line">        t_array[j] = [i, a[i]];</span>
<span class="line">        j++;</span>
<span class="line">    &#125;</span>
<span class="line">    t_array.sort(<span class="function"><span class="keyword">function</span>(<span class="params">a, b</span>) </span>&#123;</span>
<span class="line">        <span class="keyword">return</span> comparefn(a[<span class="number">1</span>], b[<span class="number">1</span>]);</span>
<span class="line">    &#125;);</span>
<span class="line">    <span class="keyword">var</span> third_index = t_array[t_array.length &gt;&gt; <span class="number">1</span>][<span class="number">0</span>];</span>
<span class="line">    <span class="keyword">return</span> third_index;</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> QuickSort = <span class="function"><span class="keyword">function</span> <span class="title">QuickSort</span>(<span class="params">a, from, to</span>) </span>&#123;</span>
<span class="line">    ......</span>
<span class="line">    while (<span class="literal">true</span>) &#123;</span>
<span class="line">        ......</span>
<span class="line">        if (to - <span class="keyword">from</span> &gt; <span class="number">1000</span>) &#123;</span>
<span class="line">            third_index = GetThirdIndex(a, <span class="keyword">from</span>, to);</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            third_index = <span class="keyword">from</span> + ((to - <span class="keyword">from</span>) &gt;&gt; <span class="number">1</span>);</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    ......</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<ul>
<li>原地排序</li>
</ul>
<p>目前，大多数快速排序算法中大部分的代码实现如下所示：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> quickSort = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>) </span>&#123;</span>
<span class="line">　　<span class="keyword">if</span> (arr.length &lt;= <span class="number">1</span>) &#123; <span class="keyword">return</span> arr; &#125;</span>
<span class="line">　　<span class="keyword">var</span> pivotIndex = <span class="built_in">Math</span>.floor(arr.length / <span class="number">2</span>);</span>
<span class="line">　　<span class="keyword">var</span> pivot = arr.splice(pivotIndex, <span class="number">1</span>)[<span class="number">0</span>];</span>
<span class="line">　　<span class="keyword">var</span> left = [];</span>
<span class="line">　　<span class="keyword">var</span> right = [];</span>
<span class="line">　　<span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; arr.length; i++)&#123;</span>
<span class="line">　　　　<span class="keyword">if</span> (arr[i] &lt; pivot) &#123;</span>
<span class="line">　　　　　　left.push(arr[i]);</span>
<span class="line">　　　　&#125; <span class="keyword">else</span> &#123;</span>
<span class="line">　　　　　　right.push(arr[i]);</span>
<span class="line">　　　　&#125;</span>
<span class="line">　　&#125;</span>
<span class="line">　　<span class="keyword">return</span> quickSort(left).concat([pivot], quickSort(right));</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>以上代码存在一个问题在于：利用 left 和 right 两个数区存储递归的子数组，因此它需要 $O(n)$ 的额外存储空间。这与理论上的平均空间复杂度 $O(logn)$ 相比差距较大。</p>
<p>额外的空间开销，同样会影响实际运行时的整体速度。（这也是快速排序在实际运行时的表现可以超过同等时间复杂度级别的其他排序算法的其中一个原因。）所以一般来说，性能较好的快速排序会采用原地(in-place)排序的方式。</p>
<p>v8 源代码中的实现是对原数组进行元素交换。</p>
<h4 id="firefox-wei-shi-yao-cai-yong-gui-bing-pai-xu">Firefox 为什么采用归并排序</h4>
<p>它的背后也是有故事的。</p>
<p>Firefox 其实在一开始发布的时候对于数组排序的实现并不是采用稳定的排序算法，这块有据可考。</p>
<p>Firefox(Firebird) 最初版本实现的数组排序算法是堆排序，这也是一种不稳定的排序算法。因此，后来有人对此提交了一个 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=224128" target="_blank" rel="external">Bug</a>。</p>
<p>Mozilla开发组内部针对这个问题进行了一系列<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=224128#c2" target="_blank" rel="external">讨论</a>。</p>
<p>从讨论的过程我们能够得出几点</p>
<ul>
<li>同时期 Mozilla 的竞争对手是 <strong>IE6</strong>，从上文的统计数据可知 IE6 是稳定排序的</li>
<li>JavaScript 之父 Brendan Eich 觉得 <strong>Stability is good</strong></li>
<li>Firefox在采用<strong>堆排序</strong>之前采用的是<strong>快速排序</strong></li>
</ul>
<p>基于开发组成员倾向于实现稳定的排序算法为主要前提，<strong>Firefox3</strong> 将<strong>归并排序</strong>作为了数组排序的新实现。</p>
<h3 id="jie-jue-pai-xu-wen-ding-xing-de-chai-yi">解决排序稳定性的差异</h3>
<p>以上说了这么多，主要是为了讲述各个浏览器对于排序实现的差异，以及解释为什么存在这些差异的一些比较表层的原因。</p>
<p>但是读到这里，读者可能还是会有疑问：如果我的项目就是需要依赖稳定排序，那该怎么办呢？</p>
<h4 id="jie-jue-fang-an">解决方案</h4>
<p>其实解决这个问题的思路比较简单。</p>
<p>浏览器出于不同考虑选择不同排序算法。可能某些偏向于追求极致的性能，某些偏向于提供良好的开发体验，但是有规律可循。</p>
<p>从目前已知的情况来看，所有主流浏览器（包括IE6，7，8）对于数组排序算法的实现基本可以枚举：</p>
<ul>
<li>归并排序 / Timsort</li>
<li>快速排序</li>
</ul>
<p>所以，我们将快速排序经过定制改造，变成稳定排序的是不是就可以了？</p>
<p>一般来说，针对对象数组使用不稳定排序会影响结果。而其他类型数组本身使用稳定排序或不稳定排序的结果是相等的。因此方案大致如下：</p>
<ul>
<li>将待排序数组进行预处理，为每个待排序的对象增加自然序属性，不与对象的其他属性冲突即可。</li>
<li>自定义排序比较方法 compareFn，总是将自然序作为前置判断相等时的第二判断维度。</li>
</ul>
<p>面对归并排序这类实现时由于算法本身就是稳定的，额外增加的自然序比较并不会改变排序结果，所以方案兼容性比较好。</p>
<p>但是涉及修改待排序数组，而且需要开辟额外空间用于存储自然序属性，可想而知v8这类引擎应该不会采用类似手段。不过作为开发者自行定制的排序方案是可行的。</p>
<h4 id="fang-an-dai-ma-shi-li">方案代码示例</h4>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> INDEX = <span class="built_in">Symbol</span>(<span class="string">'index'</span>);</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getComparer</span>(<span class="params">compare</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">left, right</span>) </span>&#123;</span>
<span class="line">        <span class="keyword">let</span> result = compare(left, right);</span>
<span class="line"></span>
<span class="line">        <span class="keyword">return</span> result === <span class="number">0</span> ? left[INDEX] - right[INDEX] : result;</span>
<span class="line">    &#125;;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">array, compare</span>) </span>&#123;</span>
<span class="line">    array = array.map(</span>
<span class="line">        <span class="function">(<span class="params">item, index</span>) =&gt;</span> &#123;</span>
<span class="line">            <span class="keyword">if</span> (<span class="keyword">typeof</span> item === <span class="string">'object'</span>) &#123;</span>
<span class="line">                item[INDEX] = index;</span>
<span class="line">            &#125;</span>
<span class="line"></span>
<span class="line">            <span class="keyword">return</span> item;</span>
<span class="line">        &#125;</span>
<span class="line">    );</span>
<span class="line"></span>
<span class="line">    <span class="keyword">return</span> array.sort(getComparer(compare));</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>以上只是一个简单的满足稳定排序的算法改造示例。</p>
<p>之所以说简单，是因为实际生产环境中作为数组输入的数据结构冗杂，需要根据实际情况判断是否需要进行更多样的排序前类型检测。</p>
<h2 id="xuan-ze-pai-xu-suan-fa-de-can-kao-fang-fa">选择排序算法的参考方法：</h2>
<p>影响排序的因素有很多，平均时间复杂度低的算法并不一定就是最优的。相反，有时平均时间复杂度高的算法可能更适合某些特殊情况。同时，选择算法时还得考虑它的可读性，以利于软件的维护。一般而言，需要考虑的因素有以下四点：</p>
<ol>
<li>
<p>待排序的记录数目 n 的大小；</p>
</li>
<li>
<p>记录本身数据量的大小，也就是记录中除关键字外的其他信息量的大小；</p>
</li>
<li>
<p>关键字的结构及其分布情况；</p>
</li>
<li>
<p>对排序稳定性的要求。</p>
</li>
</ol>
<p>设待排序元素的个数为 n。选择的大致方案如下：</p>
<ol>
<li>当 n 较大，则应采用时间复杂度为 $O(nlogn)$ 的排序方法：快速排序、堆排序或归并排序。</li>
</ol>
<ul>
<li>快速排序：是目前基于比较的内部排序中被认为是最好的方法，当待排序的关键字是随机分布时，快速排序的平均时间最短；</li>
<li>堆排序：如果内存空间允许且要求稳定性的，</li>
<li>归并排序：它有一定数量的数据移动，所以我们可能过与插入排序组合，先获得一定长度的序列，然后再合并，在效率上将有所提高。</li>
</ul>
<ol start="2">
<li>
<p>当 n 较大，内存空间允许，且要求稳定性则选择归并排序</p>
</li>
<li>
<p>当 n 较小，可采用直接插入或直接选择排序。</p>
</li>
</ol>
<p>直接插入排序：当元素分布有序，直接插入排序将大大减少比较次数和移动记录的次数。</p>
<p>直接选择排序：元素分布有序，如果不要求稳定性，选择直接选择排序</p>
<ol start="4">
<li>
<p>一般不使用或不直接使用传统的冒泡排序。</p>
</li>
<li>
<p>基数排序：
它是一种稳定的排序算法，但有一定的局限性，最好满足：</p>
</li>
</ol>
<ul>
<li>关键字可分解。</li>
<li>记录的关键字位数较少，如果密集更好</li>
<li>如果是数字时，最好是无符号的，否则将增加相应的映射复杂度，可先将其正负分开排序。</li>
</ul>
<h2 id="can-kao-zi-liao-amp-tuo-zhan-yue-du">参考资料 &amp; 拓展阅读</h2>
<p><a href="http://web.jobbole.com/87968/" target="_blank" rel="external">十大经典排序算法</a></p>
<p><a href="http://efe.baidu.com/blog/talk-about-sort-in-front-end/" target="_blank" rel="external">聊聊前端排序的那些事</a></p>
<p><a href="https://www.h5jun.com/post/array-shuffle.html" target="_blank" rel="external">Array.prototype.sort 随机排列数组的错误实现</a></p>
<h2 id="fan-wai">番外</h2>
<h3 id="shui-pai-xu-sleep-sort">睡排序（Sleep sort)</h3>
<p>复杂度：O(WTF)</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> numbers = [<span class="number">8</span>, <span class="number">32</span>, <span class="number">49</span>, <span class="number">4</span>, <span class="number">111</span>, <span class="number">999</span>];</span>
<span class="line"></span>
<span class="line">numbers.forEach(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</span>
<span class="line">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;<span class="built_in">console</span>.log(item)&#125;,item);</span>
<span class="line">&#125;)</span>
</pre></td></tr></table></figure>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F" target="_blank" rel="external">冒泡排序概念</a> <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
<li id="fn2" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F" target="_blank" rel="external">选择排序概念</a> <a href="#fnref2" class="footnote-backref">↩</a></p>
</li>
<li id="fn3" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F" target="_blank" rel="external">插入排序概念</a> <a href="#fnref3" class="footnote-backref">↩</a></p>
</li>
<li id="fn4" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F" target="_blank" rel="external">希尔排序概念</a> <a href="#fnref4" class="footnote-backref">↩</a></p>
</li>
<li id="fn5" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F" target="_blank" rel="external">归并排序概念</a> <a href="#fnref5" class="footnote-backref">↩</a></p>
</li>
<li id="fn6" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F" target="_blank" rel="external">快速排序概念</a> <a href="#fnref6" class="footnote-backref">↩</a></p>
</li>
<li id="fn7" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F" target="_blank" rel="external">堆排序概念</a> <a href="#fnref7" class="footnote-backref">↩</a></p>
</li>
<li id="fn8" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F" target="_blank" rel="external">计数排序概念</a> <a href="#fnref8" class="footnote-backref">↩</a></p>
</li>
<li id="fn9" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E6%A1%B6%E6%8E%92%E5%BA%8F" target="_blank" rel="external">桶排序概念</a> <a href="#fnref9" class="footnote-backref">↩</a></p>
</li>
<li id="fn10" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F" target="_blank" rel="external">基数排序概念</a> <a href="#fnref10" class="footnote-backref">↩</a></p>
</li>
<li id="fn11" class="footnote-item"><p><a href="http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.11" target="_blank" rel="external">Array.prototype.sort(compareFn)</a> <a href="#fnref11" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;qian-yan&quot;&gt;前言&lt;/h2&gt;
&lt;p&gt;关于排序算法的有关文章已经很多了，然而网络上用 Javascript 语言来作为示例并详实介绍的文章貌似还是不太多。这里主要是我来尝试自己针对网上各式的排序算法进行一份详实的个人总结，从而温故知新。&lt;/p&gt;
&lt;h2 id=
    
    </summary>
    
      <category term="算法" scheme="http://qcyoung.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="前端知识" scheme="http://qcyoung.com/tags/%E5%89%8D%E7%AB%AF%E7%9F%A5%E8%AF%86/"/>
    
      <category term="题目" scheme="http://qcyoung.com/tags/%E9%A2%98%E7%9B%AE/"/>
    
      <category term="算法" scheme="http://qcyoung.com/tags/%E7%AE%97%E6%B3%95/"/>
    
      <category term="浏览器" scheme="http://qcyoung.com/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript 排序算法汇总</title>
    <link href="http://qcyoung.com/2016/12/18/JavaScript%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/"/>
    <id>http://qcyoung.com/2016/12/18/JavaScript 排序算法汇总/</id>
    <published>2016-12-18T10:40:02.000Z</published>
    <updated>2018-11-25T09:09:09.200Z</updated>
    
    <content type="html"><![CDATA[<h2 id="qian-yan">前言</h2>
<p>关于排序算法的有关文章已经很多了，然而网络上用 Javascript 语言来作为示例并详实介绍的文章貌似还是不太多。这里主要是我来尝试自己针对网上各式的排序算法进行一份详实的个人总结，从而温故知新。</p>
<h2 id="ji-ben-gai-nian">基本概念</h2>
<h3 id="suan-fa-you-lie-ping-jie-zhu-yu">算法优劣评价术语</h3>
<h4 id="wen-ding-xing">稳定性</h4>
<ul>
<li>稳定：如果 <code>a</code> 原本在 <code>b</code> 前面，而 <code>a = b</code>，排序之后 <code>a</code> 仍然在 <code>b</code> 的前面；</li>
<li>不稳定：如果 <code>a</code> 原本在 <code>b</code> 的前面，而 <code>a = b</code>，排序之后 <code>a</code> 可能会出现在 <code>b</code> 的后面；</li>
</ul>
<h4 id="pai-xu-fang-shi">排序方式</h4>
<ul>
<li>内排序：所有排序操作都在内存中完成，占用常数内存，不占用额外内存。</li>
<li>外排序：由于数据太大，因此把数据放在磁盘中，而排序通过磁盘和内存的数据传输才能进行，占用额外内存。</li>
</ul>
<h4 id="fu-za-du">复杂度</h4>
<ul>
<li>时间复杂度: 一个算法执行所耗费的时间。</li>
<li>空间复杂度: 运行完一个程序所需内存的大小。</li>
</ul>
<h4 id="pai-xu-suan-fa-tu-pian-zong-jie">排序算法图片总结</h4>
<p>名词解释：
<code>n</code> : 数据规模
<code>k</code> : 桶的个数</p>
<table>
<thead>
<tr>
<th style="text-align:left">排序算法</th>
<th style="text-align:center">平均时间复杂度</th>
<th style="text-align:center">最好情况</th>
<th style="text-align:center">最坏情况</th>
<th style="text-align:center">空间复杂度</th>
<th style="text-align:center">排序方式</th>
<th style="text-align:center">稳定性</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">冒泡排序</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">选择排序</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">插入排序</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">希尔排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n(\log_2^2 n))$</td>
<td style="text-align:center">$O(n(\log_2^2 n))$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">归并排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">快速排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(\log n)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">堆排序</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(n\log n)$</td>
<td style="text-align:center">$O(1)$</td>
<td style="text-align:center">内排序</td>
<td style="text-align:center">不稳定</td>
</tr>
<tr>
<td style="text-align:left">计数排序</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(k)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">桶排序</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">$O(n^2)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
<tr>
<td style="text-align:left">基数排序</td>
<td style="text-align:center">$O(n × k)$</td>
<td style="text-align:center">$O(n × k)$</td>
<td style="text-align:center">$O(n × k)$</td>
<td style="text-align:center">$O(n + k)$</td>
<td style="text-align:center">外排序</td>
<td style="text-align:center">稳定</td>
</tr>
</tbody>
</table>
<p>从分类上来讲：</p>
<table>
    <tr>
        <td rowspan="5">排序算法</td>
        <td><b>交换排序</b></td>
        <td>冒泡排序</td>
        <td>快速排序</td>
    </tr>
    <tr>
        <td><b>选择排序</b></td>
        <td>选择排序</td>
        <td>堆排序</td>
    </tr>
    <tr>
        <td><b>插入排序</b></td>
        <td>插入排序</td>
        <td>希尔排序</td>
    </tr>
    <tr>
        <td><b>归并排序</b></td>
        <td>归并排序</td>
    </tr>
    <tr>
        <td><b>分布排序</b></td>
        <td>计数排序</td>
        <td>桶排序</td>
        <td>基数排序</td>
    </tr>
</table>
<h2 id="mou-pao-pai-xu-bubble-sort-sup-class-footnote-ref-a-href-fn-1-id-fnref-1-1-a-sup">冒泡排序（Bubble Sort）<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></h2>
<h3 id="suan-fa-yuan-li">算法原理</h3>
<blockquote>
<p>冒泡排序（Bubble Sort）是一种简单的排序算法。它重复地走访要排序的数列，一次比较两个元素，如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换时，此时该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。</p>
</blockquote>
<p>图解如下:</p>
<p><img src="http://img.blog.csdn.net/20160916160748389" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>比较相邻的元素。如果第一个比第二个大，就交换它们两个；</li>
<li>对每一对相邻元素作同样的工作，从开始第一对到结尾的最后一对，这样在最后的元素应该会是最大的数；</li>
<li>针对所有的元素重复以上的步骤，除了最后一个；</li>
<li>重复步骤 $1~3$，直到排序完成。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> bubbleSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> len = arr.length;</span>
<span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>;i &lt; len;i++)&#123;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = <span class="number">0</span>;j &lt; len - <span class="number">1</span> - i;j++)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[j] &gt; arr[j+<span class="number">1</span>])&#123;</span>
<span class="line">        <span class="keyword">let</span> temp = arr[j+<span class="number">1</span>];</span>
<span class="line">        arr[j+<span class="number">1</span>] = arr[j]</span>
<span class="line">        arr[j] = temp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> arr = [<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.log(bubbleSort(arr)); <span class="comment">//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]</span></span>
</pre></td></tr></table></figure>
<blockquote>
<p>改进冒泡排序： 我们设置一个标志性变量 <code>pos</code>，用于记录每趟排序中最后一次进行交换的位置。由于 <code>pos</code> 位置之后的元素均已交换到位，故在进行下一趟排序时只要扫描到 <code>pos</code> 位置即可。 这样的优化方式可以在最好情况下把复杂度降到 $O(n)$。</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> bubbleSort2 = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> i = arr.length - <span class="number">1</span>;</span>
<span class="line">  <span class="keyword">while</span>(i &gt; <span class="number">0</span>)&#123;</span>
<span class="line">    <span class="keyword">let</span> pos = <span class="number">0</span>;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; i; j++)&#123;</span>
<span class="line">      <span class="keyword">if</span> (arr[j] &gt; arr[j+<span class="number">1</span>])&#123;</span>
<span class="line">        pos = j; <span class="comment">//记录交换的位置</span></span>
<span class="line">        <span class="keyword">let</span> tmp = arr[j];</span>
<span class="line">        arr[j] = arr[j+<span class="number">1</span>];</span>
<span class="line">        arr[j+<span class="number">1</span>] = tmp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    i = pos; <span class="comment">//为下一趟排序作准备</span></span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<blockquote>
<p>另外传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值，我们可以考虑利用在每趟排序中进行正向和反向两遍冒泡的方法来一次得到两个最终值(最大者和最小者)，从而继续优化使排序趟数几乎减少一半。（这就是<a href="https://zh.wikipedia.org/wiki/%E9%B8%A1%E5%B0%BE%E9%85%92%E6%8E%92%E5%BA%8F" target="_blank" rel="external">鸡尾酒排序</a>）</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cooktailSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> min = <span class="number">0</span>;</span>
<span class="line">  <span class="keyword">let</span> max = arr.length - <span class="number">1</span>;</span>
<span class="line">  <span class="keyword">while</span>(min &lt; max)&#123;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = min;j &lt; max;j++)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[j] &gt; arr[j+<span class="number">1</span>])&#123;</span>
<span class="line">        <span class="keyword">let</span> tmp = arr[j];</span>
<span class="line">        arr[j] = arr[j+<span class="number">1</span>];</span>
<span class="line">        arr[j+<span class="number">1</span>] = tmp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    -- max;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = max;j &gt; min;j--)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[j] &lt; arr[j<span class="number">-1</span>])&#123;</span>
<span class="line">        <span class="keyword">let</span> tmp = arr[j]</span>
<span class="line">        arr[j] = arr[j<span class="number">-1</span>];</span>
<span class="line">        arr[j<span class="number">-1</span>] = tmp;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    ++ min;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi">算法分析</h3>
<p>冒泡排序对有 $n$ 个元素的项目平均需要 $O(n^2)$ 次比较次数，它可以原地排序，并且是能简单实现的几种排序算法之一，但是它对于少数元素之外的数列排序是很没有效率的。</p>
<ul>
<li>最佳情况： $T(n) = O(n)$</li>
<li>最差情况： $T(n) = O(n^2)$</li>
<li>平均情况： $T(n) = O(n^2)$</li>
</ul>
<h2 id="xuan-ze-pai-xu-selection-sort-sup-class-footnote-ref-a-href-fn-2-id-fnref-2-2-a-sup">选择排序（Selection sort）<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></h2>
<h3 id="suan-fa-yuan-li-1">算法原理</h3>
<blockquote>
<p>选择排序（Selection sort）是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置，然后，再从剩余未排序元素中继续寻找最小（大）元素，然后放到已排序序列的末尾。以此类推，直到所有元素均排序完毕。</p>
</blockquote>
<p>图解如下:</p>
<p><img src="http://img.blog.csdn.net/20160916164754013" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-1">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>$n$ 个记录的直接选择排序可经过 $n - 1$ 趟直接选择排序得到有序结果。具体算法描述如下：</p>
<ol>
<li>初始状态：无序区为 <code>R[1 ... n]</code>，有序区为空；</li>
<li>第 $i$ 趟排序$(i = 1, 2, 3 ... n - 1)$开始时，当前有序区和无序区分别为 $R[1 ... i - 1]$ 和 $R(i ... n)$。该趟排序从当前无序区中选出关键字最小的记录 $R[k]$，将它与无序区的第 1 个记录 $R$ 交换，使 $R[1 ... i]$ 和 $R[i + 1 ... n]$ 分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区；</li>
<li>$n - 1$ 趟结束后，数组有序化。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> SelectionSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> len = arr.length;</span>
<span class="line">  <span class="keyword">let</span> minIndex, tmp;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'选择排序耗时'</span>);</span>
<span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>;i &lt; len - <span class="number">1</span>; i++)&#123;</span>
<span class="line">    minIndex = i;</span>
<span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> j = i + <span class="number">1</span>;j &lt; len;j++)&#123;</span>
<span class="line">      <span class="keyword">if</span>(arr[minIndex] &gt; arr[j])&#123;</span>
<span class="line">        minIndex = j;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    tmp = arr[i];</span>
<span class="line">    arr[i] = arr[minIndex];</span>
<span class="line">    arr[minIndex] = tmp;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'选择排序耗时'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-1">算法分析</h3>
<p>选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上，则它不会被移动。选择排序每次交换一对元素，它们当中至少有一个将被移到其最终位置上，因此对 $n$ 个元素的表进行排序总共进行至多 $n - 1$ 次交换。在所有的<em>完全依靠交换</em>去移动元素的排序方法中，选择排序属于非常好的一种。
但原地操作几乎是选择排序的唯一优点，当空间复杂度要求较高时，可以考虑选择排序；实际适用的场合非常罕见。</p>
<ul>
<li>最佳情况： $T(n) = O(n^2)$</li>
<li>最差情况： $T(n) = O(n^2)$</li>
<li>平均情况： $T(n) = O(n^2)$</li>
</ul>
<h2 id="cha-ru-pai-xu-insertion-sort-sup-class-footnote-ref-a-href-fn-3-id-fnref-3-3-a-sup">插入排序（Insertion sort）<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></h2>
<h3 id="suan-fa-yuan-li-2">算法原理</h3>
<blockquote>
<p>插入排序（Insertion Sort）是一种简单直观的排序算法。它的工作原理是通过构建有序序列，对于未排序数据，在已排序序列中从后向前s扫描，找到相应位置并插入。插入排序在实现上，通常采用 in-place 排序（即只需用到 $O(1)$ 的额外空间的排序），因而在从后向前的扫描过程中，需要反复把已排序元素逐步向后挪位，为最新元素提供插入空间。</p>
</blockquote>
<p>图解如下:</p>
<p><img src="http://img.blog.csdn.net/20160916173802597" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-2">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>一般来说，插入排序都采用in-place在数组上实现。具体算法描述如下：</p>
<ol>
<li>从第一个元素开始，该元素可以认为已经被排序</li>
<li>取出下一个元素，在已经排序的元素序列中从后向前扫描</li>
<li>如果该元素（已排序）大于新元素，将该元素移到下一位置</li>
<li>重复步骤 3，直到找到已排序的元素小于或者等于新元素的位置</li>
<li>将新元素插入到该位置后</li>
<li>重复步骤 $2~5$</li>
</ol>
<p>如果<em>比较操作</em>的代价比<em>交换操作大</em>的话，可以采用<em>二分查找法</em>来减少比较操作的数目。该算法可以认为是插入排序的一个变种，称为<a href=""><em>二分查找插入排序</em></a>。</p>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> insertionSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">let</span> len = arr.length;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'插入排序耗时'</span>);</span>
<span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>;i &lt; len; i++)&#123;</span>
<span class="line">    <span class="keyword">let</span> key = arr[i]; </span>
<span class="line">    <span class="keyword">let</span> j = i - <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">while</span>(j &gt;= <span class="number">0</span> &amp;&amp; arr[j] &gt; key)&#123;</span>
<span class="line">      arr[j+<span class="number">1</span>] = arr[j];</span>
<span class="line">      j--;</span>
<span class="line">    &#125;</span>
<span class="line">    arr[j+<span class="number">1</span>] = key;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'插入排序耗时'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<blockquote>
<p>改进插入排序：查找插入位置时使用二分查找的方式。</p>
</blockquote>
<p>具体思路如下：</p>
<ol>
<li>在插入第 $i$ 个元素时，对前面的 $0 ~ i-1$ 元素进行折半。</li>
<li>与前面的 $0 ~ i-1$ 个元素中间的元素进行比较，如果小，则对前半再进行折半，否则对后半进行折半。</li>
<li>直到 left &gt; right，再把第 $i$ 个元素前 1 位和目标位置间的所有元素后移，把第 $i$ 个元素放在目标位置上。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> binaryInsertionSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'二分插入排序耗时：'</span>);</span>
<span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; arr.length; i++)&#123;</span>
<span class="line">    <span class="keyword">let</span> key = arr[i], left = <span class="number">0</span>, right = i - <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">while</span> (left &lt;= right)&#123;</span>
<span class="line">      <span class="keyword">let</span> middle = <span class="built_in">parseInt</span>((left + right) / <span class="number">2</span>);</span>
<span class="line">      <span class="keyword">if</span> (key &lt; arr[middle])&#123;</span>
<span class="line">        right = middle - <span class="number">1</span>;</span>
<span class="line">      &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">        left = middle + <span class="number">1</span>;</span>
<span class="line">      &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = i - <span class="number">1</span>; j &gt;= left; j--)&#123;</span>
<span class="line">      arr[j + <span class="number">1</span>] = arr[j];</span>
<span class="line">    &#125;</span>
<span class="line">    arr[left] = key;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'二分插入排序耗时：'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-2">算法分析</h3>
<ul>
<li>最佳情况： $T(n) = O(n)$</li>
<li>最差情况： $T(n) = O(n^2)$</li>
<li>平均情况： $T(n) = O(n^2)$</li>
</ul>
<h2 id="xi-er-pai-xu-shell-sort-sup-class-footnote-ref-a-href-fn-4-id-fnref-4-4-a-sup">希尔排序（Shell sort）<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup></h2>
<h3 id="suan-fa-yuan-li-3">算法原理</h3>
<blockquote>
<p>希尔排序，也称递减增量排序算法，是插入排序的一种更高效的改进版本。它是非稳定排序算法。</p>
</blockquote>
<p>希尔排序基于插入排序的以下两点性质提出了改进方法：</p>
<ol>
<li>插入排序在对几乎已经排好序的数据操作时，效率高，即可以达到线性排序的效率。</li>
<li>但插入排序一般来说是低效的，因为插入排序每次只能将数据移动一位。</li>
</ol>
<p>它与插入排序的不同之处在于，它会优先比较距离较远的元素。因此希尔排序又叫缩小增量排序。</p>
<p>图解如下:</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/JavaScript%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/shellsort.jpeg" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-3">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序，具体算法描述：</p>
<ol>
<li>选择一个增量序列$t1，t2，…，tk$，其中 $ti &gt; tj(i &gt; j)$，$tk = 1$；</li>
<li>按增量序列个数 $k$，对序列进行 $k$ 趟排序；</li>
<li>每趟排序，根据对应的增量 $ti$，将待排序列分割成若干长度为 $m$ 的子序列，分别对各子表进行直接插入排序。仅增量因子为 1 时，整个序列作为一个表来处理，表长度即为整个序列的长度。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> shellSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">  <span class="built_in">console</span>.time(<span class="string">'希尔排序耗时:'</span>);</span>
<span class="line">  <span class="keyword">let</span> len = arr.length,temp,gap = <span class="number">1</span>;</span>
<span class="line">  <span class="keyword">while</span>(gap &lt; len / <span class="number">5</span>) &#123; <span class="comment">// 动态定义间隔序列步长为 5</span></span>
<span class="line">      gap = gap * <span class="number">5</span> + <span class="number">1</span>;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="keyword">for</span> (gap; gap &gt; <span class="number">0</span>; gap = <span class="built_in">Math</span>.floor(gap / <span class="number">5</span>)) &#123;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = gap; i &lt; len; i++) &#123;</span>
<span class="line">      temp = arr[i];</span>
<span class="line">      <span class="keyword">let</span> j;</span>
<span class="line">      <span class="keyword">for</span> (j = i - gap; j &gt;= <span class="number">0</span> &amp;&amp; arr[j] &gt; temp; j -= gap) &#123;</span>
<span class="line">        arr[j + gap] = arr[j];</span>
<span class="line">      &#125;</span>
<span class="line">      arr[j + gap] = temp;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="built_in">console</span>.timeEnd(<span class="string">'希尔排序耗时:'</span>);</span>
<span class="line">  <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-3">算法分析</h3>
<ul>
<li>最佳情况： $T_(n) = O_(n\log_2 n)$</li>
<li>最差情况： $T_(n) = O_(n\log_2 n)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="gui-bing-pai-xu-merge-sort-sup-class-footnote-ref-a-href-fn-5-id-fnref-5-5-a-sup">归并排序（Merge sort）<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup></h2>
<h3 id="suan-fa-yuan-li-4">算法原理</h3>
<blockquote>
<p>归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法（Divide and Conquer）的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并，得到完全有序的序列；即先使每个子序列有序，再使子序列段间有序。若将两个有序表合并成一个有序表，称为二路归并。</p>
</blockquote>
<p>图解如下：</p>
<p><img src="http://img.blog.csdn.net/20160917001326254" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-4">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>把长度为 n 的输入序列分成两个长度为 n/2 的子序列；</li>
<li>对这两个子序列分别采用归并排序；</li>
<li>将两个排序好的子序列合并成一个最终的排序序列。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span>
<span class="line"><span class="keyword">const</span> mergeSort = <span class="function">(<span class="params">arr</span>) =&gt;</span>&#123;  <span class="comment">//采用自上而下的递归方法</span></span>
<span class="line">    <span class="keyword">let</span> len = arr.length;</span>
<span class="line">    <span class="keyword">if</span>(len &lt; <span class="number">2</span>) &#123;</span>
<span class="line">        <span class="keyword">return</span> arr;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">let</span> middle = <span class="built_in">Math</span>.floor(len / <span class="number">2</span>),</span>
<span class="line">        left = arr.slice(<span class="number">0</span>, middle),</span>
<span class="line">        right = arr.slice(middle);</span>
<span class="line">    <span class="keyword">return</span> merge(mergeSort(left), mergeSort(right));</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> merge = <span class="function">(<span class="params">left, right</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">let</span> result = [];</span>
<span class="line">    <span class="keyword">while</span> (left.length &amp;&amp; right.length) &#123;</span>
<span class="line">        <span class="keyword">if</span> (left[<span class="number">0</span>] &lt;= right[<span class="number">0</span>]) &#123;</span>
<span class="line">            result.push(left.shift());</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            result.push(right.shift());</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">while</span> (left.length)</span>
<span class="line">        result.push(left.shift());</span>
<span class="line">    <span class="keyword">while</span> (right.length)</span>
<span class="line">        result.push(right.shift());</span>
<span class="line">    <span class="keyword">return</span> result;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="keyword">let</span> arr=[<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.time(<span class="string">'归并排序耗时'</span>);</span>
<span class="line"><span class="built_in">console</span>.log(mergeSort(arr));</span>
<span class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'归并排序耗时'</span>);</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-4">算法分析</h3>
<p>和选择排序一样，归并排序的性能不受输入数据的影响，但它的表现比选择排序要好，因为它始终都是 $O_(n\log n)$ 的时间复杂度。但代价是需要额外的内存空间。</p>
<ul>
<li>最佳情况： $T_(n) = O_(n)$</li>
<li>最差情况： $T_(n) = O_(n\log n)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="kuai-su-pai-xu-quick-sort-sup-class-footnote-ref-a-href-fn-6-id-fnref-6-6-a-sup">快速排序（Quick sort）<sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup></h2>
<h3 id="suan-fa-yuan-li-5">算法原理</h3>
<blockquote>
<p>通过一趟排序将待排记录分隔成独立的两部分，其中一部分记录的关键字均比另一部分的关键字小，则可分别对这两部分记录继续进行排序，以达到整个序列有序。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917003004906" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-5">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<p>快速排序使用分治法来把一个串（list）分为两个子串（sub-lists）。具体算法描述如下：</p>
<ol>
<li>从数列中挑出一个元素，称为 “基准”（pivot）；</li>
<li>重新排序数列，所有元素比基准值小的摆放在基准前面，所有元素比基准值大的摆在基准的后面（相同的数可以到任一边）。在这个分区退出之后，该基准就处于数列的中间位置。这个称为分区（partition）操作；</li>
<li>递归地（recursive）把小于基准值元素的子数列和大于基准值元素的子数列排序。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span>
<span class="line"><span class="keyword">const</span> quickSort = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">if</span> (arr.length &lt;= <span class="number">1</span>) &#123; <span class="keyword">return</span> arr; &#125;</span>
<span class="line">    <span class="keyword">let</span> pivotIndex = <span class="built_in">Math</span>.floor(arr.length / <span class="number">2</span>);</span>
<span class="line">    <span class="keyword">let</span> pivot = arr.splice(pivotIndex, <span class="number">1</span>)[<span class="number">0</span>];</span>
<span class="line">    <span class="keyword">let</span> left = [];</span>
<span class="line">    <span class="keyword">let</span> right = [];</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++)&#123;</span>
<span class="line">    　　<span class="keyword">if</span> (arr[i] &lt; pivot) &#123;</span>
<span class="line">    　　　　left.push(arr[i]);</span>
<span class="line">    　　&#125; <span class="keyword">else</span> &#123;</span>
<span class="line">    　　　　right.push(arr[i]);</span>
<span class="line">    　　&#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">return</span> quickSort(left).concat([pivot], quickSort(right));</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">let</span> arr=[<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.time(<span class="string">'快速排序耗时'</span>);</span>
<span class="line"><span class="built_in">console</span>.log(quickSort(arr));</span>
<span class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'快速排序耗时'</span>);</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-5">算法分析</h3>
<ul>
<li>最佳情况： $T_(n) = O_(n\log n)$</li>
<li>最差情况： $T_(n) = O_(n^2)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="dui-pai-xu-heap-sort-sup-class-footnote-ref-a-href-fn-7-id-fnref-7-7-a-sup">堆排序（Heap sort）<sup class="footnote-ref"><a href="#fn7" id="fnref7">[7]</a></sup></h2>
<h3 id="suan-fa-yuan-li-6">算法原理</h3>
<blockquote>
<p>堆排序可以说是一种利用堆的概念来排序的选择排序。它利用堆这种数据结构所设计。堆积是一个近似完全二叉树的结构，并同时满足堆积的性质：即子结点的键值或索引总是小于（或者大于）它的父节点。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917105502853" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-6">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>将初始待排序关键字序列 $(R1,R2…,Rn)$ 构建成大顶堆，此堆为初始的无序区；</li>
<li>将堆顶元素 $R[1]$ 与最后一个元素 $R[n]$ 交换，此时得到新的无序区 $(R1,R2,……,Rn-1)$ 和新的有序区 $(Rn)$ ，且满足 $R[1,2…,n-1] &lt;= R[n]$；</li>
<li>由于交换后新的堆顶 $R[1]$ 可能违反堆的性质，因此需要对当前无序区 $(R1,R2,……,Rn-1)$ 调整为新堆，然后再次将 $R[1]$ 与无序区最后一个元素交换，得到新的无序区 $(R1,R2…,Rn-2)$ 和新的有序区 $(Rn-1,Rn)$。不断重复此过程直到有序区的元素个数为 n-1，则整个排序过程完成。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span>
<span class="line"><span class="keyword">const</span> heapSort = <span class="function">(<span class="params">array</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'堆排序耗时'</span>);</span>
<span class="line">    <span class="comment">//建堆</span></span>
<span class="line">    <span class="keyword">let</span> heapSize = array.length, temp;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="built_in">Math</span>.floor(heapSize / <span class="number">2</span>) - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span>
<span class="line">        heapify(array, i, heapSize);</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="comment">//堆排序</span></span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = heapSize - <span class="number">1</span>; j &gt;= <span class="number">1</span>; j--) &#123;</span>
<span class="line">        temp = array[<span class="number">0</span>];</span>
<span class="line">        array[<span class="number">0</span>] = array[j];</span>
<span class="line">        array[j] = temp;</span>
<span class="line">        heapify(array, <span class="number">0</span>, --heapSize);</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'堆排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> array;</span>
<span class="line">&#125;</span>
<span class="line"><span class="comment">/*方法说明：维护堆的性质</span>
<span class="line">@param  arr 数组</span>
<span class="line">@param  x   数组下标</span>
<span class="line">@param  len 堆大小*/</span></span>
<span class="line"><span class="keyword">const</span> heapify = <span class="function">(<span class="params">arr, x, len</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">let</span> l = <span class="number">2</span> * x + <span class="number">1</span>, r = <span class="number">2</span> * x + <span class="number">2</span>, largest = x, temp;</span>
<span class="line">    <span class="keyword">if</span> (l &lt; len &amp;&amp; arr[l] &gt; arr[largest]) &#123;</span>
<span class="line">        largest = l;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">if</span> (r &lt; len &amp;&amp; arr[r] &gt; arr[largest]) &#123;</span>
<span class="line">        largest = r;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">if</span> (largest != x) &#123;</span>
<span class="line">        temp = arr[x];</span>
<span class="line">        arr[x] = arr[largest];</span>
<span class="line">        arr[largest] = temp;</span>
<span class="line">        heapify(arr, largest, len);</span>
<span class="line">    &#125;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">let</span> arr=[<span class="number">91</span>,<span class="number">60</span>,<span class="number">96</span>,<span class="number">13</span>,<span class="number">35</span>,<span class="number">65</span>,<span class="number">46</span>,<span class="number">65</span>,<span class="number">10</span>,<span class="number">30</span>,<span class="number">20</span>,<span class="number">31</span>,<span class="number">77</span>,<span class="number">81</span>,<span class="number">22</span>];</span>
<span class="line"><span class="built_in">console</span>.log(heapSort(arr));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-6">算法分析</h3>
<ul>
<li>最佳情况： $T_(n) = O_(n\log n)$</li>
<li>最差情况： $T_(n) = O_(n\log n)$</li>
<li>平均情况： $T_(n) = O_(n\log n)$</li>
</ul>
<h2 id="ji-shu-pai-xu-counting-sort-sup-class-footnote-ref-a-href-fn-8-id-fnref-8-8-a-sup">计数排序（Counting sort）<sup class="footnote-ref"><a href="#fn8" id="fnref8">[8]</a></sup></h2>
<h3 id="suan-fa-yuan-li-7">算法原理</h3>
<blockquote>
<p>计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组 C，其中第 i 个元素是待排序数组 A 中值等于 i 的元素的个数。然后根据数组 C 来将 A 中的元素排到正确的位置。它只能对整数进行排序。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917110641479" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-7">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>找出待排序的数组中最大和最小的元素；</li>
<li>统计数组中每个值为 i 的元素出现的次数，存入数组 C 的第 i 项；</li>
<li>对所有的计数累加（从 C 中的第一个元素开始，每一项和前一项相加）；</li>
<li>反向填充目标数组：将每个元素 i 放在新数组的第 $C(i)$ 项，每放一个元素就将 $C(i)$ 减去 1。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span>
<span class="line"><span class="keyword">const</span> countingSort = <span class="function">(<span class="params">array</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">let</span> len = array.length,</span>
<span class="line">        result = [],</span>
<span class="line">        C = [],</span>
<span class="line">        min,max;</span>
<span class="line">        min = max = array[<span class="number">0</span>];</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'计数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; len; i++) &#123;</span>
<span class="line">        min = min &lt;= array[i] ? min : array[i];</span>
<span class="line">        max = max &gt;= array[i] ? max : array[i];</span>
<span class="line">        C[array[i]] = C[array[i]] ? C[array[i]] + <span class="number">1</span> : <span class="number">1</span>;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = min; j &lt; max; j++) &#123;</span>
<span class="line">        C[j + <span class="number">1</span>] = (C[j + <span class="number">1</span>] || <span class="number">0</span>) + (C[j] || <span class="number">0</span>);</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> k = len - <span class="number">1</span>; k &gt;= <span class="number">0</span>; k--) &#123;</span>
<span class="line">        result[C[array[k]] - <span class="number">1</span>] = array[k];</span>
<span class="line">        C[array[k]]--;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'计数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> result;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">let</span> arr = [<span class="number">2</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">8</span>, <span class="number">7</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">7</span>, <span class="number">3</span>, <span class="number">9</span>, <span class="number">8</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">9</span>, <span class="number">2</span>];</span>
<span class="line"><span class="built_in">console</span>.log(countingSort(arr));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-7">算法分析</h3>
<p>计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。
作为一种线性时间复杂度的排序，计数排序要求输入的数据必须是有确定范围的整数。</p>
<p>当输入的元素是 n 个 0 到 k 之间的整数时，它的运行时间是 O(n + k)。计数排序不是比较排序，排序的速度快于任何比较排序算法。由于用来计数的数组 C 的长度取决于待排序数组中数据的范围（等于待排序数组的最大值与最小值的差加上 1），这使得计数排序对于数据范围很大的数组，需要大量时间和内存。</p>
<ul>
<li>最佳情况： $T_(n) = O_(n+k)$</li>
<li>最差情况： $T_(n) = O_(n+k)$</li>
<li>平均情况： $T_(n) = O_(n+k)$</li>
</ul>
<h2 id="tong-pai-xu-bucket-sort-sup-class-footnote-ref-a-href-fn-9-id-fnref-9-9-a-sup">桶排序（Bucket sort）<sup class="footnote-ref"><a href="#fn9" id="fnref9">[9]</a></sup></h2>
<h3 id="suan-fa-yuan-li-8">算法原理</h3>
<blockquote>
<p>工作的原理是将数组分到有限数量的桶里。每个桶再个别排序（有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序）</p>
</blockquote>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/JavaScript%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/bucketsort.jpeg" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-8">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>设置一个定量的数组当作空桶；</li>
<li>遍历输入数据，并且把数据一个一个放到对应的桶里去；</li>
<li>对每个不是空的桶进行排序；</li>
<li>从不是空的桶里把排好序的数据拼接起来。</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span>
<span class="line"><span class="keyword">const</span> bucketSort = <span class="function">(<span class="params">array, num</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">if</span> (array.length &lt;= <span class="number">1</span>) &#123;</span>
<span class="line">        <span class="keyword">return</span> array;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">var</span> len = array.length, buckets = [], result = [], min = max = array[<span class="number">0</span>], regex = <span class="string">'/^[1-9]+[0-9]*$/'</span>, space, n = <span class="number">0</span>;</span>
<span class="line">    num = num || ((num &gt; <span class="number">1</span> &amp;&amp; regex.test(num)) ? num : <span class="number">10</span>);</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'桶排序耗时'</span>);</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i &lt; len; i++) &#123;</span>
<span class="line">        min = min &lt;= array[i] ? min : array[i];</span>
<span class="line">        max = max &gt;= array[i] ? max : array[i];</span>
<span class="line">    &#125;</span>
<span class="line">    space = (max - min + <span class="number">1</span>) / num;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; len; j++) &#123;</span>
<span class="line">        <span class="keyword">var</span> index = <span class="built_in">Math</span>.floor((array[j] - min) / space);</span>
<span class="line">        <span class="keyword">if</span> (buckets[index]) &#123;   <span class="comment">//  非空桶，插入排序</span></span>
<span class="line">            <span class="keyword">var</span> k = buckets[index].length - <span class="number">1</span>;</span>
<span class="line">            <span class="keyword">while</span> (k &gt;= <span class="number">0</span> &amp;&amp; buckets[index][k] &gt; array[j]) &#123;</span>
<span class="line">                buckets[index][k + <span class="number">1</span>] = buckets[index][k];</span>
<span class="line">                k--;</span>
<span class="line">            &#125;</span>
<span class="line">            buckets[index][k + <span class="number">1</span>] = array[j];</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;    <span class="comment">//空桶，初始化</span></span>
<span class="line">            buckets[index] = [];</span>
<span class="line">            buckets[index].push(array[j]);</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="keyword">while</span> (n &lt; num) &#123;</span>
<span class="line">        result = result.concat(buckets[n]);</span>
<span class="line">        n++;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'桶排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> result;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">var</span> arr=[<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.log(bucketSort(arr,<span class="number">4</span>));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-8">算法分析</h3>
<p>桶排序是计数排序的升级版。它利用了函数的映射关系，高效与否的关键就在于这个映射函数的确定。
桶排序最好情况下使用线性时间 $O(n)$，桶排序的时间复杂度，取决与对各个桶之间数据进行排序的时间复杂度，因为其它部分的时间复杂度都为 $O(n)$。很显然，桶划分的越小，各个桶之间的数据越少，排序所用的时间也会越少。但相应的空间消耗就会增大。</p>
<ul>
<li>最佳情况： $T_(n) = O_(n+k)$</li>
<li>最差情况： $T_(n) = O_(n+k)$</li>
<li>平均情况： $T_(n) = O_(n^2)$</li>
</ul>
<h2 id="ji-shu-pai-xu-radix-sort-sup-class-footnote-ref-a-href-fn-10-id-fnref-10-10-a-sup">基数排序（Radix sort）<sup class="footnote-ref"><a href="#fn10" id="fnref10">[10]</a></sup></h2>
<h3 id="suan-fa-yuan-li-9">算法原理</h3>
<blockquote>
<p>基数排序是按照低位先排序，然后收集；再按照高位排序，然后再收集；依次类推，直到最高位。有时候有些属性是有优先级顺序的，先按低优先级排序，再按高优先级排序。最后的次序就是高优先级高的在前，高优先级相同的低优先级高的在前。基数排序基于分别排序，分别收集，所以是稳定的。</p>
</blockquote>
<p><img src="http://img.blog.csdn.net/20160917123313659" alt=""></p>
<h3 id="suan-fa-miao-shu-yu-shi-xian-9">算法描述与实现</h3>
<ul>
<li>具体算法描述如下：</li>
</ul>
<ol>
<li>取得数组中的最大数，并取得位数；</li>
<li>arr 为原始数组，从最低位开始取每个位组成 radix 数组；</li>
<li>对 radix 进行计数排序（利用计数排序适用于小范围数的特点）；</li>
</ol>
<ul>
<li>代码实现如下：</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span>
<span class="line"><span class="keyword">const</span> radixSort = <span class="function">(<span class="params">arr, maxDigit</span>) =&gt;</span> &#123;</span>
<span class="line">    <span class="keyword">var</span> mod = <span class="number">10</span>;</span>
<span class="line">    <span class="keyword">var</span> dev = <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">var</span> counter = [];</span>
<span class="line">    <span class="built_in">console</span>.time(<span class="string">'基数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; maxDigit; i++, dev *= <span class="number">10</span>, mod *= <span class="number">10</span>) &#123;</span>
<span class="line">        <span class="keyword">for</span>(<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; arr.length; j++) &#123;</span>
<span class="line">            <span class="keyword">var</span> bucket = <span class="built_in">parseInt</span>((arr[j] % mod) / dev);</span>
<span class="line">            <span class="keyword">if</span>(counter[bucket]== <span class="literal">null</span>) &#123;</span>
<span class="line">                counter[bucket] = [];</span>
<span class="line">            &#125;</span>
<span class="line">            counter[bucket].push(arr[j]);</span>
<span class="line">        &#125;</span>
<span class="line">        <span class="keyword">var</span> pos = <span class="number">0</span>;</span>
<span class="line">        <span class="keyword">for</span>(<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; counter.length; j++) &#123;</span>
<span class="line">            <span class="keyword">var</span> value = <span class="literal">null</span>;</span>
<span class="line">            <span class="keyword">if</span>(counter[j]!=<span class="literal">null</span>) &#123;</span>
<span class="line">                <span class="keyword">while</span> ((value = counter[j].shift()) != <span class="literal">null</span>) &#123;</span>
<span class="line">                      arr[pos++] = value;</span>
<span class="line">                &#125;</span>
<span class="line">          &#125;</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">'基数排序耗时'</span>);</span>
<span class="line">    <span class="keyword">return</span> arr;</span>
<span class="line">&#125;</span>
<span class="line"><span class="keyword">var</span> arr = [<span class="number">3</span>, <span class="number">44</span>, <span class="number">38</span>, <span class="number">5</span>, <span class="number">47</span>, <span class="number">15</span>, <span class="number">36</span>, <span class="number">26</span>, <span class="number">27</span>, <span class="number">2</span>, <span class="number">46</span>, <span class="number">4</span>, <span class="number">19</span>, <span class="number">50</span>, <span class="number">48</span>];</span>
<span class="line"><span class="built_in">console</span>.log(radixSort(arr,<span class="number">2</span>));</span>
</pre></td></tr></table></figure>
<h3 id="suan-fa-fen-xi-9">算法分析</h3>
<p>基数排序有两种方法：</p>
<p>MSD 从高位开始进行排序
LSD 从低位开始进行排序</p>
<ul>
<li>最佳情况： $T_(n) = O_(n^2)$</li>
<li>最差情况： $T_(n) = O_(n^2)$</li>
<li>平均情况： $T_(n) = O_(n^2)$</li>
</ul>
<p>基数排序 vs 计数排序 vs 桶排序</p>
<p>这三种排序算法都利用了桶的概念，但对桶的使用方法上有明显差异：</p>
<p>基数排序：根据键值的每位数字来分配桶
计数排序：每个桶只存储单一键值
桶排序：每个桶存储一定范围的数值</p>
<p><strong>以上代码可访问 <a href="https://github.com/yangzj1992/Use-Javascript-Solve-Logic-Questions/tree/master/data-structure/Sort" target="_blank" rel="external">GitHub 具体查看</a></strong></p>
<h2 id="js-yuan-sheng-han-shu-zhong-de-pai-xu">JS 原生函数中的排序</h2>
<p>说到前端排序，自然首先会想到 JavaScript 的原生接口 <code>Array.prototype.sort</code></p>
<p>这个接口自 <code>ECMAScript 1st Edition</code> 起就被设计存在。而在规范中关于它的描述是这样的：<sup class="footnote-ref"><a href="#fn11" id="fnref11">[11]</a></sup></p>
<p><code>Array.prototype.sort(compareFn)</code></p>
<blockquote>
<p>The elements of this array are sorted. The sort is not necessarily stable (that is, elements that compare equal do not necessarily remain in their original order). If comparefn is not undefined, it should be a function that accepts two arguments x and y and returns a negative value if x &lt; y, zero if x = y, or a positive value if x &gt; y.</p>
</blockquote>
<p>显然，规范里并没有限定 <code>sort</code> 内部需要用什么排序算法。在这样的背景下，前端排序这件事完全取决于各家浏览器内核的具体实现。</p>
<h3 id="chrome-de-shi-xian">Chrome 的实现</h3>
<p>Chrome 的 JavaScript 引擎是 v8。由于它是开源的，所以可以直接看<a href="https://github.com/v8/v8" target="_blank" rel="external">源代码</a>。</p>
<p>整个 <a href="https://github.com/v8/v8/blob/master/src/js/array.js" target="_blank" rel="external">array.js</a> 都是用 JavaScript 语言实现的。排序方法部分很明显比通常看到的快速排序要复杂得多，但显然核心算法还是快速排序的思想。算法复杂的原因在于 v8 出于性能考虑进行了很多优化。(后续会展开说)</p>
<h3 id="firefox-zhong-de-shi-xian">Firefox中的实现</h3>
<p>暂时无法确定 Firefox 的 JavaScript 引擎即将使用的数组排序算法会是什么。</p>
<p>按照现有的信息，SpiderMoney 内部实现了归并排序。（这里不多做叙述）</p>
<h3 id="microsoft-edge-zhong-de-shi-xian">Microsoft Edge中的实现</h3>
<p>Microsoft Edge 的 JavaScript 引擎 Chakra 的核心部分代码已经于 2016 年初在 Github 开源。</p>
<p>通过<a href="https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Library/JavascriptArray.cpp" target="_blank" rel="external">源代码</a>可以发现，Chakra 的数组排序算法也主要是以快速排序为主。并针对其他具体特殊情况进行具体优化。</p>
<h3 id="pai-xu-de-chai-yi">排序的差异</h3>
<p>如上所述，快速排序是一种不稳定的排序算法，而归并排序是一种稳定的排序算法。由于不同引擎在算法选择上可能存在差异，导致前端如果依赖 <code>Array.prototype.sort</code> 接口实现的 JavaScript 代码，在浏览器中实际执行的排序效果是不一致的。</p>
<p>而排序稳定性的差异其实也只是在特定的场景才会体现出问题；在很多情况下，不稳定的排序也并不会造成什么影响。所以假如实际项目开发中，对于数组的排序没有稳定性的需求，那么看到这里就可以了。</p>
<p>但是若项目要求排序必须是稳定的，那么这些差异的存在将无法满足需求。我们需要为此进行一些额外的工作。</p>
<p>举个例子：</p>
<p>某市的机动车牌照拍卖系统，最终中标的规则为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
</pre></td><td class="code"><pre><span class="line">1. 按价格进行倒排序；</span>
<span class="line">2. 相同价格则按照竞标顺位（即价格提交时间）进行正排序。</span>
</pre></td></tr></table></figure>
<p>排序若是在前端进行，那么采用快速排序的浏览器中显示的中标者很可能是不符合预期的。</p>
<p>此外例如：
<a href="http://www.cnblogs.com/cchust/p/5304594.html" target="_blank" rel="external">MySQL 5.6 的 <code>limit M,N</code> 语句实现</a>
等情况都是不稳定排序的特征。</p>
<h3 id="bei-hou-de-yuan-yin">背后的原因</h3>
<h4 id="chrome-wei-shi-yao-cai-yong-kuai-su-pai-xu">Chrome为什么采用快速排序</h4>
<p>其实这个情况从一开始便存在。</p>
<p>Chrome测试版于<a href="https://zh.wikipedia.org/wiki/Google_Chrome#.E7.99.BC.E5.B8.83" target="_blank" rel="external">2008年9月2日发布</a> (这里附上当时随版本发布的漫画，还是很有意思的<a href="http://blogoscoped.com/google-chrome/" target="_blank" rel="external">Google on Google Chrome - comic book</a>)，然而发布后不久，就有开发者向 Chromium 开发组提交 <a href="https://bugs.chromium.org/p/v8/issues/detail?id=90" target="_blank" rel="external">#90 Bug V8 doesn't stable sort</a> 反馈 v8 的数组排序实现不是稳定排序的。</p>
<p>这个 Bug ISSUE 讨论的时间跨度很大。时至今日，仍然有开发者对 v8 的数组排序的实现提出评论。</p>
<p>同时我们还注意到，该 ISSUE 曾经已被关闭。但是于 2013 年 6 月被开发组成员重新打开，作为 ECMAScript Next 规范讨论的参考。</p>
<p>而 <a href="https://mail.mozilla.org/pipermail/es-discuss/2013-June/031276.html" target="_blank" rel="external">es-discuss</a> 的最后结论是这样的</p>
<blockquote>
<p>It does not change. Stable is a subset of unstable. And vice versa, every unstable algorithm returns a stable result for some inputs. Mark’s point is that requiring “always unstable” has no meaning, no matter what language you chose.</p>
</blockquote>
<p>这也正如本文前段所引用的已定稿 <code>ECMAScript 2015</code> 规范中的描述一样。</p>
<h4 id="shi-dai-te-dian">时代特点</h4>
<p>IMHO，Chrome 发布之初即被报告出这个问题可能是有其特殊的时代特点。</p>
<p>上文已经说到，Chrome 第一版是 2008 年 9 月发布的。根据 <a href="http://gs.statcounter.com/#browser-ww-monthly-200809-200809-bar" target="_blank" rel="external">statcounter</a> 的统计数据，那个时期市场占有率最高的两款浏览器分别是 IE(那时候只有 IE6 和 IE7) 和 Firefox，市场占有率分别达到了 67.16% 和 25.77%。也就是说，两个浏览器加起来的市场占有率超过了 90%。</p>
<p>而根据另一份<a href="http://ofb.net/~sethml/is-sort-stable.html" target="_blank" rel="external">浏览器排序算法稳定性的统计</a>数据显示，这两款超过了 90% 市场占有率的浏览器都采用了稳定的数组排序。所以 Chrome 发布之初被开发者质疑也是合情合理的。</p>
<p>我们从 ISSUE 讨论的过程中，可以大概理解开发组成员对于引擎实现采用快速排序的一些考量。他们认为引擎必须遵守 ECMAScript 规范。由于规范不要求稳定排序的描述，故他们认为 v8 的实现也是完全符合规范的。</p>
<h4 id="xing-neng-kao-lu">性能考虑</h4>
<p>另外，他们认为 v8 设计的一个重要考量在于引擎的性能。</p>
<p><strong>快速排序</strong>相比较于<strong>归并排序</strong>，在整体性能上表现更好：</p>
<ul>
<li>更高的计算效率。快速排序在实际计算机执行环境中比同等时间复杂度的其他排序算法更快（不命中最差组合的情况下）</li>
<li>更低的空间成本。前者仅有 $O(logn)$ 的空间复杂度，相比较后者 $O(n)$ 的空间复杂度在运行时的内存消耗更少</li>
</ul>
<h4 id="v-8-zai-shu-zu-pai-xu-suan-fa-zhong-de-xing-neng-you-hua">v8 在数组排序算法中的性能优化</h4>
<p>既然说 v8 非常看中引擎的性能，那么在数组排序中它做了哪些事呢？</p>
<p>通过阅读源代码，还是粗浅地学习了一些皮毛。</p>
<ul>
<li>混合插入排序
快速排序是分治的思想，将大数组分解，逐层往下递归。但是若递归深度太大，为了维持递归，调用栈的内存资源消耗也会很大。优化不好甚至可能造成栈溢出。</li>
</ul>
<p>目前 v8 的实现是设定一个阈值，对最下层的 10 个及以下长度的小数组使用插入排序。</p>
<p>根据代码注释以及 Wikipedia 中的描述，虽然插入排序的平均时间复杂度为 $O(n^2)$ 差于快速排序的 $O(nlogn)$。但是在运行环境，小数组使用插入排序的效率反而比快速排序会更高，这里不再展开。</p>
<p><a href="https://github.com/v8/v8/blob/master/src/js/array.js" target="_blank" rel="external">v8 代码示例</a>:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> QuickSort = <span class="function"><span class="keyword">function</span> <span class="title">QuickSort</span>(<span class="params">a, from, to</span>) </span>&#123;</span>
<span class="line">    ......</span>
<span class="line">    while (<span class="literal">true</span>) &#123;</span>
<span class="line">        <span class="comment">// Insertion sort is faster for short arrays.</span></span>
<span class="line">        <span class="keyword">if</span> (to - <span class="keyword">from</span> &lt;= <span class="number">10</span>) &#123;</span>
<span class="line">            InsertionSort(a, <span class="keyword">from</span>, to);</span>
<span class="line">            <span class="keyword">return</span>;</span>
<span class="line">        &#125;</span>
<span class="line">        ......</span>
<span class="line">    &#125;</span>
<span class="line">    ......</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<ul>
<li>三数取中</li>
</ul>
<p>正如已知的，快速排序的阿克琉斯之踵在于，最差数组组合情况下会算法退化。</p>
<p>快速排序的算法核心在于选择一个基准(<strong>pivot</strong>)，将经过比较交换的数组按基准分解为两个数区进行后续递归。试想如果对一个已经有序的数组，每次选择基准元素时总是选择第一个或者最后一个元素，那么每次都会有一个数区是空的，递归的层数将达到 <strong>n</strong>，最后导致算法的时间复杂度退化为 $O(n^2)$。因此 pivot 的选择非常重要。</p>
<p>v8采用的是**三数取中(median-of-three)**的优化：除了头尾两个元素再额外选择一个元素参与基准元素的竞争。</p>
<p>第三个元素的选取策略大致为：</p>
<ol>
<li>当数组长度小于等于 <strong>1000</strong> 时，选择折半位置的元素作为目标元素。</li>
<li>当数组长度超过 <strong>1000</strong> 时，每隔 <strong>200-215</strong> 个(<em>非固定，跟着数组长度而变化</em>)左右选择一个元素来先确定一批候选元素。接着在这批候选元素中进行一次排序，将所得的中位值作为目标元素</li>
</ol>
<p>最后取三个元素的中位值作为 <strong>pivot</strong>。</p>
<p>v8 代码示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> GetThirdIndex = <span class="function"><span class="keyword">function</span>(<span class="params">a, from, to</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">var</span> t_array = <span class="keyword">new</span> InternalArray();</span>
<span class="line">    <span class="comment">// Use both 'from' and 'to' to determine the pivot candidates.</span></span>
<span class="line">    <span class="keyword">var</span> increment = <span class="number">200</span> + ((to - <span class="keyword">from</span>) &amp; <span class="number">15</span>);</span>
<span class="line">    <span class="keyword">var</span> j = <span class="number">0</span>;</span>
<span class="line">    <span class="keyword">from</span> += <span class="number">1</span>;</span>
<span class="line">    to -= <span class="number">1</span>;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="keyword">from</span>; i &lt; to; i += increment) &#123;</span>
<span class="line">        t_array[j] = [i, a[i]];</span>
<span class="line">        j++;</span>
<span class="line">    &#125;</span>
<span class="line">    t_array.sort(<span class="function"><span class="keyword">function</span>(<span class="params">a, b</span>) </span>&#123;</span>
<span class="line">        <span class="keyword">return</span> comparefn(a[<span class="number">1</span>], b[<span class="number">1</span>]);</span>
<span class="line">    &#125;);</span>
<span class="line">    <span class="keyword">var</span> third_index = t_array[t_array.length &gt;&gt; <span class="number">1</span>][<span class="number">0</span>];</span>
<span class="line">    <span class="keyword">return</span> third_index;</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> QuickSort = <span class="function"><span class="keyword">function</span> <span class="title">QuickSort</span>(<span class="params">a, from, to</span>) </span>&#123;</span>
<span class="line">    ......</span>
<span class="line">    while (<span class="literal">true</span>) &#123;</span>
<span class="line">        ......</span>
<span class="line">        if (to - <span class="keyword">from</span> &gt; <span class="number">1000</span>) &#123;</span>
<span class="line">            third_index = GetThirdIndex(a, <span class="keyword">from</span>, to);</span>
<span class="line">        &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">            third_index = <span class="keyword">from</span> + ((to - <span class="keyword">from</span>) &gt;&gt; <span class="number">1</span>);</span>
<span class="line">        &#125;</span>
<span class="line">    &#125;</span>
<span class="line">    ......</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<ul>
<li>原地排序</li>
</ul>
<p>目前，大多数快速排序算法中大部分的代码实现如下所示：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> quickSort = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>) </span>&#123;</span>
<span class="line">　　<span class="keyword">if</span> (arr.length &lt;= <span class="number">1</span>) &#123; <span class="keyword">return</span> arr; &#125;</span>
<span class="line">　　<span class="keyword">var</span> pivotIndex = <span class="built_in">Math</span>.floor(arr.length / <span class="number">2</span>);</span>
<span class="line">　　<span class="keyword">var</span> pivot = arr.splice(pivotIndex, <span class="number">1</span>)[<span class="number">0</span>];</span>
<span class="line">　　<span class="keyword">var</span> left = [];</span>
<span class="line">　　<span class="keyword">var</span> right = [];</span>
<span class="line">　　<span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; arr.length; i++)&#123;</span>
<span class="line">　　　　<span class="keyword">if</span> (arr[i] &lt; pivot) &#123;</span>
<span class="line">　　　　　　left.push(arr[i]);</span>
<span class="line">　　　　&#125; <span class="keyword">else</span> &#123;</span>
<span class="line">　　　　　　right.push(arr[i]);</span>
<span class="line">　　　　&#125;</span>
<span class="line">　　&#125;</span>
<span class="line">　　<span class="keyword">return</span> quickSort(left).concat([pivot], quickSort(right));</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>以上代码存在一个问题在于：利用 left 和 right 两个数区存储递归的子数组，因此它需要 $O(n)$ 的额外存储空间。这与理论上的平均空间复杂度 $O(logn)$ 相比差距较大。</p>
<p>额外的空间开销，同样会影响实际运行时的整体速度。（这也是快速排序在实际运行时的表现可以超过同等时间复杂度级别的其他排序算法的其中一个原因。）所以一般来说，性能较好的快速排序会采用原地(in-place)排序的方式。</p>
<p>v8 源代码中的实现是对原数组进行元素交换。</p>
<h4 id="firefox-wei-shi-yao-cai-yong-gui-bing-pai-xu">Firefox 为什么采用归并排序</h4>
<p>它的背后也是有故事的。</p>
<p>Firefox 其实在一开始发布的时候对于数组排序的实现并不是采用稳定的排序算法，这块有据可考。</p>
<p>Firefox(Firebird) 最初版本实现的数组排序算法是堆排序，这也是一种不稳定的排序算法。因此，后来有人对此提交了一个 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=224128" target="_blank" rel="external">Bug</a>。</p>
<p>Mozilla开发组内部针对这个问题进行了一系列<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=224128#c2" target="_blank" rel="external">讨论</a>。</p>
<p>从讨论的过程我们能够得出几点</p>
<ul>
<li>同时期 Mozilla 的竞争对手是 <strong>IE6</strong>，从上文的统计数据可知 IE6 是稳定排序的</li>
<li>JavaScript 之父 Brendan Eich 觉得 <strong>Stability is good</strong></li>
<li>Firefox在采用<strong>堆排序</strong>之前采用的是<strong>快速排序</strong></li>
</ul>
<p>基于开发组成员倾向于实现稳定的排序算法为主要前提，<strong>Firefox3</strong> 将<strong>归并排序</strong>作为了数组排序的新实现。</p>
<h3 id="jie-jue-pai-xu-wen-ding-xing-de-chai-yi">解决排序稳定性的差异</h3>
<p>以上说了这么多，主要是为了讲述各个浏览器对于排序实现的差异，以及解释为什么存在这些差异的一些比较表层的原因。</p>
<p>但是读到这里，读者可能还是会有疑问：如果我的项目就是需要依赖稳定排序，那该怎么办呢？</p>
<h4 id="jie-jue-fang-an">解决方案</h4>
<p>其实解决这个问题的思路比较简单。</p>
<p>浏览器出于不同考虑选择不同排序算法。可能某些偏向于追求极致的性能，某些偏向于提供良好的开发体验，但是有规律可循。</p>
<p>从目前已知的情况来看，所有主流浏览器（包括IE6，7，8）对于数组排序算法的实现基本可以枚举：</p>
<ul>
<li>归并排序 / Timsort</li>
<li>快速排序</li>
</ul>
<p>所以，我们将快速排序经过定制改造，变成稳定排序的是不是就可以了？</p>
<p>一般来说，针对对象数组使用不稳定排序会影响结果。而其他类型数组本身使用稳定排序或不稳定排序的结果是相等的。因此方案大致如下：</p>
<ul>
<li>将待排序数组进行预处理，为每个待排序的对象增加自然序属性，不与对象的其他属性冲突即可。</li>
<li>自定义排序比较方法 compareFn，总是将自然序作为前置判断相等时的第二判断维度。</li>
</ul>
<p>面对归并排序这类实现时由于算法本身就是稳定的，额外增加的自然序比较并不会改变排序结果，所以方案兼容性比较好。</p>
<p>但是涉及修改待排序数组，而且需要开辟额外空间用于存储自然序属性，可想而知v8这类引擎应该不会采用类似手段。不过作为开发者自行定制的排序方案是可行的。</p>
<h4 id="fang-an-dai-ma-shi-li">方案代码示例</h4>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
</pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">const</span> INDEX = <span class="built_in">Symbol</span>(<span class="string">'index'</span>);</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getComparer</span>(<span class="params">compare</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">left, right</span>) </span>&#123;</span>
<span class="line">        <span class="keyword">let</span> result = compare(left, right);</span>
<span class="line"></span>
<span class="line">        <span class="keyword">return</span> result === <span class="number">0</span> ? left[INDEX] - right[INDEX] : result;</span>
<span class="line">    &#125;;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">array, compare</span>) </span>&#123;</span>
<span class="line">    array = array.map(</span>
<span class="line">        <span class="function">(<span class="params">item, index</span>) =&gt;</span> &#123;</span>
<span class="line">            <span class="keyword">if</span> (<span class="keyword">typeof</span> item === <span class="string">'object'</span>) &#123;</span>
<span class="line">                item[INDEX] = index;</span>
<span class="line">            &#125;</span>
<span class="line"></span>
<span class="line">            <span class="keyword">return</span> item;</span>
<span class="line">        &#125;</span>
<span class="line">    );</span>
<span class="line"></span>
<span class="line">    <span class="keyword">return</span> array.sort(getComparer(compare));</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>以上只是一个简单的满足稳定排序的算法改造示例。</p>
<p>之所以说简单，是因为实际生产环境中作为数组输入的数据结构冗杂，需要根据实际情况判断是否需要进行更多样的排序前类型检测。</p>
<h2 id="xuan-ze-pai-xu-suan-fa-de-can-kao-fang-fa">选择排序算法的参考方法：</h2>
<p>影响排序的因素有很多，平均时间复杂度低的算法并不一定就是最优的。相反，有时平均时间复杂度高的算法可能更适合某些特殊情况。同时，选择算法时还得考虑它的可读性，以利于软件的维护。一般而言，需要考虑的因素有以下四点：</p>
<ol>
<li>
<p>待排序的记录数目 n 的大小；</p>
</li>
<li>
<p>记录本身数据量的大小，也就是记录中除关键字外的其他信息量的大小；</p>
</li>
<li>
<p>关键字的结构及其分布情况；</p>
</li>
<li>
<p>对排序稳定性的要求。</p>
</li>
</ol>
<p>设待排序元素的个数为 n。选择的大致方案如下：</p>
<ol>
<li>当 n 较大，则应采用时间复杂度为 $O(nlogn)$ 的排序方法：快速排序、堆排序或归并排序。</li>
</ol>
<ul>
<li>快速排序：是目前基于比较的内部排序中被认为是最好的方法，当待排序的关键字是随机分布时，快速排序的平均时间最短；</li>
<li>堆排序：如果内存空间允许且要求稳定性的，</li>
<li>归并排序：它有一定数量的数据移动，所以我们可能过与插入排序组合，先获得一定长度的序列，然后再合并，在效率上将有所提高。</li>
</ul>
<ol start="2">
<li>
<p>当 n 较大，内存空间允许，且要求稳定性则选择归并排序</p>
</li>
<li>
<p>当 n 较小，可采用直接插入或直接选择排序。</p>
</li>
</ol>
<p>直接插入排序：当元素分布有序，直接插入排序将大大减少比较次数和移动记录的次数。</p>
<p>直接选择排序：元素分布有序，如果不要求稳定性，选择直接选择排序</p>
<ol start="4">
<li>
<p>一般不使用或不直接使用传统的冒泡排序。</p>
</li>
<li>
<p>基数排序：
它是一种稳定的排序算法，但有一定的局限性，最好满足：</p>
</li>
</ol>
<ul>
<li>关键字可分解。</li>
<li>记录的关键字位数较少，如果密集更好</li>
<li>如果是数字时，最好是无符号的，否则将增加相应的映射复杂度，可先将其正负分开排序。</li>
</ul>
<h2 id="can-kao-zi-liao-amp-tuo-zhan-yue-du">参考资料 &amp; 拓展阅读</h2>
<p><a href="http://web.jobbole.com/87968/" target="_blank" rel="external">十大经典排序算法</a></p>
<p><a href="http://efe.baidu.com/blog/talk-about-sort-in-front-end/" target="_blank" rel="external">聊聊前端排序的那些事</a></p>
<p><a href="https://www.h5jun.com/post/array-shuffle.html" target="_blank" rel="external">Array.prototype.sort 随机排列数组的错误实现</a></p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F" target="_blank" rel="external">冒泡排序概念</a> <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
<li id="fn2" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F" target="_blank" rel="external">选择排序概念</a> <a href="#fnref2" class="footnote-backref">↩</a></p>
</li>
<li id="fn3" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F" target="_blank" rel="external">插入排序概念</a> <a href="#fnref3" class="footnote-backref">↩</a></p>
</li>
<li id="fn4" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F" target="_blank" rel="external">希尔排序概念</a> <a href="#fnref4" class="footnote-backref">↩</a></p>
</li>
<li id="fn5" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F" target="_blank" rel="external">归并排序概念</a> <a href="#fnref5" class="footnote-backref">↩</a></p>
</li>
<li id="fn6" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F" target="_blank" rel="external">快速排序概念</a> <a href="#fnref6" class="footnote-backref">↩</a></p>
</li>
<li id="fn7" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F" target="_blank" rel="external">堆排序概念</a> <a href="#fnref7" class="footnote-backref">↩</a></p>
</li>
<li id="fn8" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F" target="_blank" rel="external">计数排序概念</a> <a href="#fnref8" class="footnote-backref">↩</a></p>
</li>
<li id="fn9" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E6%A1%B6%E6%8E%92%E5%BA%8F" target="_blank" rel="external">桶排序概念</a> <a href="#fnref9" class="footnote-backref">↩</a></p>
</li>
<li id="fn10" class="footnote-item"><p><a href="https://zh.wikipedia.org/wiki/%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F" target="_blank" rel="external">基数排序概念</a> <a href="#fnref10" class="footnote-backref">↩</a></p>
</li>
<li id="fn11" class="footnote-item"><p><a href="http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.11" target="_blank" rel="external">Array.prototype.sort(compareFn)</a> <a href="#fnref11" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;qian-yan&quot;&gt;前言&lt;/h2&gt;
&lt;p&gt;关于排序算法的有关文章已经很多了，然而网络上用 Javascript 语言来作为示例并详实介绍的文章貌似还是不太多。这里主要是我来尝试自己针对网上各式的排序算法进行一份详实的个人总结，从而温故知新。&lt;/p&gt;
&lt;h2 id=
    
    </summary>
    
      <category term="算法" scheme="http://qcyoung.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="前端知识" scheme="http://qcyoung.com/tags/%E5%89%8D%E7%AB%AF%E7%9F%A5%E8%AF%86/"/>
    
      <category term="题目" scheme="http://qcyoung.com/tags/%E9%A2%98%E7%9B%AE/"/>
    
      <category term="算法" scheme="http://qcyoung.com/tags/%E7%AE%97%E6%B3%95/"/>
    
      <category term="浏览器" scheme="http://qcyoung.com/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>2016.11 - Do whatever you do intensely</title>
    <link href="http://qcyoung.com/2016/12/05/2016.11%20-%20Do%20whatever%20you%20do%20intensely/"/>
    <id>http://qcyoung.com/2016/12/05/2016.11 - Do whatever you do intensely/</id>
    <published>2016-12-05T05:57:19.000Z</published>
    <updated>2016-12-05T05:55:56.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="gong-zuo-xiang-guan">工作相关</h2>
<p>这个月主要是继续收尾完善 CMS 并做了一个感恩节主题的 H5 项目，同时由于 CMS 这个项目在前期规划上的交流问题，公司在 C 端方向也开发了 CMS 项目导致在开发资源上一定程度存在重复开发现象，于是上面想把这两个项目合并，合并开发一套 CMS 后台来共同服务我们 B 端和 C 端方向的前台。</p>
<p>而这也算是毕业以来第一次遇到这种算的上是『公司 + 部门』级别的项目合并（一定意义上的跨公司，但是在公司的技术体系上却又并不是分隔的很独立）。中间各种过程不表了。由于两边前端的技术栈比较统一，所以前端合并的开发量主要也就在接口的联调、组件展示逻辑的兼容、以及埋点逻辑等方向上的处理。相比于这个项目的开发来说，项目的复杂点更多的是管理决策的 PK 和方案的确定吧。。</p>
<p>（就像美团和点评的合并吧，这种项目合并中间必定会或多或少<a href="http://info.3g.qq.com/g/index5/ttnews/yidian.jsp?aid=yidian&amp;id=tech_20151121032832&amp;g_f=23748" target="_blank" rel="external">伤害其中一个团队或是Team</a>，站在公司的角度上肯定希望最大程度的节约开发资源。所以不太会容忍多个 Team 分别开发各自功能却近似的项目。但是在两边的项目都起到一定规模的时候才发觉。。。确实很伤团队的士气，而作为个人最好得便是在开发层面上自然地尽可能做好自己负责的项目，这样即便当你遇到这种项目资源重复，需要合并时。只要做到比别人的好。这样合并时更多的就会以你的开发方案和代码为主，让别人基于你的代码架构上来开发。你的工作成果也就会得到肯定并且在一定程度上不会浪费）</p>
<p>另外感恩节的 H5 项目主要就是一个针对每个商户的总结性的商户数据 H5 统计展示并最后分享领券的一个互动活动页。除了工期比较紧外，技术上难点并不多，于是也就比较快速的用 jq 来实现具体的移动端的展示和动画效果了。在一些具体的图形数据展示上用了 d3 作了雷达图的分类数据展示等。第一次用 d3 在移动端做项目，发现 d3 还是在移动端上有一些兼容性的问题（例如 SVG 的 dy , dx 等)。还是需要在应用时多注意一下，在平时也更熟悉一下 d3 的一些应用。</p>
<h2 id="ji-zhu-ji-qi-ta-xiang-guan">技术及其他相关</h2>
<ol>
<li>这个月有两篇看到的关于技术债的文章讲得很好(<a href="http://mp.weixin.qq.com/s?__biz=MzA4ODgwNjk1MQ==&amp;mid=2653788501&amp;idx=1&amp;sn=9399568096319bbca8e80164ba304b8e&amp;chksm=8bfdba1dbc8a330b34d3a6ae58825867a63a70b5d2407ae4bba509e0438f78167efe7cc09d6c&amp;mpshare=1&amp;scene=1&amp;srcid=1202GxV3nWnpntUzj3RusBF3#rd" target="_blank" rel="external">关于技术债务</a>，
<a href="http://mp.weixin.qq.com/s?__biz=MzA3NDM0ODQwMw==&amp;mid=2649827509&amp;idx=1&amp;sn=61a7adbeb49a105b9a22bae41614d07f" target="_blank" rel="external">技术债：the good, the bad, and the tao</a>)，技术债确实是每个技术团队都或多或少会面临的问题，如何正确看待技术债，合理控制技术债。真的很有帮助（看了 MongoDb 的例子也真是有所学习了..）
主要总结的思想如下：</li>
</ol>
<ul>
<li>拥抱 MVP。先解决温饱问题，再考虑还债。</li>
<li>把技术债视情况外包出去，</li>
<li>雇佣你所能获得的最优秀的人，给予她们你所能给予的，最能发挥她们能力的权限。（所有工程师各方面培养主人翁意识）</li>
<li>拥抱匡威定律。你的组织架构决定了你的代码结构。想要快速独立的功能交付能力，你要有包含所有角色，拥有直接决策权的端到端的功能团队，而不是开发，测试，运维等彼此独立。（相伴随的也要有健全的 Monitoring 和测试机制）</li>
<li>在实现上可以多些负债，在接口上尽量减少负债。系统设计的框架得是大体对的。</li>
<li>定期处理一定的技术债务</li>
</ul>
<ol start="2">
<li>
<p>今年双 11 又看到了去年双 11 天猫的狂欢城的技术方案总结，有些细节挺有意思的，也了解了这些实时活动的相关设计，容灾机制等方案。<a href="http://www.infoq.com/cn/articles/tianmao-interaction-solutions" target="_blank" rel="external">天猫双 11 晚会和狂欢城的互动技术方案</a></p>
</li>
<li>
<p>由于一直在做移动端的商城，所以这些手势的相关 API 还是比较了解的，但是对于 pinch rotate 这些操作相应的实践就并不多了，通过 AlloyFinger 熟悉了一下。<a href="http://www.alloyteam.com/2016/11/11568/" target="_blank" rel="external">超小 Web 手势库 AlloyFinger 原理</a></p>
</li>
<li>
<p>对应到编程领域也很贴切：形成主见 -&gt; 发现不能解释的事情 -&gt; 融汇贯通 -&gt; 以简御繁 -&gt; 运用自如 -&gt; 一览众山小 -&gt; 通透。<a href="http://cul.qq.com/a/20161121/002271.htm" target="_blank" rel="external">梁漱溟：思考问题的八层境界</a></p>
</li>
<li>
<p><a href="http://mp.weixin.qq.com/s?__biz=MzA4MzYyNjM2OQ==&amp;mid=2247483939&amp;idx=1&amp;sn=6b7600e93fed84875ba02b6121eac117&amp;chksm=9ff2d2cda8855bdb9983ae6193e32eded93a58e7f08b427dfc8a75ad3e29d9fe29c2e084d17c&amp;scene=0#rd" target="_blank" rel="external">HoloLens 初代到底有多牛</a>，这是一篇关于 HoloLens 硬件、技术原理、细节的 PPT 介绍，内容比较详实。期待 HoloLens 能早日普及。（然而有生之年是否能见到类似 SAO 的产品呢..）</p>
</li>
<li>
<p>看了月影个人的英语学习方法经验，有一些经验还是很有参考价值的。
例如：翻译技术文章的时候可以根据作者的 GitHub 地址进行深入了解背景甚至交流等等。
<a href="http://mp.weixin.qq.com/s?__biz=MzIwNjEwNTQ4Mw==&amp;mid=2651576733&amp;idx=1&amp;sn=6ca82a137b129114e34ab26b2299a2cb&amp;chksm=8cd9c779bbae4e6fcd8a35610cd34a247a0e46fd4ed328c3e38e944bf406d4ceba840919434b&amp;mpshare=1&amp;scene=1&amp;srcid=11301plTAQrl7LP0AtEtIEMD#rd" target="_blank" rel="external">实录｜月影谈循序渐进的英语学习方法</a></p>
</li>
</ol>
<h2 id="ge-ren-sheng-huo-xiang-guan">个人生活相关</h2>
<p>这个月到了 24 岁，感觉良好。</p>
<p>这个月业余时间没有写太多新的代码，主要因为有一些其他烦心的事，看到 Github 上灰溜溜的四列还是挺惭愧的。需要调整一下心态了。</p>
<p>（本文的题目是本月扇贝某日打卡的每日一句，『Do whatever you do intensely -- 罗伯特亨利』，说的是无论做什么，都要满怀热情。而对于我来讲如何保证热情呢？就是去践行新的计划与目标。）</p>
<h2 id="san-shou-ge">三首歌</h2>
<p><a href="http://music.163.com/#/song?id=656437" target="_blank" rel="external">ユメセカイ - 戸松遥</a></p>
<p><a href="http://music.163.com/#/m/song?id=39224531" target="_blank" rel="external">失う - らいらい</a></p>
<p><a href="http://music.163.com/#/m/song?id=276904" target="_blank" rel="external">外面的世界 - 莫文蔚</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;gong-zuo-xiang-guan&quot;&gt;工作相关&lt;/h2&gt;
&lt;p&gt;这个月主要是继续收尾完善 CMS 并做了一个感恩节主题的 H5 项目，同时由于 CMS 这个项目在前期规划上的交流问题，公司在 C 端方向也开发了 CMS 项目导致在开发资源上一定程度存在重复开发
    
    </summary>
    
      <category term="我的生活" scheme="http://qcyoung.com/categories/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="我的生活" scheme="http://qcyoung.com/tags/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    
      <category term="月记" scheme="http://qcyoung.com/tags/%E6%9C%88%E8%AE%B0/"/>
    
      <category term="音乐" scheme="http://qcyoung.com/tags/%E9%9F%B3%E4%B9%90/"/>
    
  </entry>
  
  <entry>
    <title>2016.10 - 往日的伤秋情怀也在这短暂的秋天里变得没影了</title>
    <link href="http://qcyoung.com/2016/11/07/2016.10%20-%20%E5%BE%80%E6%97%A5%E7%9A%84%E4%BC%A4%E7%A7%8B%E6%83%85%E6%80%80%E4%B9%9F%E5%9C%A8%E8%BF%99%E7%9F%AD%E6%9A%82%E7%9A%84%E7%A7%8B%E5%A4%A9%E9%87%8C%E5%8F%98%E5%BE%97%E6%B2%A1%E5%BD%B1%E4%BA%86/"/>
    <id>http://qcyoung.com/2016/11/07/2016.10 - 往日的伤秋情怀也在这短暂的秋天里变得没影了/</id>
    <published>2016-11-07T05:57:19.000Z</published>
    <updated>2018-11-25T09:09:09.200Z</updated>
    
    <content type="html"><![CDATA[<p>从今以后，想每个月粗略记录一下自己在这个月的工作、技术相关以及生活的内容。记录一些自己看到的好东西啥的...也算是每个月简单的一些总结 + 扯扯淡（增添些生活气息 =.=）。</p>
<h2 id="gong-zuo-xiang-guan">工作相关</h2>
<p>目前这个月一直在搞公司的 CMS 系统。其实公司这块起步相对也算挺晚的了。这个项目也算是又一次从零开始参与搭建一个项目..</p>
<p>理论上来讲这是公司一个很大的项目，公司的双 11 活动页面很多入口都会走由此系统搭建的各级页面。从 9 月开始从 0 起步进行开发到这个月月底完成两期版本上线。但是在公司里..由于每个项目都是最高优先级..所以这个项目中途都还被插了好几个紧急需求..真的要吐槽下..</p>
<p>这个项目中我主要负责了 CMS 后台设计器部分的页面展示和交互，以及前台部分上微信和 APP 的展示对接。两边的技术栈上我都统一用了 Vue + Vuex + Vuerouter 全家桶，撸起来也还是挺快的。</p>
<p>在开发过程中主要是在后台设计器的制作中回顾了下 drag event 这一事件。用了一个基于 Vue 的拖拽插件 <a href="https://github.com/james2doyle/vue-drag-and-drop" target="_blank" rel="external">vue-drag-and-drop</a> 来协助实现了设计器里的拖拽控件交互。写了一个点击展示调色板的小插件，准备回头整理一下开源出来。同时后台的设计器由于想回头交给后端方便点，所以也加了 Bootstrap 的样式。并用了 <a href="http://kleinejan.github.io/titatoggle/" target="_blank" rel="external">titatoggle</a> 来优化展示 toggle 形式的一个小插件。</p>
<p>最后在双 11 前完成了目前的 CMS 系统，他主要具有以下特性：</p>
<ol>
<li>可视化快速构建页面内容和设置，</li>
<li>前后端分离，不由后端来负责渲染页面，后端只负责提供高性能，可复用的 API。</li>
<li>主要负责移动端展示，兼容 APP 逻辑。</li>
<li>版本可降级，页面加载逻辑性能优化。</li>
</ol>
<p>架构上来讲，CMS及其周边系统大概为如下的关系：</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/module_relation.png" alt="系统关系"></p>
<p>其中 Weblog 是我们公司自己的埋点分析处理工具。在前台会自动将数据埋到各个组件模块上。auth 系统是权限系统用来审核页面发布流程。GIS 负责来提供城市区域维度的信息，它与商户智能服务共同进行判断使每个用户根据自己的位置信息和用户标签来浏览差异化的页面展示信息。而商品中心提供了基础的商品信息服务。而在后台方案管理模块中我们可以进入设计器和方案预览页去编辑、查看搭建效果。</p>
<p>在前端我们会首次请求拿到此页面的方案信息（包含页面背景，页面名称，模块序列等页面方案层级的信息）在模块序列中会包含这个方案页面的各模块ID，根据各模块ID，我们来反查各模块的内容并填充到页面上，在首次加载时只会加载首屏的模块内容从而也起到一个懒加载的效果。</p>
<p>另外后端会在每个方案生成后产生一个版本ID字段,在复用次数较多的页面，前端会存储这个页面模块的信息内容和布局以及版本号，在请求后端接口时会携带这一版本ID，如果能够匹配，或是请求失败，则会直接调用前端存储的上次内容。这也是降级和性能优化的一个方案。</p>
<h2 id="ji-zhu-ji-qi-ta-xiang-guan">技术及其他相关</h2>
<p>这里简单说一下这个月看到的技术和其他相关的有意思的东西：</p>
<ol>
<li>
<p>首先分享的还是在做 CMS 项目时刚好看到 JD 他们的 CMS 架构演进分享，毕竟我所在的公司在体量上还相差甚远，从他们的架构中可以去了解其他公司目前的 CMS 系统是如何实现发展的。<a href="http://mp.weixin.qq.com/s?__biz=MzIwODA4NjMwNA==&amp;mid=2652897861&amp;idx=1&amp;sn=f2804fd064c7d3ea86554c187ae03db7" target="_blank" rel="external">京东上千页面搭建基石——CMS前后端分离演进史</a></p>
</li>
<li>
<p>这个月在忙 CMS 的同时，还参与了 <a href="https://vuefe.cn/" target="_blank" rel="external">Vue2 中文文档</a>的翻译校对，也算是为一直在使用的 Vue 做出一些贡献..不过在这个翻译校对的过程中也发现，以社区形式贡献的内容输出。质量管理真的是一个需要重点把握的内容。例如某人初次翻译后我认为应该对其进行初次校对后视能力和态度才能再让他参与翻译，这样就很容易发现一些其他人翻译时留下的不负责任的坑。（不要问我为什么有这样的领悟 = =。。）</p>
</li>
<li>
<p>此外这个月接触到了 <a href="http://getbem.com/introduction/" target="_blank" rel="external">BEM</a> 这一 CSS 命名方案，发现这也算是很老的一个概念了。。尽管目前类似 webpack css-loader 使 CSS 已经变得模块化并解决了很多作用域冲突的问题，但在语义化和设计规范的推进下还是准备尝试在个人项目中先试着使用来体验一下感受。
相关资料：</p>
</li>
</ol>
<ul>
<li><a href="https://www.smashingmagazine.com/2016/06/battling-bem-extended-edition-common-problems-and-how-to-avoid-them/" target="_blank" rel="external">Battling BEM (Extended Edition): 10 Common Problems And How To Avoid Them</a></li>
<li><a href="https://www.zhihu.com/question/21935157?sort=created" target="_blank" rel="external">如何看待 CSS 中 BEM 的命名方式？</a></li>
</ul>
<p>使用案例：</p>
<ul>
<li><a href="https://github.com/ElemeFE/element" target="_blank" rel="external">饿了么组件库 element</a></li>
<li><a href="https://github.com/weui/weui/" target="_blank" rel="external">微信组件库 weui</a></li>
</ul>
<ol start="4">
<li>
<p>另外看到一个有趣的校招题：用 HTML 和 CSS 画一个笑脸。如下是预期效果和最终实现参考。
<img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/smile.png" alt="笑脸">
<a href="http://codepen.io/yangzj1992/pen/mrNBdZ" target="_blank" rel="external">demo</a></p>
</li>
<li>
<p>这个月还看到了『绿色地球』这一个很有意义的项目，创建人居然选择在我大成都开始创业运行，这就更得关注并支持一下这样的项目，希望它能走的长远，早日把业务拓展到帝都来。感兴趣的朋友可以看看下面的文章来具体了解。</p>
</li>
</ol>
<ul>
<li><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODAwNDIwMA==&amp;mid=2650522355&amp;idx=1&amp;sn=66e16af0905af40324740f1763f22320" target="_blank" rel="external">他是年薪几十万的微软工程师，却辞职去收破烂，成了最让女儿自豪的爸爸</a></li>
</ul>
<p>下面是他在一席的演讲，挺值得看看的：</p>
<ul>
<li><a href="http://mp.weixin.qq.com/s?__biz=MjM5NjYyMjM0MA==&amp;mid=2650864114&amp;idx=1&amp;sn=b6a6dab97498a430e4b28099f6e8c7eb" target="_blank" rel="external">我们每天制造这么多垃圾，到底拿它怎么办</a></li>
</ul>
<h2 id="ge-ren-sheng-huo-xiang-guan">个人生活相关</h2>
<p>这个月国庆回了次绵阳，在城区逛了很多地方，市中心的公园、警钟街等地方基本和小时候一样还是保留着原来的样子，甚至几年前吐槽的车多了路还是跟九几年一样窄..但是新的变化也是有的 ——</p>
<p>在涪江畔越王楼，曾经的三江广场被彻底翻修，由以前长满野草的河岸变成了沿河散步休憩的步行广场，修的十分的漂亮，两岸江边的建筑墙上也做了光幕，到了晚上在建筑物上可以展现十分靓丽的灯光秀，今年国庆恰逢绵阳主题灯光展，在越王楼上看三江河畔的灯光秀还是很吸引人的。</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/riverside.jpeg" alt="涪江">
<img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/fule_bridge.jpeg" alt="富乐大桥">
<img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/light_show.jpeg" alt="灯光秀"></p>
<p>另外马家巷的小吃 + 平日早餐的（油茶 + 米粉）真的是绵阳一绝。唉，帝都实在是难找如此这般正统风味的米粉和油茶...（所幸找到一家淘宝店卖的油茶很不错..可解相思之苦= =..）</p>
<p>下面是正宗的<a href="http://baike.baidu.com/view/1641554.htm" target="_blank" rel="external">四川米粉</a>和油茶的样子（我就不说淘宝直接搜油茶，默认搜出来的其他省的冲泡型油茶有多难喝了..)</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/rice_noodle.jpg" alt="四川米粉"></p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/oil-tea.jpg" alt="四川油茶"></p>
<p>另外也第一次去了北川地震遗址，感受了生命、生活的无常，珍惜现有的日子和幸福时光吧。</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/monument.jpeg" alt="纪念碑">
<img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/hero_chinese.jpeg" alt="宣传画">
<img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/201610/school_ruin.jpeg" alt="学校废墟"></p>
<p>另外 WOW 更新 7.0 后，作为一个单纯的剧情党和战场爱好者，深深地感受到了暴雪对我们这种休闲玩家的恶意，加上网易月卡的补刀。游戏成本一下子陡增（虽然架不过脸好，居然莫名的掉了小德核心橙护腕。。居然战场啥的还能混下去。）然而在 7.1 打了一把 4 个小时才通关的 KLZ 之后。。确实发现 WOW 真的越来越有点肝不动了。。实在是有些可惜吧。。</p>
<p>最后，在这十月的一个月里，我的衣着从：四川的短袖 + 短裤 =&gt; 北京的毛衣 + 秋裤。。不禁感慨我国真是幅员辽阔。。（相反帝都的秋天也真是越来越短暂了。。以前这个月里我还总会沉浸在伤秋的氛围里过上好几天日子，这一年感觉都没啥反应。。就已经开始来暖气准备过冬了 = =。。）</p>
<h2 id="san-shou-ge">三首歌</h2>
<p><a href="http://music.163.com/#/song?id=662235" target="_blank" rel="external">Pray - 川瀬智子</a></p>
<p><a href="http://music.163.com/#/song?id=498187" target="_blank" rel="external">βίος &lt;MK+nZk Version&gt; - 澤野弘之</a></p>
<p><a href="http://music.163.com/#/song?id=426881500" target="_blank" rel="external">三葉のテーマ - RADWIMPS</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;从今以后，想每个月粗略记录一下自己在这个月的工作、技术相关以及生活的内容。记录一些自己看到的好东西啥的...也算是每个月简单的一些总结 + 扯扯淡（增添些生活气息 =.=）。&lt;/p&gt;
&lt;h2 id=&quot;gong-zuo-xiang-guan&quot;&gt;工作相关&lt;/h2&gt;
&lt;p&gt;目前这
    
    </summary>
    
      <category term="我的生活" scheme="http://qcyoung.com/categories/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="我的生活" scheme="http://qcyoung.com/tags/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    
      <category term="月记" scheme="http://qcyoung.com/tags/%E6%9C%88%E8%AE%B0/"/>
    
      <category term="音乐" scheme="http://qcyoung.com/tags/%E9%9F%B3%E4%B9%90/"/>
    
      <category term="美食" scheme="http://qcyoung.com/tags/%E7%BE%8E%E9%A3%9F/"/>
    
  </entry>
  
  <entry>
    <title>【译】教程：如何通过 Rollup 来打包 JavaScript</title>
    <link href="http://qcyoung.com/2016/09/29/%E3%80%90%E8%AF%91%E3%80%91%E6%95%99%E7%A8%8B%EF%BC%9A%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%20Rollup%20%E6%9D%A5%E6%89%93%E5%8C%85%20JavaScript/"/>
    <id>http://qcyoung.com/2016/09/29/【译】教程：如何通过 Rollup 来打包 JavaScript/</id>
    <published>2016-09-29T15:58:14.000Z</published>
    <updated>2016-10-24T05:54:20.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文链接 : <a href="https://code.lengstorf.com/learn-rollup-js/" target="_blank" rel="external">Tutorial: How to Bundle JavaScript With Rollup</a><br>
原文作者 : <a href="https://github.com/jlengstorf/" target="_blank" rel="external">jlengstorf</a><br>
译文出自 : <a href="http://zcfy.cc/" target="_blank" rel="external">众成翻译</a><br>
译者 : <a href="http://qcyoung.com">yangzj1992</a><br>
校对者: <a href="https://www.zhihu.com/people/ha-ha-qiu-52" target="_blank" rel="external">lisa</a><br>
首发于: <a href="http://zcfy.cc/article/how-to-bundle-javascript-with-rollup-step-by-step-tutorial-1254.html" target="_blank" rel="external">众成翻译</a></p>
</blockquote>
<blockquote>
<p>本文将通过一步步的系列教学来学习如何使用更小，更高效的工具 Rollup 来代替 Webpack 和 Browserify 打包 JavaScript 文件。</p>
</blockquote>
<p>这周，我们将第一次用 <a href="http://rollupjs.org/" target="_blank" rel="external">Rollup</a> 来构建我们的项目，它是一款用来打包 Javascript 代码的构建工具（它同样支持样式表，但我们将在下周单独介绍这一点）</p>
<blockquote>
<p>译者注：原文系列文章如下(本文为第一篇)<br>
<a href="https://code.lengstorf.com/learn-rollup-js/" target="_blank" rel="external">Part I: How to Use Rollup to Process and Bundle JavaScript Files</a><br>
<a href="https://code.lengstorf.com/learn-rollup-css/" target="_blank" rel="external">Part II: How to Use Rollup to Process and Bundle Stylesheets</a><br>
<a href="https://code.lengstorf.com/learn-rollup-css/#livereload" target="_blank" rel="external">Part III: How to Use Rollup to Watch and Live Reload Files During Development</a></p>
</blockquote>
<p>通过本教程，我们会了解到以下 Rollup 的相关配置方式：</p>
<ul>
<li>组合我们的脚本,</li>
<li>移除未使用的代码,</li>
<li>转译代码使其支持老版本浏览器,</li>
<li>在浏览器中支持使用 Node modules,</li>
<li>使用环境变量,</li>
<li>压缩文件代码使文件大小尽可能最小化。</li>
</ul>
<h2 id="yu-bei-zhi-shi">预备知识</h2>
<ul>
<li>如果你了解 JavaScript 的相关知识那么对阅读此文会更有帮助。</li>
<li>熟悉 <a href="https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch3.md#modules" target="_blank" rel="external">ES2015 modules</a>当然更没有什么坏处。</li>
<li>你需要在你的机器上安装 <code>npm</code> (还没有安装? <a href="https://nodejs.org/" target="_blank" rel="external">在这安装 Node.js</a>)</li>
</ul>
<h2 id="shi-yao-shi-rollup">什么是 Rollup?</h2>
<p>Rollup 是下一代的 JavaScript 模块打包工具。当你使用 ES2015 模块来编写你的应用或者库时，它可以对它们有效的打包来成为一个单独文件供浏览器和 Node.js 使用。</p>
<p>这与 <a href="http://browserify.org/" target="_blank" rel="external">Browserify</a> 和 <a href="https://webpack.github.io" target="_blank" rel="external">webpack</a> 很相似。</p>
<p>你也可以把 Rollup 称为一个构建工具，类似于 <a href="http://gruntjs.com/" target="_blank" rel="external">Grunt</a> 和 <a href="https://github.com/gulpjs/gulp" target="_blank" rel="external">Gulp</a>。然而，你需要重点注意的是当你使用 Grunt 和 Gulp 去处理类似创建 JavaScript bundles 的任务时，这些工具在底层也会像 Rollup, Browserify 或是 webpack 一样去使用一些相同的方式来处理。</p>
<h3 id="wei-shi-yao-ni-xu-yao-guan-xin-rollup">为什么你需要关心 Rollup?</h3>
<p>Rollup 如此令人兴奋的原因在于它能够保持文件体积更小。这听上去很傻瓜，所以 <strong>tl;dr</strong> 版本在这：相比于其他工具创建的 JavaScript bundles，Rollup 总是会创建相比之更小，更快的 bundle。</p>
<p>之所以会这样是因为 Rollup 是基于 ES2015 模块的，它相比于 webpack 和 Browserify 所使用的 CommonJS 模块更加具有效率，另外 Rollup 也会使用一种叫 <em>tree-shaking</em> 的特性来更容易的移除模块中未使用的代码，这意味着在最终的 bundle 中只有我们实际需要的代码。</p>
<p>Tree-shaking 在我们引用了包含很多可用的函数或方法的第三方工具或框架时就会变得十分重要。例如我们只使用它们中的一两个方法时 —— 像 <a href="https://lodash.com/" target="_blank" rel="external">lodash</a> 或 <a href="https://jquery.com/" target="_blank" rel="external">jQuery</a> 这样的库就会在加载时产生<strong>许多</strong>额外的开销。</p>
<p>目前 Browserify 和 webpack 在最终生成时仍会包含大量未使用的代码（译者注：在 webpack2 中也引入了 tree-shaking 特性）。但 Rollup 并不如此 —— 它只会生成我们最终实际使用的代码。</p>
<p><strong>(2016 年 8 月 22 日更新)</strong> 澄清一下， Rollup 只会在 ES 模块中支持 tree-shaking 特性。目前依照 CommonJS 模块所编写的 lodash 和 jQuery 不能被支持 tree-shaken。然而 tree-shaking <strong>并不是</strong> Rollup 唯一的速度和性能上的优势。可以看这些文章了解更多信息<a href="https://www.reddit.com/r/javascript/comments/4yprc5/how_to_bundle_javascript_with_rollup_stepbystep/d6qzgzm" target="_blank" rel="external">Rich Harris’s explanation</a>、<a href="https://www.reddit.com/r/javascript/comments/4yprc5/how_to_bundle_javascript_with_rollup_stepbystep/d6qzmgh?context=3" target="_blank" rel="external">Nolan Lawson’s added info</a></p>
<h2 id="di-yi-bu-fen-ru-he-shi-yong-rollup-lai-chu-li-bing-da-bao-java-script-wen-jian">第一部分：如何使用 Rollup 来处理并打包 JavaScript 文件</h2>
<p>为了展示 Rollup 是如何运作的，让我们一起针对一个十分简单的项目用 Rollup 处理打包 JavaScript。</p>
<h3 id="di-0-bu-chuang-jian-yi-ge-bao-han-java-script-he-css-de-da-bao-xiang-mu">第 0 步: 创建一个包含 JavaScript 和 CSS 的打包项目</h3>
<p>开始之前，我们需要一个项目来进行工作。在此教学中，我们将会根据此 <a href="https://github.com/jlengstorf/learn-rollup" target="_blank" rel="external">Github 项目</a>来进行展示。</p>
<p>目录结构是这样的：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
</pre></td><td class="code"><pre><span class="line">learn-rollup/</span>
<span class="line">├── src/</span>
<span class="line">│   ├── scripts/</span>
<span class="line">│   │   ├── modules/</span>
<span class="line">│   │   │   ├── mod1.js</span>
<span class="line">│   │   │   └── mod2.js</span>
<span class="line">│   │   └── main.js</span>
<span class="line">│   └── styles/</span>
<span class="line">│       └── main.css</span>
<span class="line">└── package.json</span>
</pre></td></tr></table></figure>
<p>你可以在你的终端中执行下面的命令来安装此项目，在本教学中我们将通过此项目进行展示。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment"># Move to the folder where you keep your dev projects.</span></span>
<span class="line"><span class="built_in">cd</span> /path/to/your/projects</span>
<span class="line"></span>
<span class="line"><span class="comment"># Clone the starter branch of the app from GitHub.</span></span>
<span class="line">git <span class="built_in">clone</span> -b step-0 --single-branch https://github.com/jlengstorf/learn-rollup.git</span>
<span class="line"></span>
<span class="line"><span class="comment"># The files are downloaded to /path/to/your/projects/learn-rollup/</span></span>
</pre></td></tr></table></figure>
<h3 id="bu-zou-1-an-zhuang-rollup-bing-chuang-jian-pei-zhi-wen-jian">步骤 1：安装 Rollup 并创建配置文件</h3>
<p>首先，通过下面的命令安装 Rollup:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install --save-dev rollup</span>
</pre></td></tr></table></figure>
<p>接下来，在 <code>learn-rollup</code> 文件夹中创建一个新文件 <code>rollup.config.js</code>。之后在文件中添加下面的内容：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line">  <span class="attr">entry</span>: <span class="string">'src/scripts/main.js'</span>,</span>
<span class="line">  <span class="attr">dest</span>: <span class="string">'build/js/main.min.js'</span>,</span>
<span class="line">  <span class="attr">format</span>: <span class="string">'iife'</span>,</span>
<span class="line">  <span class="attr">sourceMap</span>: <span class="string">'inline'</span>,</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>下面是每一个配置选项都做了些什么：</p>
<ul>
<li>
<p><code>entry</code> —— 这是我们希望 Rollup 去执行的文件。在大多数项目中，这将是你主要的 JavaScript 文件，它负责一切的初始化工作并作为开始文件。</p>
</li>
<li>
<p><code>dest</code> —— 这是脚本程序执行后所存储的位置。</p>
</li>
<li>
<p><code>format</code> —— Rollup 支持多种输出格式。因为我们在浏览器中运行，我们希望使用<a href="http://benalman.com/news/2010/11/immediately-invoked-function-expression/" target="_blank" rel="external">立即调用的函数表达式</a>(immediately-invoked function expression,IIFE)。<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>
</li>
<li>
<p><code>sourceMap</code> —— 如果有 sourcemap 的话，那么在调试代码时会提供很大的帮助，这个选项会在生成文件中添加 sourcemap，来让事情变得更加简单。</p>
</li>
</ul>
<p><strong>注意：</strong> 关于其他的 <code>format</code> 选项以及什么场合你可能会需要它们，可以参考 <a href="https://github.com/rollup/rollup/wiki/JavaScript-API#format" target="_blank" rel="external">Rollup’s wiki</a></p>
<h4 id="ce-shi-rollup-pei-zhi">测试 Rollup 配置</h4>
<p>一旦我们创建了配置文件，就可以在我们的终端里运行下面的代码进行测试了：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">./node_modules/.bin/rollup -c</span>
</pre></td></tr></table></figure>
<p>这样会在你的项目中创建一个新的 <code>build</code> 文件夹，它包含了一个 <code>js</code> 的子文件夹，在 <code>js</code> 文件夹中还包含了我们生成的 <code>main.min.js</code> 文件。</p>
<p>我们在浏览器中打开 <code>build/index.html</code> 可以看到 bundle 已经被正确创建了。</p>
<p><strong>注意：</strong> 在这个阶段，只有现代浏览器会正常工作并不产生错误。如果要让不支持<code>ES2015/ES6</code> 的旧版本的浏览器也正常工作，我们需要添加一些插件。</p>
<h4 id="da-bao-shu-chu-de-nei-rong">打包输出的内容</h4>
<p>Rollup 如此强大的原因在于它的 “tree-shaking” 特性，它会将我们引用的模块中未使用的代码剥离。例如，在 <code>src/scripts/modules/mod1.js</code> 中有一个名为 <code>sayGoodbyeTo()</code> 的函数并未在你的项目中使用 —— 既然它不会被使用，那么 Rollup 在最后的 bundle 中就不会包含它：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
</pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line"><span class="meta">'use strict'</span>;</span>
<span class="line"></span>
<span class="line"><span class="comment">/**</span>
<span class="line"> * Says hello.</span>
<span class="line"> * @param  &#123;String&#125; name a name</span>
<span class="line"> * @return &#123;String&#125;      a greeting for `name`</span>
<span class="line"> */</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHelloTo</span>(<span class="params"> name </span>) </span>&#123;</span>
<span class="line">  <span class="keyword">const</span> toSay = <span class="string">`Hello, <span class="subst">$&#123;name&#125;</span>!`</span>;</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> toSay;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line"><span class="comment">/**</span>
<span class="line"> * Adds all the values in an array.</span>
<span class="line"> * @param  &#123;Array&#125; arr an array of numbers</span>
<span class="line"> * @return &#123;Number&#125;    the sum of all the array values</span>
<span class="line"> */</span></span>
<span class="line"><span class="keyword">const</span> addArray = <span class="function"><span class="params">arr</span> =&gt;</span> &#123;</span>
<span class="line">  <span class="keyword">const</span> result = arr.reduce(<span class="function">(<span class="params">a, b</span>) =&gt;</span> a + b, <span class="number">0</span>);</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> result;</span>
<span class="line">&#125;;</span>
<span class="line"></span>
<span class="line"><span class="comment">// Import a couple modules for testing.</span></span>
<span class="line"><span class="comment">// Run some functions from our imported modules.</span></span>
<span class="line"><span class="keyword">const</span> result1 = sayHelloTo(<span class="string">'Jason'</span>);</span>
<span class="line"><span class="keyword">const</span> result2 = addArray([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]);</span>
<span class="line"></span>
<span class="line"><span class="comment">// Print the results on the page.</span></span>
<span class="line"><span class="keyword">const</span> printTarget = <span class="built_in">document</span>.getElementsByClassName(<span class="string">'debug__output'</span>)[<span class="number">0</span>];</span>
<span class="line"></span>
<span class="line">printTarget.innerText = <span class="string">`sayHelloTo('Jason') =&gt; <span class="subst">$&#123;result1&#125;</span>\n\n`</span></span>
<span class="line">printTarget.innerText += <span class="string">`addArray([1, 2, 3, 4]) =&gt; <span class="subst">$&#123;result2&#125;</span>`</span>;</span>
<span class="line"></span>
<span class="line">&#125;());</span>
<span class="line"><span class="comment">//# sourceMappingURL=data:application/json;charset=utf-8;base64,...</span></span>
</pre></td></tr></table></figure>
<p>而在其他的构建工具中却并不会如此，因此如果我们引用了一个很大的库如 <a href="https://lodash.com/" target="_blank" rel="external">lodash</a> 却只为了使用它一到两个方法的话，最后的 bundles 会<strong>十分</strong>的巨大。</p>
<p>例如，使用 <a href="https://webpack.github.io" target="_blank" rel="external">webpack</a> 的话，<code>sayGoodbyeTo()</code> 函数就会被引入，并且最终的 bundle 体积相比于 Rollup 所生成的 bundle 要大两倍还多。<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p>
<h3 id="bu-zou-2-she-zhi-babel-lai-shi-yong-xin-de-java-script-te-xing">步骤 2： 设置 Babel 来使用新的 JavaScript 特性。</h3>
<p>此时，我们获得了可以在现代浏览器中运行的代码包，然而如果访问的浏览器仍是旧版本的话那么就会产生错误 —— 这样不太理想。</p>
<p>幸好，<a href="https://babeljs.io" target="_blank" rel="external">Babel</a> 已经提供了支持。它能够帮助我们<a href="https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them" target="_blank" rel="external">转译 JavaScript 新特性</a><a href="https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch1.md" target="_blank" rel="external">(ES6/ES2015 等等)</a>到 ES5 版本，这也将支持目前所有的浏览器来正常运行代码。</p>
<p>如果你从未使用过 Babel，那么从今以后你作为开发者的日子就要永远改变了。去尝试 JavaScript 的新特性可以让你的语言变得更简单、干净，让你的开发更加愉快。</p>
<p>所以让我们立刻开始 Rollup 的这一部分吧。</p>
<h4 id="an-zhuang-bi-yao-de-mo-kuai">安装必要的模块</h4>
<p>首先，我们需要安装 <a href="https://github.com/rollup/rollup-plugin-babel" target="_blank" rel="external">Babel Rollup 插件</a> 和 <a href="https://github.com/rollup/babel-preset-es2015-rollup" target="_blank" rel="external">合适的 Babel preset</a></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment"># Install Rollup’s Babel plugin.</span></span>
<span class="line">npm install --save-dev rollup-plugin-babel</span>
<span class="line"></span>
<span class="line"><span class="comment"># Install the Babel preset for transpiling ES2015 using Rollup.</span></span>
<span class="line">npm install --save-dev babel-preset-es2015-rollup</span>
</pre></td></tr></table></figure>
<p><strong>注意:</strong> Babel preset 是一个有关 Babel 插件的集合，它会告诉 Babel 我们实际上想要转译什么。</p>
<h4 id="chuang-jian-code-babelrc-code">创建 <code>.babelrc</code></h4>
<p>接下来，在你的项目根目录(<code>learn-rollup/</code>)创建一个名为 <code>.babelrc</code> 的新文件，在它内部添加以下 JSON 内容：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">&#123;</span>
<span class="line">  <span class="attr">"presets"</span>: [<span class="string">"es2015-rollup"</span>],</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>这会告诉 Babel 它应该使用哪种 preset 来转译代码。</p>
<h4 id="geng-xin-code-rollup-config-js-code">更新 <code>rollup.config.js</code></h4>
<p>要让它能够真正运行，我们需要更新 <code>rollup.config.js</code>。</p>
<p>在 <code>rollup.config.js</code> 中，我们需要 <code>import</code> Babel 插件，将它添加到一个新的配置选项 <code>plugins</code> 中，它会管控一个数组形式的插件列表。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Rollup plugins</span></span>
<span class="line"><span class="keyword">import</span> babel <span class="keyword">from</span> <span class="string">'rollup-plugin-babel'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line">  <span class="attr">entry</span>: <span class="string">'src/scripts/main.js'</span>,</span>
<span class="line">  <span class="attr">dest</span>: <span class="string">'build/js/main.min.js'</span>,</span>
<span class="line">  <span class="attr">format</span>: <span class="string">'iife'</span>,</span>
<span class="line">  <span class="attr">sourceMap</span>: <span class="string">'inline'</span>,</span>
<span class="line">  <span class="attr">plugins</span>: [</span>
<span class="line">    babel(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: <span class="string">'node_modules/**'</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">  ],</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>为了避免转译第三方脚本，我们需要设置一个 <code>exclude</code> 的配置选项来忽略掉 <code>node_modules</code> 目录</p>
<h4 id="jian-cha-shu-chu-de-bundle">检查输出的 bundle</h4>
<p>安装和配置完成后，我们可以重新构建 bundle:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">./node_modules/.bin/rollup -c</span>
<span class="line"></span>
<span class="line">// 译者注：若执行报错，运行 npm install --save-dev babel-preset-es2015 具体issue 详情见：https://github.com/jlengstorf/learn-rollup/issues/2</span>
</pre></td></tr></table></figure>
<p>当我们观察输出时，它看上去<strong>貌似没有什么</strong>改变。然而实际上它还是有一些细微的区别的：例如， <code>addArray()</code> 函数：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addArray = <span class="function"><span class="keyword">function</span> <span class="title">addArray</span>(<span class="params">arr</span>) </span>&#123;</span>
<span class="line">  <span class="keyword">var</span> result = arr.reduce(<span class="function"><span class="keyword">function</span> (<span class="params">a, b</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">return</span> a + b;</span>
<span class="line">  &#125;, <span class="number">0</span>);</span>
<span class="line"></span>
<span class="line">  <span class="keyword">return</span> result;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>这里可以看到 Babel 如何转换 <a href="https://strongloop.com/strongblog/an-introduction-to-javascript-es6-arrow-functions/" target="_blank" rel="external">箭头表示函数</a> (<code>arr.reduce((a, b) =&gt; a + b, 0)</code>) 到一个常规函数。</p>
<p>在转译运行完成后，程序执行的结果依然相同，但是代码已经支持到了 IE9 之前的浏览器。</p>
<p><strong>重点:</strong> Babel 也提供了 <a href="https://babeljs.io/docs/usage/polyfill/" target="_blank" rel="external"><code>babel-polyfill</code></a>，它可以让类似像 <code>Array.prototype.reduce()</code> 的代码可以在 IE8 以及更早的浏览器上能够得到顺利执行。</p>
<h3 id="bu-zou-3-tian-jia-es-lint-lai-jian-cha-tong-chang-de-java-script-cuo-wu">步骤 3: 添加 ESLint 来检查通常的 JavaScript 错误。</h3>
<p>在你的代码中使用 linter 无疑是十分好的决定，因为它会强制执行一致的编码规范来帮助你捕捉像是漏掉了括弧这种棘手的 bug。</p>
<p>在这个项目中，我们将会使用 <a href="http://eslint.org/" target="_blank" rel="external">ESLint</a>。</p>
<h4 id="an-zhuang-mo-kuai">安装模块</h4>
<p>为了使用 ESLint，我们将要安装 <a href="https://github.com/TrySound/rollup-plugin-eslint" target="_blank" rel="external">ESLint Rollup plugin</a></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install --save-dev rollup-plugin-eslint</span>
</pre></td></tr></table></figure>
<h4 id="sheng-cheng-yi-ge-code-eslintrc-json-code">生成一个 <code>.eslintrc.json</code>。</h4>
<p>为了确保我们只获取我们想要的错误，我们需要首先配置 ESLint。这里可以通过下面的代码来自动生成大多数配置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line">$ ./node_modules/.bin/eslint --init</span>
<span class="line">? How would you like to configure ESLint? Answer questions about your style</span>
<span class="line">? Are you using ECMAScript 6 features? Yes</span>
<span class="line">? Are you using ES6 modules? Yes</span>
<span class="line">? Where will your code run? Browser</span>
<span class="line">? Do you use CommonJS? No</span>
<span class="line">? Do you use JSX? No</span>
<span class="line">? What style of indentation do you use? Spaces</span>
<span class="line">? What quotes do you use for strings? Single</span>
<span class="line">? What line endings do you use? Unix</span>
<span class="line">? Do you require semicolons? Yes</span>
<span class="line">? What format do you want your config file to be in? JSON</span>
<span class="line">Successfully created .eslintrc.json file in /Users/jlengstorf/dev/code.lengstorf.com/projects/learn-rollup</span>
</pre></td></tr></table></figure>
<p>如果你回答了上述的问题，你将会在 <code>.eslintrc.json</code> 中获得以下输出内容：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
</pre></td><td class="code"><pre><span class="line">&#123;</span>
<span class="line">  <span class="attr">"env"</span>: &#123;</span>
<span class="line">    <span class="attr">"browser"</span>: <span class="literal">true</span>,</span>
<span class="line">    <span class="attr">"es6"</span>: <span class="literal">true</span></span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">"extends"</span>: <span class="string">"eslint:recommended"</span>,</span>
<span class="line">  <span class="attr">"parserOptions"</span>: &#123;</span>
<span class="line">    <span class="attr">"sourceType"</span>: <span class="string">"module"</span></span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">"rules"</span>: &#123;</span>
<span class="line">    <span class="attr">"indent"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="number">4</span></span>
<span class="line">    ],</span>
<span class="line">    <span class="attr">"linebreak-style"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="string">"unix"</span></span>
<span class="line">    ],</span>
<span class="line">    <span class="attr">"quotes"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="string">"single"</span></span>
<span class="line">    ],</span>
<span class="line">    <span class="attr">"semi"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="string">"always"</span></span>
<span class="line">    ]</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h4 id="diao-zheng-code-eslintrc-json-code">调整 <code>.eslintrc.json</code>。</h4>
<p>然而，我们还需要去做一些调整来避免我们的项目出现问题：</p>
<ol>
<li>
<p>我们要用 2 缩进符来代替 4 缩进符。</p>
</li>
<li>
<p>我们将使用一个全局变量 <code>ENV</code>，所以我们需要为它设置一个白名单。</p>
</li>
</ol>
<p>所以我们来做以下调整 —— 在你的 <code>.eslintrc.json</code> 中修改 <code>globals</code> 属性和 <code>indent</code> 属性：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
</pre></td><td class="code"><pre><span class="line">&#123;</span>
<span class="line">  <span class="attr">"env"</span>: &#123;</span>
<span class="line">    <span class="attr">"browser"</span>: <span class="literal">true</span>,</span>
<span class="line">    <span class="attr">"es6"</span>: <span class="literal">true</span></span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">"globals"</span>: &#123;</span>
<span class="line">    <span class="attr">"ENV"</span>: <span class="literal">true</span></span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">"extends"</span>: <span class="string">"eslint:recommended"</span>,</span>
<span class="line">  <span class="attr">"parserOptions"</span>: &#123;</span>
<span class="line">    <span class="attr">"sourceType"</span>: <span class="string">"module"</span></span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">"rules"</span>: &#123;</span>
<span class="line">    <span class="attr">"indent"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="number">2</span></span>
<span class="line">    ],</span>
<span class="line">    <span class="attr">"linebreak-style"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="string">"unix"</span></span>
<span class="line">    ],</span>
<span class="line">    <span class="attr">"quotes"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="string">"single"</span></span>
<span class="line">    ],</span>
<span class="line">    <span class="attr">"semi"</span>: [</span>
<span class="line">      <span class="string">"error"</span>,</span>
<span class="line">      <span class="string">"always"</span></span>
<span class="line">    ]</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<h4 id="geng-xin-code-rollup-config-js-code-1">更新 <code>rollup.config.js</code>。</h4>
<p>接下来，<code>import</code> ESLint 插件并将它添加到 Rollup 配置中：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Rollup plugins</span></span>
<span class="line"><span class="keyword">import</span> babel <span class="keyword">from</span> <span class="string">'rollup-plugin-babel'</span>;</span>
<span class="line"><span class="keyword">import</span> eslint <span class="keyword">from</span> <span class="string">'rollup-plugin-eslint'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line">  <span class="attr">entry</span>: <span class="string">'src/scripts/main.js'</span>,</span>
<span class="line">  <span class="attr">dest</span>: <span class="string">'build/js/main.min.js'</span>,</span>
<span class="line">  <span class="attr">format</span>: <span class="string">'iife'</span>,</span>
<span class="line">  <span class="attr">sourceMap</span>: <span class="string">'inline'</span>,</span>
<span class="line">  <span class="attr">plugins</span>: [</span>
<span class="line">    babel(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: <span class="string">'node_modules/**'</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">    eslint(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: [</span>
<span class="line">        <span class="string">'src/styles/**'</span>,</span>
<span class="line">      ]</span>
<span class="line">    &#125;),</span>
<span class="line">  ],</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<h4 id="jian-cha-kong-zhi-tai-shu-chu">检查控制台输出</h4>
<p>首先，我们运行 <code>./node_modules/.bin/rollup -c</code>，然而好像并没有发生什么，这是因为在标准设置下，应用代码已经顺利通过了 linter 的检查并且没有发现任何问题。</p>
<p>但是如果我们引入一个问题 —— 比如移除一个分号 —— 我们则会看到 ESLint 的帮助提示:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</pre></td><td class="code"><pre><span class="line">$ ./node_modules/.bin/rollup -c</span>
<span class="line"></span>
<span class="line">/Users/jlengstorf/dev/code.lengstorf.com/projects/learn-rollup/src/scripts/main.js</span>
<span class="line">  12:64  error  Missing semicolon  semi</span>
<span class="line"></span>
<span class="line">✖ 1 problem (1 error, 0 warnings)</span>
</pre></td></tr></table></figure>
<p>像这样一些无意间引入的神秘 bug 就会被立刻发现，帮助信息中也包含了文件名，行数以及列数。</p>
<p>尽管这并不会消除我们项目中所有需要调试的问题，但对明显的拼写错误和疏忽起到了相当大的帮助。<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></p>
<h3 id="bu-zou-4-tian-jia-cha-jian-lai-chu-li-fei-es-mo-kuai">步骤4：添加插件来处理非 ES 模块。</h3>
<p>如果你的依赖项使用了 Node 模式的模块那么下面的插件是很重要的。如果没有它，你将会在 <code>require</code> 时产生错误。</p>
<h4 id="tian-jia-node-mo-kuai-zuo-wei-yi-lai-xiang">添加 Node 模块作为依赖项。</h4>
<p>在这个示例项目中如果没有引用第三方模块的话将会变得很麻烦，但这并不是让你将第三方模块剪切到实际项目中。所以，为了使我们的 Rollup <strong>更方便使用</strong>，让我们为代码添加引用第三方模块的功能。</p>
<p>为了简单起见，我们将在代码中添加一个 <a href="https://www.npmjs.com/package/debug" target="_blank" rel="external"><code>debug</code></a> 包来简单的记录日志，通过下面的命令安装：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install --save debug</span>
</pre></td></tr></table></figure>
<p><strong>注意:</strong> 由于这将被引用到主项目中，使用 <code>--save</code> 参数是很重要的，这将避免在生产环境中由于 <code>devDependencies</code> 没有被安装而导致的错误。</p>
<p>然后，在 <code>src/scripts/main.js</code>中，我们添加一些简单的日志：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Import a couple modules for testing.</span></span>
<span class="line"><span class="keyword">import</span> &#123; sayHelloTo &#125; <span class="keyword">from</span> <span class="string">'./modules/mod1'</span>;</span>
<span class="line"><span class="keyword">import</span> addArray <span class="keyword">from</span> <span class="string">'./modules/mod2'</span>;</span>
<span class="line"></span>
<span class="line"><span class="comment">// Import a logger for easier debugging.</span></span>
<span class="line"><span class="keyword">import</span> debug <span class="keyword">from</span> <span class="string">'debug'</span>;</span>
<span class="line"><span class="keyword">const</span> log = debug(<span class="string">'app:log'</span>);</span>
<span class="line"></span>
<span class="line"><span class="comment">// Enable the logger.</span></span>
<span class="line">debug.enable(<span class="string">'*'</span>);</span>
<span class="line">log(<span class="string">'Logging is enabled!'</span>);</span>
<span class="line"></span>
<span class="line"><span class="comment">// Run some functions from our imported modules.</span></span>
<span class="line"><span class="keyword">const</span> result1 = sayHelloTo(<span class="string">'Jason'</span>);</span>
<span class="line"><span class="keyword">const</span> result2 = addArray([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]);</span>
<span class="line"></span>
<span class="line"><span class="comment">// Print the results on the page.</span></span>
<span class="line"><span class="keyword">const</span> printTarget = <span class="built_in">document</span>.getElementsByClassName(<span class="string">'debug__output'</span>)[<span class="number">0</span>];</span>
<span class="line"></span>
<span class="line">printTarget.innerText = <span class="string">`sayHelloTo('Jason') =&gt; <span class="subst">$&#123;result1&#125;</span>\n\n`</span>;</span>
<span class="line">printTarget.innerText += <span class="string">`addArray([1, 2, 3, 4]) =&gt; <span class="subst">$&#123;result2&#125;</span>`</span>;</span>
</pre></td></tr></table></figure>
<p>到目前为止一切顺利，但是当我们运行 rollup 时我们会得到一个警告：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">$ ./node_modules/.bin/rollup -c</span>
<span class="line">Treating &apos;debug&apos; as external dependency</span>
<span class="line">No name was provided for external module &apos;debug&apos; in options.globals – guessing &apos;debug&apos;</span>
</pre></td></tr></table></figure>
<p>如果我们再一次检查我们的 <code>index.html</code> ，我们会发现 <code>debug</code> 会抛出一个 <code>ReferenceError</code></p>
<p>通常情况下，第三方 Node 模块并不会被 Rollup 正确加载</p>
<p>这是由于 Node 模块使用的是 <a href="http://wiki.commonjs.org/wiki/Modules/1.1" target="_blank" rel="external">CommonJS</a>，它并不被 Rollup 兼容因此不能直接使用。为了解决它，我们需要添加一些插件来处理 Node 依赖和 CommonJS 模块。</p>
<h4 id="an-zhuang-mo-kuai-1">安装模块。</h4>
<p>为了解决这个问题，我们准备为 Rollup 添加两个插件:</p>
<ol>
<li>
<p><a href="https://github.com/rollup/rollup-plugin-node-resolve" target="_blank" rel="external"><code>rollup-plugin-node-resolve</code></a>, 它会允许加载在 <code>node_modules</code> 中的第三方模块。</p>
</li>
<li>
<p><a href="https://github.com/rollup/rollup-plugin-commonjs" target="_blank" rel="external"><code>rollup-plugin-commonjs</code></a>, 它会将 CommonJS 模块转换为 ES6,来为 Rollup 获得兼容。</p>
</li>
</ol>
<p>用下面的命令安装这两个插件：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install --save-dev rollup-plugin-node-resolve rollup-plugin-commonjs</span>
</pre></td></tr></table></figure>
<h4 id="geng-xin-code-rollup-config-js-code-2">更新 <code>rollup.config.js</code>.</h4>
<p>接下来，在 Rollup 配置中 <code>import</code> 来添加插件：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Rollup plugins</span></span>
<span class="line"><span class="keyword">import</span> babel <span class="keyword">from</span> <span class="string">'rollup-plugin-babel'</span>;</span>
<span class="line"><span class="keyword">import</span> eslint <span class="keyword">from</span> <span class="string">'rollup-plugin-eslint'</span>;</span>
<span class="line"><span class="keyword">import</span> resolve <span class="keyword">from</span> <span class="string">'rollup-plugin-node-resolve'</span>;</span>
<span class="line"><span class="keyword">import</span> commonjs <span class="keyword">from</span> <span class="string">'rollup-plugin-commonjs'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line">  <span class="attr">entry</span>: <span class="string">'src/scripts/main.js'</span>,</span>
<span class="line">  <span class="attr">dest</span>: <span class="string">'build/js/main.min.js'</span>,</span>
<span class="line">  <span class="attr">format</span>: <span class="string">'iife'</span>,</span>
<span class="line">  <span class="attr">sourceMap</span>: <span class="string">'inline'</span>,</span>
<span class="line">  <span class="attr">plugins</span>: [</span>
<span class="line">    resolve(&#123;</span>
<span class="line">      <span class="attr">jsnext</span>: <span class="literal">true</span>,</span>
<span class="line">      <span class="attr">main</span>: <span class="literal">true</span>,</span>
<span class="line">      <span class="attr">browser</span>: <span class="literal">true</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">    commonjs(),</span>
<span class="line">    eslint(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: [</span>
<span class="line">        <span class="string">'src/styles/**'</span>,</span>
<span class="line">      ]</span>
<span class="line">    &#125;),</span>
<span class="line">    babel(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: <span class="string">'node_modules/**'</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">  ],</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p><strong>注意：</strong> <code>jsnext</code> 属性是指定<a href="https://github.com/rollup/rollup/wiki/jsnext:main" target="_blank" rel="external">将 Node 包转换为 ES2015 模块</a>。<code>main</code> 和 <code>browser</code> 属性将使插件决定将哪些文件应用到 bundle。</p>
<h4 id="jian-cha-kong-zhi-tai-shu-chu-1">检查控制台输出。</h4>
<p>用 <code>./node_modules/.bin/rollup -c</code> 重新构建 bundle，然后在浏览器中再次检查输出：</p>
<p>好的！我们的日志现在正常展示了。</p>
<h3 id="bu-zou-5-tian-jia-cha-jian-lai-ti-dai-huan-jing-bian-liang">步骤5：添加插件来替代环境变量</h3>
<p>环境变量能为我们的开发流程提供很大的帮助，我们可以通过它来执行像是关闭或开启日志、注入开发环境脚本等功能。</p>
<p>所以让我们来确保 Rollup 能够使用这一特性。</p>
<h4 id="zai-code-main-js-code-zhong-tian-jia-ji-chu-pei-zhi-code-env-code">在 <code>main.js</code> 中添加基础配置 <code>ENV</code>。</h4>
<p>让我们添加一个环境变量来使我们的日志脚本只在非 <code>production</code> 环境下才会执行。在  <code>src/scripts/main.js</code> 中让我们改变 <code>log()</code> 初始化后的逻辑：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Import a logger for easier debugging.</span></span>
<span class="line"><span class="keyword">import</span> debug <span class="keyword">from</span> <span class="string">'debug'</span>;</span>
<span class="line"><span class="keyword">const</span> log = debug(<span class="string">'app:log'</span>);</span>
<span class="line"></span>
<span class="line"><span class="comment">// The logger should only be disabled if we’re not in production.</span></span>
<span class="line"><span class="keyword">if</span> (ENV !== <span class="string">'production'</span>) &#123;</span>
<span class="line"></span>
<span class="line">  <span class="comment">// Enable the logger.</span></span>
<span class="line">  debug.enable(<span class="string">'*'</span>);</span>
<span class="line">  log(<span class="string">'Logging is enabled!'</span>);</span>
<span class="line">&#125; <span class="keyword">else</span> &#123;</span>
<span class="line">  debug.disable();</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>然而，当我们重新构建我们的 bundle (<code>./node_modules/.bin/rollup -c</code>) 并检查浏览器，我们可以看到 <code>ENV</code> 报出了 <code>ReferenceError</code> 的错误。</p>
<p>这并不奇怪，因为我们并没有在所有位置定义它，我们试着运行 <code>ENV=production ./node_modules/.bin/rollup -c</code> ，然而它仍然没有正常工作。这是由于用这种方式设置环境变量只会对 Rollup 生效，对 Rollup 生成的 bundle 并不起作用。</p>
<p>我们仍需要一个插件来将我们的环境变量作用到 bundle 中。</p>
<h4 id="an-zhuang-mo-kuai-2">安装模块</h4>
<p>首先安装 <a href="https://github.com/rollup/rollup-plugin-replace" target="_blank" rel="external"><code>rollup-plugin-replace</code></a>,它本质上是一个用来查找和替换的工具。它可以做很多事，但对我们来说只需要找到目前的环境变量并用实际值来替代就可以了。（例如：在 bundle 中出现的所有 <code>ENV</code> 将被 <code>&quot;production&quot;</code> 替换）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install --save-dev rollup-plugin-replace</span>
</pre></td></tr></table></figure>
<h3 id="geng-xin-code-rollup-config-js-code-3">更新 <code>rollup.config.js</code></h3>
<p>在 <code>rollup.config.js</code> 中, 让我们 <code>import</code> 插件并添加到我们的插件列表里。</p>
<p>配置很简单：我们可以添加一个 <code>key:value</code> 的配对表，<code>key</code> 值是准备被替换的键值，而 <code>value</code> 是将要被替换的值。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Rollup plugins</span></span>
<span class="line"><span class="keyword">import</span> babel <span class="keyword">from</span> <span class="string">'rollup-plugin-babel'</span>;</span>
<span class="line"><span class="keyword">import</span> eslint <span class="keyword">from</span> <span class="string">'rollup-plugin-eslint'</span>;</span>
<span class="line"><span class="keyword">import</span> resolve <span class="keyword">from</span> <span class="string">'rollup-plugin-node-resolve'</span>;</span>
<span class="line"><span class="keyword">import</span> commonjs <span class="keyword">from</span> <span class="string">'rollup-plugin-commonjs'</span>;</span>
<span class="line"><span class="keyword">import</span> replace <span class="keyword">from</span> <span class="string">'rollup-plugin-replace'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line">  <span class="attr">entry</span>: <span class="string">'src/scripts/main.js'</span>,</span>
<span class="line">  <span class="attr">dest</span>: <span class="string">'build/js/main.min.js'</span>,</span>
<span class="line">  <span class="attr">format</span>: <span class="string">'iife'</span>,</span>
<span class="line">  <span class="attr">sourceMap</span>: <span class="string">'inline'</span>,</span>
<span class="line">  <span class="attr">plugins</span>: [</span>
<span class="line">    resolve(&#123;</span>
<span class="line">      <span class="attr">jsnext</span>: <span class="literal">true</span>,</span>
<span class="line">      <span class="attr">main</span>: <span class="literal">true</span>,</span>
<span class="line">      <span class="attr">browser</span>: <span class="literal">true</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">    commonjs(),</span>
<span class="line">    eslint(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: [</span>
<span class="line">        <span class="string">'src/styles/**'</span>,</span>
<span class="line">      ]</span>
<span class="line">    &#125;),</span>
<span class="line">    babel(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: <span class="string">'node_modules/**'</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">    replace(&#123;</span>
<span class="line">      <span class="attr">ENV</span>: <span class="built_in">JSON</span>.stringify(process.env.NODE_ENV || <span class="string">'development'</span>),</span>
<span class="line">    &#125;),</span>
<span class="line">  ],</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>在我们的配置中，我们会找到每一个 <code>ENV</code> 并用 <code>process.env.NODE_ENV</code> 去替换 —— 在 Node 应用或是 <code>development</code> 中我们会用传统的方式去设置环境。我们会使用 <code>JSON.stringify</code> 来确保值是双引号的，不像 <code>ENV</code> 这样。</p>
<h4 id="jian-cha-jie-guo">检查结果</h4>
<p>首先，重新构建 bundle 并在浏览器中检查。此时控制台应该像以前一样已经输出显示了 —— 这意味着我们的默认值被接受了。</p>
<p>为了查看真实的效果，让我们在 <code>production</code> 环境中运行下面代码</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">`NODE_ENV=production ./node_modules/.bin/rollup -c`</span>
</pre></td></tr></table></figure>
<p><strong>注意:</strong> 在 Windows 中，使用 <code>SET NODE_ENV=production ./node_modules/.bin/rollup -c</code> 来避免在设置环境变量时产生错误。</p>
<p>当我们重新加载浏览器时，在控制台中就不再有输出内容了：</p>
<p>这样我们通过零代码修改，仅使用一个环境变量就禁用了日志记录。</p>
<h3 id="bu-zou-6-tian-jia-uglify-js-lai-zui-xiao-hua-ya-suo-wo-men-sheng-cheng-jiao-ben-de-ti-ji">步骤 6: 添加 UglifyJS 来最小化压缩我们生成脚本的体积。</h3>
<p>本教程最后一步的任务是添加 UglifyJS 来最小化压缩 bundle。它可以通过移除注上释、缩短变量名、重整代码来<strong>极大程度的</strong>减少 bundle 的体积大小 —— 这样在一定程度降低了代码的可读性，但是在网络通信上变得更有效率。</p>
<h4 id="an-zhuang-cha-jian">安装插件。</h4>
<p>我们将会使用 <a href="https://github.com/mishoo/UglifyJS2/" target="_blank" rel="external">UglifyJS</a> 来压缩 bundle，这里我们通过 <a href="https://github.com/TrySound/rollup-plugin-uglify" target="_blank" rel="external"><code>rollup-plugin-uglify</code></a> 来实现。</p>
<p>用下面的命令来安装：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install --save-dev rollup-plugin-uglify</span>
</pre></td></tr></table></figure>
<h4 id="geng-xin-code-rollup-config-js-code-4">更新 <code>rollup.config.js</code></h4>
<p>接下来，让我们在 Rollup 配置中添加 Uglify 。然而，为了在开发中使代码更具可读性，让我们来设置只在生产环境中压缩混淆代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// Rollup plugins</span></span>
<span class="line"><span class="keyword">import</span> babel <span class="keyword">from</span> <span class="string">'rollup-plugin-babel'</span>;</span>
<span class="line"><span class="keyword">import</span> eslint <span class="keyword">from</span> <span class="string">'rollup-plugin-eslint'</span>;</span>
<span class="line"><span class="keyword">import</span> resolve <span class="keyword">from</span> <span class="string">'rollup-plugin-node-resolve'</span>;</span>
<span class="line"><span class="keyword">import</span> commonjs <span class="keyword">from</span> <span class="string">'rollup-plugin-commonjs'</span>;</span>
<span class="line"><span class="keyword">import</span> replace <span class="keyword">from</span> <span class="string">'rollup-plugin-replace'</span>;</span>
<span class="line"><span class="keyword">import</span> uglify <span class="keyword">from</span> <span class="string">'rollup-plugin-uglify'</span>;</span>
<span class="line"></span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line">  <span class="attr">entry</span>: <span class="string">'src/scripts/main.js'</span>,</span>
<span class="line">  <span class="attr">dest</span>: <span class="string">'build/js/main.min.js'</span>,</span>
<span class="line">  <span class="attr">format</span>: <span class="string">'iife'</span>,</span>
<span class="line">  <span class="attr">sourceMap</span>: <span class="string">'inline'</span>,</span>
<span class="line">  <span class="attr">plugins</span>: [</span>
<span class="line">    resolve(&#123;</span>
<span class="line">      <span class="attr">jsnext</span>: <span class="literal">true</span>,</span>
<span class="line">      <span class="attr">main</span>: <span class="literal">true</span>,</span>
<span class="line">      <span class="attr">browser</span>: <span class="literal">true</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">    commonjs(),</span>
<span class="line">    eslint(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: [</span>
<span class="line">        <span class="string">'src/styles/**'</span>,</span>
<span class="line">      ]</span>
<span class="line">    &#125;),</span>
<span class="line">    babel(&#123;</span>
<span class="line">      <span class="attr">exclude</span>: <span class="string">'node_modules/**'</span>,</span>
<span class="line">    &#125;),</span>
<span class="line">    replace(&#123;</span>
<span class="line">      <span class="attr">ENV</span>: <span class="built_in">JSON</span>.stringify(process.env.NODE_ENV || <span class="string">'development'</span>),</span>
<span class="line">    &#125;),</span>
<span class="line">    (process.env.NODE_ENV === <span class="string">'production'</span> &amp;&amp; uglify()),</span>
<span class="line">  ],</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>我们使用了<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Short-Circuit_Evaluation" target="_blank" rel="external">短路计算</a>策略，这是一种常见的（尽管<a href="http://stackoverflow.com/questions/5049006/using-s-short-circuiting-as-an-if-statement" target="_blank" rel="external">有些讨厌</a>)捷径来在条件性的情况下设置一个值。<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup></p>
<p>在我们的例子中，我们只会在 <code>NODE_ENV</code> 设置为 <code>production</code> 时加载 <code>uglify()</code>。</p>
<h4 id="jian-cha-zui-xiao-hua-bundle">检查最小化 bundle。</h4>
<p>当配置保存后，让我们设置 <code>NODE_ENV</code> 为 production 并运行 Rollup:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">`NODE_ENV=production ./node_modules/.bin/rollup -c`</span>
</pre></td></tr></table></figure>
<p><strong>注意:</strong> 在 Windows 中, 使用 <code>SET NODE_ENV=production ./node_modules/.bin/rollup -c</code> 来避免在设置环境变量时产生错误。</p>
<p>这样的输出并不整洁，但它的体积<strong>十分</strong>小。</p>
<p>在之前，我们的 bundle 大小是 42KB。 在通过 UglifyJS 运行后，它减少到了 29KB —— 这样我们在没有额外付出的情况下就节省了超过 30% 的文件体积。</p>
<h2 id="kuo-zhan-yue-du">扩展阅读</h2>
<ul>
<li>
<p><a href="https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/" target="_blank" rel="external">The cost of small modules</a> —— 是这篇文章让我对 Rollup 产生了兴趣，因为它展示了 Rollup 相比 webpack 和 Browserify 的一些显著的优势。</p>
</li>
<li>
<p><a href="http://rollupjs.org/guide/" target="_blank" rel="external">Rollup’s getting started guide</a></p>
</li>
<li>
<p><a href="https://github.com/rollup/rollup/wiki/Command-Line-Interface" target="_blank" rel="external">Rollup’s CLI docs</a></p>
</li>
<li>
<p><a href="https://github.com/rollup/rollup/wiki/Plugins" target="_blank" rel="external">A list of Rollup plugins</a></p>
</li>
</ul>
<hr>
<h2 id="you-wen-ti-you-xiang-fa-fa-xian-liao-yi-ge-bug">有问题？有想法？发现了一个BUG?</h2>
<p>本文中的代码被托管在 GitHub 上。你可以 <a href="https://github.com/jlengstorf/learn-rollup" target="_blank" rel="external">fork 项目</a> 来修改并测试它，<a href="https://github.com/jlengstorf/learn-rollup/issues" target="_blank" rel="external">开启一个 issue</a> 来报告 bug，或是 <a href="https://github.com/jlengstorf/learn-rollup/compare" target="_blank" rel="external">创建一个 PR</a>来提出改进或修改。</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>这是一个相当复杂难以理解的概念, 所以如果不能完全明白也不要感到有压力。简而言之，我们希望我们的代码能在我们的作用域内，从而避免与其他的脚本产生冲突。这里 IIFE 是一个包含我们的代码并在它自身作用域产生的<a href="http://skilldrick.co.uk/2011/04/closures-explained-with-javascript/" target="_blank" rel="external">闭包</a> <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
<li id="fn2" class="footnote-item"><p>重要的是需要记住，目前我们处理的是这样的一个小例子，这并不是很复杂就使文件大小大了一倍。在这时文件大小的比对是 3KB 和 8KB。 <a href="#fnref2" class="footnote-backref">↩</a></p>
</li>
<li id="fn3" class="footnote-item"><p>就像之前花费无数个小时去追踪一个 bug，最终发现原因仅仅是傻傻的变量名拼写错误一样。linter 对工作效率的提高十分明显，这并不是我们夸大其词。 <a href="#fnref3" class="footnote-backref">↩</a></p>
</li>
<li id="fn4" class="footnote-item"><p>例如，我们很常见到用这样的方式来指定默认值(例如: <code>var foo = maybeThisExists || 'default';</code>)。 <a href="#fnref4" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文链接 : &lt;a href=&quot;https://code.lengstorf.com/learn-rollup-js/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Tutorial: How to Bundle JavaScri
    
    </summary>
    
      <category term="前端工程" scheme="http://qcyoung.com/categories/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B/"/>
    
    
      <category term="Rollup" scheme="http://qcyoung.com/tags/Rollup/"/>
    
      <category term="webpack" scheme="http://qcyoung.com/tags/webpack/"/>
    
      <category term="构建工具" scheme="http://qcyoung.com/tags/%E6%9E%84%E5%BB%BA%E5%B7%A5%E5%85%B7/"/>
    
      <category term="前端工程" scheme="http://qcyoung.com/tags/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>【译】使用 currentColor 属性写出更好的 CSS 代码</title>
    <link href="http://qcyoung.com/2016/09/28/%E3%80%90%E8%AF%91%E3%80%91%E4%BD%BF%E7%94%A8%20currentColor%20%E5%B1%9E%E6%80%A7%E5%86%99%E5%87%BA%E6%9B%B4%E5%A5%BD%E7%9A%84%20CSS%20%E4%BB%A3%E7%A0%81/"/>
    <id>http://qcyoung.com/2016/09/28/【译】使用 currentColor 属性写出更好的 CSS 代码/</id>
    <published>2016-09-28T04:19:19.000Z</published>
    <updated>2016-10-13T06:22:21.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文地址：<a href="https://hashnode.com/post/writing-better-css-with-currentcolor-cit5mgva31co79c53ia20vetq" target="_blank" rel="external">Writing better CSS with currentColor</a><br>
原文作者：<a href="https://hashnode.com/@alkshendra" target="_blank" rel="external">Alkshendra Maurya</a><br>
译文出自：<a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a><br>
译者：<a href="http://qcyoung.com">yangzj1992</a><br>
校对者： <a href="https://github.com/llp0574" target="_blank" rel="external">linpu.li</a>, <a href="https://github.com/yifili09" target="_blank" rel="external">Nicolas(Yifei) Li</a><br>
首发于: <a href="http://gold.xitu.io/entry/57eb30bebf22ec0058898ee7/detail" target="_blank" rel="external">掘金</a></p>
</blockquote>
<p>总有一些极其强大的 CSS 属性在目前已经有了很好的浏览器支持，但却很少被开发者使用。 <code>currentColor</code> 就是这样的属性之一。</p>
<p>MDN 把 currentColor <a href="https://developer.mozilla.org/en/docs/Web/CSS/color_value#currentColor_keyword" target="_blank" rel="external">定义为</a>:</p>
<blockquote>
<p><code>currentColor</code> 代表了当前元素被应用上的 color 颜色值。它允许让继承自属性或子元素属性的 color 属性为默认值而不再继承。</p>
</blockquote>
<p>在本文中，我们将通过一些有趣的方式来概述如何使用 CSS <code>currentColor</code> 这一关键字。</p>
<hr>
<h2 id="jie-shao">介绍</h2>
<p><code>currentColor</code> 关键字按某种规则获取了 color 属性的值并赋值给了自身。</p>
<p>在任何你想要默认继承 <code>color</code> 属性值的地方都可以使用 <code>currentColor</code> 这一关键字。这样当你改变 <code>color</code> 关键字的属性值时，它会自动的通过规则反映在所有 <code>currentColor</code> 关键字使用的地方。这难道不是很棒吗？😀</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box</span> &#123;</span>
<span class="line">    <span class="attribute">color</span>: red;</span>
<span class="line">    <span class="attribute">border</span>: <span class="number">1px</span> solid currentColor;</span>
<span class="line">    <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">2px</span> <span class="number">2px</span> currentColor;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>在上面的代码片段里，你可以看到我们不是在所有的地方都重复相同的 color 值，而是用 currentColor 来代替。这使得 CSS 变得更加容易管理，你将不再需要在不同的地方来追踪 color 值</p>
<hr>
<h2 id="ge-chong-yong-fa">各种用法</h2>
<p>来看一下 <code>currentColor</code> 可能的用例和例子:</p>
<p><strong>简化 color 定义</strong></p>
<p>像链接，边框，图标以及阴影的值总是随着它们的父元素 color 值保持一致，这可以通过简化的 currentColor 来替换一遍又一遍的特定 color 值；从而使代码更加易于管理。</p>
<p>例如:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
</pre></td><td class="code"><pre><span class="line">.box &#123;</span>
<span class="line">    color: red;</span>
<span class="line">&#125;</span>
<span class="line">.box .child-1 &#123;</span>
<span class="line">    background: currentColor;</span>
<span class="line">&#125;</span>
<span class="line">.box .child-2 &#123;</span>
<span class="line">    color: currentColor;</span>
<span class="line">    border 1px solid currentColor;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>在上面的代码片段中，你可以看到我们不是在边框、阴影上指定一个颜色，而是在这些属性上使用了 <code>currentColor</code>，这将使它们自动变为 <code>red</code>。</p>
<p><strong>简化过渡和动画</strong></p>
<p>currentColor 可以使 transitions 和 animations 变得更加简单。</p>
<p>让我们考虑一下最早的代码示例，并且改变一下 hover 时的 <code>color</code> 值。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box</span><span class="selector-pseudo">:hover</span> &#123;</span>
<span class="line">    <span class="attribute">color</span>: purple;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>这里，我们不需要再在 <code>:hover</code> 里写三个不同的属性，我们只需改变 <code>color</code> 值；所有使用 <code>currentColor</code> 的属性会自动在 hover 时发生改变。</p>
<p><strong>在伪元素上使用</strong></p>
<p>像是<code>:before</code> 和 <code>:after</code> 这样的伪元素也同样可以通过用 currentColor 来获取它的父元素的值。这就可以用于创建带有动态颜色的『提示框』，或是使用 body 颜色的『覆盖层』，并给它一个半透明的效果。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box</span> &#123;</span>
<span class="line">    <span class="attribute">color</span>: red;</span>
<span class="line">&#125;</span>
<span class="line"><span class="selector-class">.box</span><span class="selector-pseudo">:before</span> &#123;</span>
<span class="line">    <span class="attribute">color</span>: currentColor;</span>
<span class="line">    <span class="attribute">border</span>: <span class="number">1px</span> solid currentColor;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>这里，<code>:before</code> 伪元素的 <code>color</code> 和 <code>border-color</code> 会从父元素 div 中获得并可以被组建成类似提示框的东西。</p>
<p><strong>在 SVG 中使用</strong></p>
<p>SVG 中 <code>currentColor</code> 的值同样可以从父元素中获取。当你在不同地方应用 SVG 并想从父元素中继承 color 值而又不想每次明确提及时，使用它是相当有帮助的。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-tag">svg</span> &#123;</span>
<span class="line">    <span class="attribute">fill</span>: currentColor;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>在这里，svg 将会使用与它父元素相同的填充颜色，并且会动态的随着父元素颜色的修改而发生变化。</p>
<p><strong>在渐变中使用</strong></p>
<p><code>currentColor</code> 可以同样用于创建 CSS 渐变，其中渐变属性的一部分可以被设置成父元素的 <code>currentColor</code> 。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box</span> &#123;</span>
<span class="line">    <span class="attribute">background</span>: <span class="built_in">linear-gradient</span>(top bottom right, currentColor, #FFFFFF);</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>在这里，<strong>顶部</strong>的渐变颜色将会总是与父元素保持一致。虽然在这种情况下只会有一个动态颜色的限制，但对基于父元素颜色来生成动态的渐变来说，这仍然是一个简洁的方法。</p>
<p>这儿有一个 <a href="http://codepen.io/alkshendra/pen/xEVrJJ?editors=1100#0" target="_blank" rel="external">Codepen 示例</a>来演示上述的所有例子。</p>
<hr>
<h2 id="liu-lan-qi-zhi-chi">浏览器支持</h2>
<p>CSS <code>currentColor</code> 是从 CSS3 引入 SVG 规范时产生的，自 2003 年以来一直存在。因此浏览器对 <code>currentColor</code> 的支持是很可靠的，除了 IE8 和一些更低版本的浏览器。</p>
<p>下面这张图展示了目前有关浏览器支持情况的信息，信息来自 <a href="http://caniuse.com/#feat=currentcolor" target="_blank" rel="external">caniuse.com</a>:</p>
<p><img src="https://res.cloudinary.com/hashnode/image/upload/v1474021764/g03f4hx1ftb0frtoonfw.png" alt="currentColor Support"></p>
<hr>
<h2 id="jie-lun">结论</h2>
<p>CSS <code>currentColor</code> 尽管是一个很好的特性，但还尚未得到充分运用。它提供了很棒的支持并带来了相当的可能性来使你保持你的代码更加的整洁。</p>
<p>尽管 CSS 变量有它自己的方式，但是养成使用 <code>currentColor</code> 的习惯还是很酷的。</p>
<p>这只是一个我发现的很有趣的简单的话题，如果有人也对此话题感兴趣。请让我知道你的想法并在下面留言！😊</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文地址：&lt;a href=&quot;https://hashnode.com/post/writing-better-css-with-currentcolor-cit5mgva31co79c53ia20vetq&quot; target=&quot;_blank&quot; rel=
    
    </summary>
    
      <category term="CSS" scheme="http://qcyoung.com/categories/CSS/"/>
    
    
      <category term="CSS" scheme="http://qcyoung.com/tags/CSS/"/>
    
  </entry>
  
  <entry>
    <title>从极客公园视频感慨每个产品的背后</title>
    <link href="http://qcyoung.com/2016/09/19/%E4%BB%8E%E6%9E%81%E5%AE%A2%E5%85%AC%E5%9B%AD%E8%A7%86%E9%A2%91%E6%84%9F%E6%85%A8%E6%AF%8F%E4%B8%AA%E4%BA%A7%E5%93%81%E7%9A%84%E8%83%8C%E5%90%8E/"/>
    <id>http://qcyoung.com/2016/09/19/从极客公园视频感慨每个产品的背后/</id>
    <published>2016-09-19T04:55:18.000Z</published>
    <updated>2018-11-25T09:09:09.199Z</updated>
    
    <content type="html"><![CDATA[<h2 id="shi-pin">视频</h2>
<h3 id="2011-nian-ji-ke-gong-yuan-chuang-xin-da-hui">2011年极客公园创新大会</h3>
<iframe height="498" width="510" src="http://player.youku.com/embed/XMzM5NzA0NTQ0" frameborder="0" 'allowfullscreen'=""></iframe>
<h3 id="2013-nian-ji-ke-gong-yuan-chuang-xin-da-hui">2013年极客公园创新大会</h3>
<iframe height="498" width="510" src="http://player.youku.com/embed/XNDk3MDc2Mzk2" frameborder="0" 'allowfullscreen'=""></iframe>
<h2 id="gan-kai">感慨</h2>
<p>前两天不经意间又看到了极客公园几年前发布的创新大会的宣传视频。不禁还是有些感慨。</p>
<p>当时我还在读大学，看到的是 2013 年版的极客公园宣传视频，后来又看到了 2011 年版的。这两个视频里各个团队所展现的青春与活力一直是我当时很向往的。在当时，这些产品也或多或少算是一时风头的新兴人气产品。然而随着时间的流逝，也正如视频里所说的那样：很少有产品能一直流行。</p>
<p>这里面的一些产品，可能有的也真的只是昙花一现，能坚持到现在还绽放傲人成绩的并不多。在工作之后也是更深刻的懂得了每个产品的背后真的是许多研发产品运营团队的同事们的共同努力和支撑所维系的。做好一个让公司满意、让用户满意的产品真的很不容易。</p>
<p>不管它们最终的结局时怎样，是犯了哪些错误导致了产品的落幕，然而在当时那几年，它们确实是同类产品中的佼佼者，我们的世界也确实因为它们的存在而与众不同过。</p>
<p>刚实习时，老大也曾对我说过，一个优秀的工程师一定要对自己手上的产品负责。在这里也再次勉励自己，希望在自己的研发生涯里能尽可能的让自己所负责的产品尽可能的更优秀。</p>
<h2 id="fu-zhu">附注</h2>
<p>这里也晒一张我所在的团队的照片，但也是去年12月的照片了。之后团队因为公司和个人的原因也换组、调离、出出入入了不少人。但觉得至少这张照片很能展现当时我们团队阳光、青春的模样，也以此作纪念。</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/weixin_mall_20151203.jpg" alt="团队合照"></p>
<p>最后，两个视频的 BGM 也很不错，很喜欢它们的歌词和旋律。分别是在格莱美获奖的 Rascal Flatts -《Bless The Broken Road》和 Taylor Swift -《Long Live》</p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;shi-pin&quot;&gt;视频&lt;/h2&gt;
&lt;h3 id=&quot;2011-nian-ji-ke-gong-yuan-chuang-xin-da-hui&quot;&gt;2011年极客公园创新大会&lt;/h3&gt;
&lt;iframe height=&quot;498&quot; width=&quot;510&quot; src=&quot;http:
    
    </summary>
    
      <category term="产品" scheme="http://qcyoung.com/categories/%E4%BA%A7%E5%93%81/"/>
    
    
      <category term="我的生活" scheme="http://qcyoung.com/tags/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    
      <category term="产品" scheme="http://qcyoung.com/tags/%E4%BA%A7%E5%93%81/"/>
    
      <category term="team" scheme="http://qcyoung.com/tags/team/"/>
    
      <category term="个人感受" scheme="http://qcyoung.com/tags/%E4%B8%AA%E4%BA%BA%E6%84%9F%E5%8F%97/"/>
    
  </entry>
  
  <entry>
    <title>ssh_exchange_identification:Connection closed by remote host 报错问题</title>
    <link href="http://qcyoung.com/2016/08/28/ssh_exchange_identification:%20Connection%20closed%20by%20remote%20host%20%E6%8A%A5%E9%94%99%E9%97%AE%E9%A2%98/"/>
    <id>http://qcyoung.com/2016/08/28/ssh_exchange_identification: Connection closed by remote host 报错问题/</id>
    <published>2016-08-28T14:55:11.000Z</published>
    <updated>2018-11-25T09:09:09.200Z</updated>
    
    <content type="html"><![CDATA[<h2 id="cuo-wu-qing-kuang">错误情况</h2>
<p>今天下午，我莫名无法向 GitHub push 代码了...最终从5点多调试到晚上11点半..感觉略坑..遂记录如下..</p>
<p>报错内容：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">fatal: unable to access <span class="string">'https://***.git/'</span>: SSL peer handshake failed, the server most likely requires a client certificate to connect</span>
</pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
</pre></td><td class="code"><pre><span class="line">ssh_exchange_identification: Connection closed by remote host</span>
<span class="line">fatal: Could not <span class="built_in">read</span> from remote repository.</span>
</pre></td></tr></table></figure>
<h2 id="jie-jue-guo-cheng">解决过程</h2>
<p>首先简单搜了一下发现可能是 ssh key 的问题..遂重新按照<a href="https://help.github.com/articles/generating-an-ssh-key/" target="_blank" rel="external">官方文档</a>重新生成 ssh key。并再添加 key 后执行 <code>ssh -T git@github.com</code> 来测试，仍然报错:
<code>ssh_exchange_identification: Connection closed by remote host</code></p>
<p>执行<code>ssh -vT git@github.com</code>输出记录如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
</pre></td><td class="code"><pre><span class="line">$ ssh -vT git@github.com</span>
<span class="line">OpenSSH_6.9p1, LibreSSL 2.1.8</span>
<span class="line">debug1: Reading configuration data /etc/ssh/ssh_config</span>
<span class="line">debug1: /etc/ssh/ssh_config line 21: Applying options for *</span>
<span class="line">debug1: Connecting to github.com [1.111.11.111] port 22.</span>
<span class="line">debug1: Connection established.</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_rsa type 1</span>
<span class="line">debug1: key_load_public: No such file or directory</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_rsa-cert type -1</span>
<span class="line">debug1: key_load_public: No such file or directory</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_dsa type -1</span>
<span class="line">debug1: key_load_public: No such file or directory</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_dsa-cert type -1</span>
<span class="line">debug1: key_load_public: No such file or directory</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_ecdsa type -1</span>
<span class="line">debug1: key_load_public: No such file or directory</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_ecdsa-cert type -1</span>
<span class="line">debug1: key_load_public: No such file or directory</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_ed25519 type -1</span>
<span class="line">debug1: key_load_public: No such file or directory</span>
<span class="line">debug1: identity file /Users/yangzhongjing/.ssh/id_ed25519-cert type -1</span>
<span class="line">debug1: Enabling compatibility mode for protocol 2.0</span>
<span class="line">debug1: Local version string SSH-2.0-OpenSSH_6.9</span>
<span class="line">ssh_exchange_identification: Connection closed by remote host</span>
</pre></td></tr></table></figure>
<p>这里面<code>/Users/yangzhongjing/.ssh/id_rsa</code> 这个文件是存在的。不知道为什么会报：<code>key_load_public: No such file or directory</code>..</p>
<p>后来在网上搜了很多内容，执行了以下方法：</p>
<ul>
<li><code>vim /etc/hosts.allow</code> 添加 <code>sshd: ALL</code></li>
<li><code>ssh-add ~/.ssh/id_rsa</code></li>
<li>调整了 <code>/etc/ssh/sshd_config</code> 中的 <code>MaxSessions 10</code> 调大</li>
<li>删除 <code>~/.ssh</code>目录重新生成ssh key</li>
</ul>
<p>然而并没有什么卵用...</p>
<h2 id="zui-zhong-yuan-yin">最终原因..</h2>
<p>最后在茫茫文海中搜到了一篇关于路由器端口禁用也可能会导致这个问题的留言..突然想到昨天我自己也调试了家里的路由器，添加了一些路由器插件...赶紧切到路由器后台，发现了幕后凶手...</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/shijietong.png" alt=""></p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/shijietongdetail.png" alt=""></p>
<p>最终..禁用插件，重启路由。问题解决..</p>
<p>SO...后来人可以试试上面列举到的搜索的方法..或者看看你的网络设置，一般可以解决这一报错问题..</p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;cuo-wu-qing-kuang&quot;&gt;错误情况&lt;/h2&gt;
&lt;p&gt;今天下午，我莫名无法向 GitHub push 代码了...最终从5点多调试到晚上11点半..感觉略坑..遂记录如下..&lt;/p&gt;
&lt;p&gt;报错内容：&lt;/p&gt;
&lt;figure class=&quot;highlig
    
    </summary>
    
      <category term="bug" scheme="http://qcyoung.com/categories/bug/"/>
    
    
      <category term="GitHub" scheme="http://qcyoung.com/tags/GitHub/"/>
    
      <category term="SSH" scheme="http://qcyoung.com/tags/SSH/"/>
    
      <category term="Debug" scheme="http://qcyoung.com/tags/Debug/"/>
    
  </entry>
  
  <entry>
    <title>【译】JavaScript 变量的生命周期：为什么 let 不存在变量提升</title>
    <link href="http://qcyoung.com/2016/08/03/%E3%80%90%E8%AF%91%E3%80%91JavaScript%20%E5%8F%98%E9%87%8F%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%EF%BC%9A%E4%B8%BA%E4%BB%80%E4%B9%88%20let%20%E4%B8%8D%E5%AD%98%E5%9C%A8%E5%8F%98%E9%87%8F%E6%8F%90%E5%8D%87/"/>
    <id>http://qcyoung.com/2016/08/03/【译】JavaScript 变量的生命周期：为什么 let 不存在变量提升/</id>
    <published>2016-08-03T05:21:20.000Z</published>
    <updated>2016-10-24T05:20:36.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文链接 : <a href="https://rainsoft.io/variables-lifecycle-and-why-let-is-not-hoisted/" target="_blank" rel="external">JavaScript variables lifecycle: why let is not hoisted</a><br>
原文作者 : <a href="https://rainsoft.io/author/dmitri-pavlutin/" target="_blank" rel="external">Dmitri Pavlutin</a><br>
译文出自 : <a href="http://zcfy.cc/" target="_blank" rel="external">众成翻译</a><br>
译者 : <a href="http://qcyoung.com">yangzj1992</a><br>
校对者: <a href="https://www.zhihu.com/people/ha-ha-qiu-52" target="_blank" rel="external">lisa</a><br>
首发于: <a href="http://zcfy.cc/article/javascript-variables-lifecycle-why-let-is-not-hoisted-976.html" target="_blank" rel="external">众成翻译</a></p>
</blockquote>
<p>变量提升是一个将变量或者声明函数提升到作用域起始处的过程，通常指的是变量声明 <code>var</code> 和函数声明 <code>function fun() {...}</code></p>
<p>当 <code>let</code>（以及具备了和 <code>let</code> 相似声明行为的 <code>const</code> 和 <code>class</code>）等声明方式在 ES2015 中被引入后，许多的开发者包括我都使用了<em>变量提升</em>的定义来描述变量是如何被访问的。但经过对这个问题更多的搜索后，我十分惊讶的发现<em>变量提升</em>并不是可以用来准确描述 <code>let</code> 变量初始化和可用性的合适术语。</p>
<p>ES2015 为 <code>let</code> 提供了一个不同的改进机制。它要求了更严格的变量声明方式（你在定义变量前是无法访问它的）并且这也在结果上保证了更好的代码质量。</p>
<p>现在让我们一起深入了解关于这个过程的更多细节。</p>
<h3 id="rong-yi-chu-cuo-de-code-var-code-bian-liang-ti-sheng">容易出错的 <code>var</code> 变量提升</h3>
<p>有时我会在作用域下的任何位置上看到一个奇怪的变量声明 <code>var varname</code> 和函数声明 <code>function funName() {...}</code> 。</p>
<p><a href="http://jsbin.com/fewiri/1/edit?js,console" target="_blank" rel="external">Try in JS Bin</a></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// var hoisting</span></span>
<span class="line">num;     <span class="comment">// =&gt; undefined</span></span>
<span class="line"><span class="keyword">var</span> num;</span>
<span class="line">num = <span class="number">10</span>;</span>
<span class="line">num;     <span class="comment">// =&gt; 10</span></span>
<span class="line"><span class="comment">// function hoisting</span></span>
<span class="line">getPi;   <span class="comment">// =&gt; function getPi() &#123;...&#125;</span></span>
<span class="line">getPi(); <span class="comment">// =&gt; 3.14</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getPi</span>(<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="keyword">return</span> <span class="number">3.14</span>;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>变量 <code>num</code> 在它的声明语句 <code>var num</code> 之前就被访问了，所以它的值为 <code>undefined</code></p>
<p>函数 <code>function getPi() {...}</code> 是定义在文件的末尾的。然而函数可以在它声明 <code>getPi()</code> 之前就被调用，因为它被提升到了作用域的顶部。</p>
<p>这就是典型的<em>变量提升</em>。</p>
<p>事实证明，在首次使用变量或函数后才声明变量或函数会很容易产生困惑。假设你正滚动查看一个大文件，然后发现了一个未声明的变量...你肯定会想它到底为什么在这里出现并且它是在哪定义的呢？</p>
<p>当然一个熟练的 JavaScript 开发者并不会这样编写代码。但在成千上万个 JavaScript Github 库中却可能存在着相当数量的这样的代码。</p>
<p>甚至在上面给出的代码示例中，我们也很难去明白代码中的声明流程。</p>
<p>我们应当自然地首先声明或是描述一个未知的术语。在这之后再对它进行使用。<code>let</code> 便是鼓励你遵循这种方法来设置变量。</p>
<h3 id="shen-ceng-nei-rong-bian-liang-de-sheng-ming-zhou-qi">深层内容: 变量的生命周期</h3>
<p>当引擎使用变量时，它们的生命周期包含以下阶段：</p>
<ol>
<li>
<p><strong>声明阶段</strong> 这一阶段在作用域中注册了一个变量。</p>
</li>
<li>
<p><strong>初始化阶段</strong> 这一阶段分配了内存并在作用域中让内存与变量建立了一个绑定。在这一步变量会被自动初始化为 <code>undefined</code> 。</p>
</li>
<li>
<p><strong>赋值阶段</strong> 这一阶段为初始化变量分配具体的一个值。</p>
</li>
</ol>
<p>一个变量在通过声明阶段时它还是处于 <strong>未初始化的</strong> 状态，这时它仍然还没有到达初始化阶段。</p>
<p><img src="http://p0.qhimg.com/t01ea63a0e0c145b0f3.jpg" alt="Infographic"></p>
<p>注意，按照变量的生命周期过程，<em>声明阶段</em>与我们通常所说的<em>变量声明</em>是不同的术语。简单来讲，引擎处理变量声明需要经过完整的这 3 个阶段：声明阶段，初始化阶段和赋值阶段。</p>
<h3 id="code-var-code-bian-liang-de-sheng-ming-zhou-qi"><code>var</code> 变量的生命周期</h3>
<p>稍微熟悉下这些生命周期阶段，现在让我们用它们来描述引擎是如何处理 <code>var</code> 变量的。</p>
<p><img src="http://p0.qhimg.com/t014b5f030a2ed83760.jpg" alt="Infographic"></p>
<p>假设一个场景，当 JavaScript 遇到了一个函数作用域，其中包含了 <code>var variable</code> 的语句。则在任何语句执行之前，这个变量在作用域的开头就通过了<em>声明阶段</em>并马上来到了<em>初始化阶段</em>（步骤一）。</p>
<p>同时 <code>var variable</code> 在函数作用域中的位置并不会影响它的声明和初始化阶段的进行。</p>
<p>在声明和初始化阶段之后，赋值阶段之前，变量的值便是 <code>undefined</code> 并已经可以被使用了。</p>
<p>在<em>赋值阶段</em> <code>variable = 'value'</code> 语句使变量接受了它的初始化值（步骤二）。</p>
<p>这里的<em>变量提升</em>严格的说是指变量在函数作用域的<em>开始位置就完成了声明和初始化阶段</em>。在这里这两个阶段之间并没有任何的间隙。</p>
<p>让我们参考一个示例来研究。下面的代码创建了一个包含 <code>var</code> 语句的函数作用域：</p>
<p><a href="http://jsbin.com/karuxe/3/edit?js,console" target="_blank" rel="external">Try in JS Bin</a></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">multiplyByTen</span>(<span class="params">number</span>) </span>&#123;</span>
<span class="line">  <span class="built_in">console</span>.log(ten); <span class="comment">// =&gt; undefined</span></span>
<span class="line">  <span class="keyword">var</span> ten;</span>
<span class="line">  ten = <span class="number">10</span>;</span>
<span class="line">  <span class="built_in">console</span>.log(ten); <span class="comment">// =&gt; 10</span></span>
<span class="line">  <span class="keyword">return</span> number * ten;</span>
<span class="line">&#125;</span>
<span class="line">multiplyByTen(<span class="number">4</span>); <span class="comment">// =&gt; 40</span></span>
</pre></td></tr></table></figure>
<p>当 JavaScript 开始执行 <code>multipleByTen(4)</code> 时进入了函数作用域中，变量 <code>ten</code> 在第一个语句之前就经过了声明和初始化阶段，所以当调用 <code>console.log(ten)</code> 时打印为 <code>undefined</code>。</p>
<p>当语句 <code>ten = 10</code> 为变量赋值了初始化值。在赋值后，语句 <code>console.log(ten)</code> 打印了正确的 <code>10</code> 值。</p>
<h3 id="han-shu-sheng-ming-de-sheng-ming-zhou-qi">函数声明的生命周期</h3>
<p>对于一个 <em>函数声明语句</em> <code>function funName() {...}</code> 那就更简单了。</p>
<p><img src="http://p0.qhimg.com/t0134485377a7130217.jpg" alt="Infographic"></p>
<p><em>声明、初始化和赋值阶段</em>在封闭的函数作用域的开头便立刻进行（只有一步）。 <code>funName()</code> 可以在作用域中的任意位置被调用，这与其声明语句所在的位置无关（它甚至可以被放在程序的最底部）。</p>
<p>下面的代码是一个函数提升的演示：</p>
<p><a href="http://jsbin.com/ximedo/2/edit?js,console" target="_blank" rel="external">Try in JS Bin</a></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sumArray</span>(<span class="params">array</span>) </span>&#123;</span>
<span class="line">  <span class="keyword">return</span> array.reduce(sum);</span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params">a, b</span>) </span>&#123;</span>
<span class="line">    <span class="keyword">return</span> a + b;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;</span>
<span class="line">sumArray([<span class="number">5</span>, <span class="number">10</span>, <span class="number">8</span>]); <span class="comment">// =&gt; 23</span></span>
</pre></td></tr></table></figure>
<p>当 JavaScript 执行 <code>sumArray([5, 10, 8])</code> 时，它便进入了 <code>sumArray</code> 的函数作用域。在作用域内，任何语句执行之前的瞬间，<code>sum</code> 就经过了所有的三个阶段：声明，初始化和赋值阶段。</p>
<p>这样 <code>array.reduce(sum)</code> 即使在它的声明语句 <code>function sum(a, b) {...}</code> 之前也可以使用 <code>sum</code>。</p>
<h3 id="code-let-code-bian-liang-de-sheng-ming-zhou-qi"><code>let</code> 变量的生命周期</h3>
<p><code>let</code> 变量的处理方式不同于 <code>var</code>。它的主要区分点在于声明和初始化阶段是分开的。</p>
<p><img src="http://p0.qhimg.com/t0122846bdbec4513d7.jpg" alt="Infographic"></p>
<p>现在让我们研究这样一个场景，当解释器进入了一个包含 <code>let variable</code> 语句的块级作用域中。这个变量立即通过了<em>声明阶段</em>，并在作用域内注册了它的名称（步骤一）。</p>
<p>然后解释器继续逐行解析块语句。</p>
<p>这时如果你在这个阶段尝试访问 <code>variable</code>，JavaScript 将会抛出 <code>ReferenceError: variable is not defined</code>。因为这个变量的状态依然是<em>未初始化</em>的。</p>
<p>此时 <code>variable</code> 处于<em>临时死区</em>中。</p>
<p>当解释器到达语句 <code>let variable</code> 时，此时变量通过了初始化阶段（步骤二）。现在变量状态是<em>初始化的</em>并且访问它的值是 <code>undefined</code>。</p>
<p>同时变量在此时也离开了<em>临时死区</em>。</p>
<p>之后当到达赋值语句 <code>variable = 'value'</code> 时，变量通过了赋值阶段（步骤三）。</p>
<p>如果 JavaScript 遇到这样的语句 <code>let variable = 'value'</code> ，那么变量会在这一条语句中同时经过初始化和赋值阶段。</p>
<p>让我们继续看一个示例。这里 <code>let</code> 变量 <code>number</code> 被创建在了一个块级作用域中：</p>
<p><a href="http://jsbin.com/qixoko/2/edit?js,console" target="_blank" rel="external">Try in JS Bin</a></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> condition = <span class="literal">true</span>;</span>
<span class="line"><span class="keyword">if</span> (condition) &#123;</span>
<span class="line">  <span class="comment">// console.log(number); // =&gt; Throws ReferenceError</span></span>
<span class="line">  <span class="keyword">let</span> number;</span>
<span class="line">  <span class="built_in">console</span>.log(number); <span class="comment">// =&gt; undefined</span></span>
<span class="line">  number = <span class="number">5</span>;</span>
<span class="line">  <span class="built_in">console</span>.log(number); <span class="comment">// =&gt; 5</span></span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>当 JavaScript 进入 <code>if (condition) {...}</code> 块级作用域中，<code>number</code> 立即通过了声明阶段。</p>
<p>因为 <code>number</code> 尚未初始化并且处于临时死区，此时试图访问该变量会抛出 <code>ReferenceError: number is not defined</code>.</p>
<p>之后语句 <code>let number</code> 使其得以初始化。现在变量可以被访问，但它的值是 <code>undefined</code>。</p>
<p>之后赋值语句 <code>number = 5</code> 当然也使变量经过了赋值阶段。</p>
<p><code>const</code> 和 <code>class</code> 类型与 <code>let</code> 有着相同的生命周期，除了它们的赋值语句只会发生一次。</p>
<h4 id="wei-shi-yao-bian-liang-ti-sheng-zai-code-let-code-de-sheng-ming-zhou-qi-zhong-wu-xiao">为什么变量提升在 <code>let</code> 的生命周期中无效</h4>
<p>如上所述，<em>变量提升</em>是变量的<em>耦合</em>声明并且在作用域的顶部完成初始化。</p>
<p>然而 <code>let</code> 生命周期中将声明和初始化阶段<em>解耦</em>。这一解耦使 <code>let</code> 的<em>变量提升</em>现象消失。</p>
<p>由于两个阶段之间的间隙创建了临时死区，在此时变量无法被访问。</p>
<p>这就像科幻的风格一样，在 <code>let</code> 生命周期中由于<em>变量提升</em>失效所以产生了临时死区。</p>
<h3 id="jie-lun">结论</h3>
<p>使用 <code>var</code> 自由的去声明变量很容易出现错误。</p>
<p>基于这一点，ES2015 引进了 <code>let</code>。它使用了一种改进的算法来声明变量并添加了块作用域。</p>
<p>因为声明和初始化阶段是解耦的，变量提升对于 <code>let</code> 变量（也包括 <code>const</code> 和 <code>class</code>)是无效的。在初始化之前，变量处于临时死区中并不可被访问。</p>
<p>为了保证平稳的变量声明，推荐这些技巧以供参考：</p>
<ul>
<li>
<p>声明，初始化变量后再使用变量。这个流程才是正确并易于遵循的。</p>
</li>
<li>
<p>尽可能的减少变量数。你暴露的变量越少，你的代码则会变得更加模块化。</p>
</li>
</ul>
<p>这就是今天所有的内容。我们在下一篇文章再见。</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文链接 : &lt;a href=&quot;https://rainsoft.io/variables-lifecycle-and-why-let-is-not-hoisted/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;JavaScri
    
    </summary>
    
      <category term="JavaScript" scheme="http://qcyoung.com/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="http://qcyoung.com/tags/JavaScript/"/>
    
      <category term="ECMAScript6" scheme="http://qcyoung.com/tags/ECMAScript6/"/>
    
  </entry>
  
  <entry>
    <title>某只程序猿的扇贝英语打卡 500 天有感</title>
    <link href="http://qcyoung.com/2016/07/18/%E6%9F%90%E5%8F%AA%E7%A8%8B%E5%BA%8F%E7%8C%BF%E7%9A%84%E6%89%87%E8%B4%9D%E8%8B%B1%E8%AF%AD%E6%89%93%E5%8D%A1500%E5%A4%A9%E6%9C%89%E6%84%9F/"/>
    <id>http://qcyoung.com/2016/07/18/某只程序猿的扇贝英语打卡500天有感/</id>
    <published>2016-07-18T04:13:05.000Z</published>
    <updated>2018-11-25T09:09:09.200Z</updated>
    
    <content type="html"><![CDATA[<p>作为一名学渣..尤其是英语学渣..在 500 天打卡日到来的时候不禁还是想写点啥..所以在此记录一下自己打卡 500 天的经历和想法。</p>
<h2 id="chu-zhong-guo-cheng-ji-yuan-wang">初衷、过程及愿望</h2>
<p>在上周日 2016.7.10 我的英语打卡到达了第 500 天。推算 500 天前大概是 2015 年 3 月之前。当时还是寒假，我决定在毕业前的最后一次六级考试中好好刷个分..（之前基本是裸考，顺便这最后一次一起陪我们宿舍某个一直没有过六级的哥们考完他最后一次的六级考试 Ծ ̮ Ծ）。在看了一些网上推荐的英语学习方法后，最后选择了觉得比较适合我的扇贝英语系列（扇贝全系列 APP）。</p>
<p>这里简单介绍并安利一下选择扇贝的主要原因：</p>
<ol>
<li>它的系列 APP 很全，基本上选择了它你就不需要在安装其他的英语 APP 了</li>
<li>扇贝小组里的学习氛围很好，尤其是看到各路考托考雅思以及初、高中同学们的努力打卡的记录和笔记真的还是很有触动的。</li>
<li>扇贝单词 APP 中有很多网友上传的单词书，基本包含了各类专业常见单词。作为程序员目前我就已经背过了专业类的：软件开发及测试、离散数学、人月神话、科技常用英语等单词书。对自己平时的工作、阅读真的起到了一定帮助。</li>
<li>扇贝 APP 相比其他同类 APP 并不流氓，它相当注重用户体验。这里上张图大家一看就懂了。</li>
</ol>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/shanbayapp_permissions.jpg" alt="扇贝 APP 权限"></p>
<p>然后说下我的英语成绩，说实话这是我学业生涯以来最惭愧的一科。小时候父母工资拮据时都还给我报了一个月 80 块钱的英语学习班（那时工资好像才几百元）。因此小时候还能混进英奥班学习，然而后来淘气加偏科，因此英语成绩相对的也就慢慢掉下来了。记得高考前几乎每一次英语考试中选词填空好多都是蒙的。最后高考时英语也才 100 多分。。现在想起来确实觉得实在是对不住小时候的高价英语补习班。。</p>
<p>后来在大学中慢慢认识到了英语的重要性，但确实大学期间也还是没有很大程度的投入精力去学习英语。所以当时也是借此为契机，准备在毕业后养成学习英语的习惯（也主要是每天学习的习惯）。而且身为一名程序猿，英语能力确实一定程度上也是自己吃饭的技能之一啊..（想起之前知乎上看到的一个梗 —— 问：2016 年，中国的前端在关注什么？答：关注国外的前端..）。因此我的梦想也是能有朝一日能够可以做到流利的与国外朋友或同行进行英语交流。出国不存在任何语言障碍的水平，这样我也就心满意足了。（马云那样一半的英语交流能力也就够吧..）</p>
<h2 id="da-qia-500-tian-de-cheng-guo">打卡 500 天的成果</h2>
<p>首先需要说明，在开始打卡前我的英语水平差不多就是六级低分飘过的水平..（再高水平的英语考试目前还没考过..）然而在扇贝训练后的那次最后一次六级考试却是华丽丽的刷分失败..（没过线的水平..）当时还跟朋友说，我天天背单词，背了 100 多天也居然还是没过线(╯‵□′)╯︵┻━┻..但说实话在毕业后又坚持下来的这 300 多天来看，我觉得我的英语还是有进步的。（这里真的不虚！）</p>
<p>目前我每天的英语学习计划是 <strong>50 单词 + 10 炼句 + 2 阅读 + 5 听力</strong> （最近扇贝系列也刚出了口语系列,但目前我还没有把它加入每日计划的打算）。每天花费的时间平均大概在 30--45 分钟之间，基本上是可以接受的，也并不耽误正常工作休息时间。每天睡觉前看完 2 篇阅读，上班的路上背完单词和炼句，午休时间做完听力。基本上就是我每天正常的扇贝学习时间。</p>
<p>到 500 天打卡日为止累计学习单词数 10615 个，掌握单词数 10017 个，看完了 4 本英文原著。其他具体的学习内容感觉用扇贝的徽章图也比较好描述。。</p>
<p><img src="https://yangzj1992-1251901721.cos.ap-beijing.myqcloud.com/images/shanbay500.jpg" alt="扇贝 500 天"></p>
<p>其实打卡这件事跟坚持做其他事的差别并不大（健身、读书、听 VOA 等等），只要能真正的坚持下来肯定是不错的，我觉得这 500 天下来我无论是从学习的劲头上还是习惯上来说确实让我更多的产生了一些自信。相信自己之后在遇到更麻烦的挑战时也会敢于去尝试解决并相信自己能够做成。</p>
<p>谨以此日志记录这 500 天的扇贝打卡，并祝愿自己英语能力的梦想能早日实现。</p>
<p>更新：前两天看到一篇讲<a href="http://blog.jobbole.com/101359/" target="_blank" rel="external">程序员英语学习</a>的文章，感觉写的很不错，在此进行分享。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;作为一名学渣..尤其是英语学渣..在 500 天打卡日到来的时候不禁还是想写点啥..所以在此记录一下自己打卡 500 天的经历和想法。&lt;/p&gt;
&lt;h2 id=&quot;chu-zhong-guo-cheng-ji-yuan-wang&quot;&gt;初衷、过程及愿望&lt;/h2&gt;
&lt;p&gt;在上周日 2
    
    </summary>
    
      <category term="英语" scheme="http://qcyoung.com/categories/%E8%8B%B1%E8%AF%AD/"/>
    
    
      <category term="英语" scheme="http://qcyoung.com/tags/%E8%8B%B1%E8%AF%AD/"/>
    
      <category term="扇贝英语" scheme="http://qcyoung.com/tags/%E6%89%87%E8%B4%9D%E8%8B%B1%E8%AF%AD/"/>
    
  </entry>
  
  <entry>
    <title>【译】UI 的黑暗面！暗色背景的优势</title>
    <link href="http://qcyoung.com/2016/07/06/%E3%80%90%E8%AF%91%E3%80%91UI%20%E7%9A%84%E9%BB%91%E6%9A%97%E9%9D%A2%EF%BC%81%E6%9A%97%E8%89%B2%E8%83%8C%E6%99%AF%E7%9A%84%E4%BC%98%E5%8A%BF/"/>
    <id>http://qcyoung.com/2016/07/06/【译】UI 的黑暗面！暗色背景的优势/</id>
    <published>2016-07-06T04:45:39.000Z</published>
    <updated>2016-10-14T07:33:30.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文链接 : <a href="https://medium.com/@tubikstudio/dark-side-of-ui-benefits-of-dark-background-12f560bf7165#.k0d00u47a" target="_blank" rel="external">Dark Side of UI. Benefits of Dark Background</a><br>
原文作者 : <a href="https://medium.com/@tubikstudio" target="_blank" rel="external">Tubik Studio</a><br>
译文出自 : <a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a><br>
译者 : <a href="http://qcyoung.com">yangzj1992</a><br>
校对者: <a href="https://github.com/wild-flame" target="_blank" rel="external">David Lin</a>, <a href="https://github.com/Ruixi" target="_blank" rel="external">Ruixi</a><br>
首发于: <a href="http://gold.xitu.io/entry/577c9385a633bd005be7fe7a/detail" target="_blank" rel="external">掘金</a></p>
</blockquote>
<p><img src="http://ac-Myg6wSTV.clouddn.com/18dcdce02f167c38bd04.jpeg" alt=""></p>
<p>在用户界面的背景中是否选择使用暗色调依然是一个具有高度争议的问题。毋庸置疑，这个问题是很实际的：选择一个合适的背景在所有的产品功效上都起着至关重要的作用，因为它可能会是改善或是反而毁掉设计方案中布局和功能的关键因素。在今天，我们的文章将致力于讨论在 UI 设计中使用暗色背景的好处和缺陷，所以让我们前往 UI 的黑暗面吧。</p>
<p>在我们<a href="http://tubikstudio.com/light-and-darkness-in-ui-design-matter-of-choice/" target="_blank" rel="external">之前的文章</a>中我们已经分析了一些可以影响选择通常的配色方案和基本的背景颜色的因素，也提到了一些在这个过程中要考虑的重点。这一次我们将更多的关注暗色设计的网站和移动应用的优缺点。我们在 <a href="http://tubikstudio.com/" target="_blank" rel="external">Tubik Studio</a> 中创建并测试了不同的用户界面，这些实际的工作经验证实了暗色背景会是强大而有吸引力的、能提供积极用户体验的解决方案。所以，理所当然的，让我们来开始讨论应该在何时何地怎样让它最大程度的发挥效果吧。</p>
<h3 id="dui-yu-an-se-de-shi-jue-gan-zhi">对于暗色的视觉感知</h3>
<p>很久之前，在 2009 年曾有过一个公开的投票调查结果，<a href="http://www.problogger.net/archives/2009/05/19/light-or-dark-blog-backgrounds-poll-results/" target="_blank" rel="external">ProBlogger</a> 基于此已经公布了一些有趣的观点。读者被问及他们更喜欢哪种颜色的博客背景。几乎一半的读者回答更喜欢亮色背景 - 这对于传统的文本驱动型博客来说是十分合理的，在可读性方面如此可以胜过其他方案。然而，有 10% 的受访者回答他们更喜欢暗色背景，并且有超过三分之一的人提到选择的依据应该取决于博客的性质和内容。设计师在寻找设计方案时是不能忽略占有如此大比例的用户的。此外，在具有更少的文本型驱动内容的数字产品情况时，如网站或应用程序中，持上述观点的人数比例应该还会增加。这个例子很好的说明了用户研究和调查应该是设计过程中的重要组成部分。了解用户想要什么或是至少了解他们所能够接受的是什么，这能够将传统视觉的限度推向一个极致。</p>
<p>Richard H. Hall 和 Patrick Hanna 对于这个问题提供的<a href="http://lite.mst.edu/media/research/ctel/documents/LITE-2003-04.pdf" target="_blank" rel="external">科学研究</a>中强调了视觉感知的背景颜色和效果的关键点。在分析了不同研究者之前对网络页面效果和可读性方面的实际试验后。作者们总结了：「<em>他们发现正向对比（即白底黑字）会有更好的效果，并与之前提到的研究结合，说明颜色组合之间的对比度越大效果越好。</em>」因此在合适的设计和测试下，在其他方面深色背景也可以像浅色背景一样具有好的效果，尤其是在对比性以及布局元素的易读性上。在用户测试角度上这项研究基于不同颜色组合和效果下包含了很多有趣及有用的信息。所以在此强烈推荐给设计师们。</p>
<p><img src="http://ac-Myg6wSTV.clouddn.com/f5f8b33e3c4fb542fef3.jpg" alt=""></p>
<figcaption><a href="http://tubikstudio.com/works-ribbet/" target="_blank" rel="external">Ribbet 的用户界面来自 Tubik Studio</a></figcaption>
<h3 id="ke-du-xing-fang-mian">可读性方面</h3>
<p>用户体验设计的著名大师之一 Jacob Nielsen 曾提到过：「<em>在使用高对比度颜色的文本和背景时。最优的易读性方案是要求使用黑色文字和白色背景（所谓的正向文本）。而白色文字和黑色背景（反向文本）几乎也是一样好的。尽管这在对比度上与正向文本是一样的，但倒配色方案会让人们略微有些迷惑并会稍微降低他们的阅读速度。易读性相当受配色方案的影响，像文本比纯黑稍亮的颜色，尤其是背景色比纯白稍暗的配色，易读性会变得很差。</em>」</p>
<p>的确，可读性是产品效果表现上的重要指标并且它不仅只针对文本。它超越了文本的限制并且意味着所有有意义的象征，包括字母，数字，象形符号和图片都应该被留意到并轻易的在界面中识别出。因此，设计师在选择深色的背景时应该准备更额外深入的选择并测试不同设备上的字体、图标和图像。</p>
<p><img src="http://ac-Myg6wSTV.clouddn.com/30476ea6fc9c5178170f.png" alt=""></p>
<figcaption><a href="https://dribbble.com/shots/2632600-SwiftyBeaver-Landing-Page" target="_blank" rel="external">SwiftyBeaver 的着陆页</a>，来自 <a href="https://dribbble.com/LudmilaShevchenko" target="_blank" rel="external">Ludmila Shevchenko</a></figcaption>
<p>最好的网页和应用程序的设计实践，例如 <a href="http://www.awwwards.com/websites/black/" target="_blank" rel="external">Awwwards 上最好的黑色网站</a>合集上的例子，这里展现了大量使用深色背景作为基础配色的优秀设计方案，这些方案都没有以牺牲可读性为代价。为了避免低可读性这个问题，在设计过程中重要的是要记住：</p>
<ul>
<li>深色背景会吸收一部分其他元素的光线，所以应该在元素之间留有足够的空间或&quot;气&quot;;</li>
<li>行的长度可以让文本区块对用户更具可读性并易于理解;</li>
<li>行间距的空间设计，以及文本行的长度问题都会对可读性具有很大的影响，在深色背景下尤甚，所以段落的大小，字距和行间距都需要仔细考虑。</li>
<li>深色并不总是意味着黑色，所以在每一个特定情况的设计中，去花费一些时间测试不同种类的深色背景和颜色所呈现的内容是十分合理的，在试验中尽情的尝试吧;</li>
<li>阴影，渐变和光晕都会影响可读性;</li>
<li>无衬线字体通常比较清晰，而衬线字体看起来更加优雅，在实践中应用这个因素可以增强内容的可读性。</li>
</ul>
<h3 id="dui-bi-du-fang-mian">对比度方面</h3>
<p><a href="http://webdesign.about.com/od/color/l/bl_contrast_table.htm" target="_blank" rel="external">webdesign.about.com</a> 用表格展现了一个有趣的视觉感知方面需要考虑的展现效果。该表展示了不同的颜色组合之间的对比和效果水平并提供了一个有趣的事实：表中的黑色部分是唯一一个可以为几乎所有颜色提供良好对比度效果的颜色。因此在设计界面的每一个特定情况下去仔细试验，这一因素可以作为尝试使用深色背景的理由之一。</p>
<p><img src="http://ac-Myg6wSTV.clouddn.com/962624f37bb9c9a8b839.jpg" alt=""></p>
<p>在可读性方面，对比度能是使内容更容易识别和清晰的因素之一。</p>
<p>关于对比度和可读性的这种提示信息在之前的一个<a href="http://www.writer2001.com/colwebcontrast.htm" target="_blank" rel="external">早期的调查</a>中有说过：「<em>在深色背景下，确保你没有包含相当明亮的字体：使用柔和的白色到浅灰色字体，或使用单调的色彩来最小程度的减少巨大的反差和眩光；这个原则在做幻灯片时也同样适用：用至少 5% 的灰度来减少眩光的亮白。有趣的是，这样仍然在「阅览」时会被认为是白色的。同样的，将字体加粗，可以让字体有足够的大小让人不觉得文字被深色背景所「吞噬」</em>」这个试验以及其他试验能够提供不同类型的调色法，而这些调色法能够为网页和应用页面提供高效、自然的内容。</p>
<p>还有一件事就是深色背景在某种程度上通常显得更沉重以及能更深入的呈现图形的内容如图片，相片，插图，海报和广告。良好的构图并遵守视觉层级原则可以显著的增强这种布局元素的视觉感知。这个因素在当界面基于更多图形材料而非文本时会使深色背景更高效并且具有吸引力。</p>
<p><img src="http://ac-Myg6wSTV.clouddn.com/ffb8689a5486125bdc5b.png" alt=""></p>
<figcaption><a href="https://dribbble.com/shots/2062865-Analytics-App" target="_blank" rel="external">一款分析应用</a>来自 <a href="https://dribbble.com/LudmilaShevchenko" target="_blank" rel="external">Ludmila Shevchenko</a></figcaption>
<h3 id="qing-gan-gan-zhi-fang-mian">情感感知方面</h3>
<p>色彩心理学也是在选择背景颜色时需要考虑到的，这不仅只是包含在所展现的有效范围中，还包含内容自身所承担的信息载体。黑暗的颜色通常与优雅和神秘感有关。此外，黑色往往与优雅，礼节，声望和权利有关。这或许是为什么许多强大的品牌都会使用黑白色调的主题，用深色来主导、用亮色来展示承载的信息，使用这样的方案来构建视觉展现效果。界面设计在这方面可以为其他设计解决方案和一般的产品展示提供额外的支持。</p>
<p><img src="http://ac-Myg6wSTV.clouddn.com/dd0719f001ef35d5600e.gif" alt=""></p>
<figcaption><a href="https://dribbble.com/shots/2620649-Tubik-Studio-Museu" target="_blank" rel="external">Tubik Studio | 博物馆</a> 来自 <a href="https://dribbble.com/ErnestAsanov" target="_blank" rel="external">Ernest Asanov</a></figcaption>
<h3 id="shen-se-bei-jing-de-you-shi">深色背景的优势</h3>
<p>根据上述各点，我们可以总结得到在用户界面应用深色背景可以提供以下实际好处，包括：</p>
<ul>
<li>格调及优雅</li>
<li>神秘感</li>
<li>奢华显赫的外观</li>
<li>可以广范围的运用对比</li>
<li>支持视觉层次的展示</li>
<li>反映内容的深度</li>
<li>视觉吸引力。</li>
</ul>
<p><img src="http://ac-Myg6wSTV.clouddn.com/7947c9ab8ad7a9dd9128.png" alt=""></p>
<figcaption><a href="https://dribbble.com/shots/2749617-Tubik-Studio-Vinny-s-Bakery" target="_blank" rel="external">Tubik Studio | Vinny 的面包店</a> 来自 <a href="https://dribbble.com/ErnestAsanov" target="_blank" rel="external">Ernest Asanov</a></figcaption>
<h3 id="kao-lu-yao-dian">考虑要点</h3>
<p>在另一方面，深色背景需要彻底的关注和分析最微小的细节，如果它们没有以合适的方式呈现，那么这些细节则可能会在布局中变得模糊。因此我们应该考虑：</p>
<ul>
<li><strong>用户研究</strong> 实际调查，理论研究和试验数据是针对目标用户十分重要的数据资源，他们的需求是选择有效和有吸引力的设计方案的基础。</li>
<li><strong>竞争研究</strong> 对关系密切的竞争对手进行市场研究，可以理解已经被市场上其他对手使用的设计方案，并且这一因素会影响到原始设计的选择从而使产品更加明显。</li>
<li><strong>用户测试</strong> 深色背景在可读性和易读性方面是十分脆弱的，所以应当在各种类型的设备和分辨率下严格的去测试。</li>
<li><strong>环境因素</strong> 去分析典型条件下将被用于目标用户的产品，可以为选择或反对深色背景提供其他的理由。</li>
<li><strong>大量的内容</strong> 元素和块的数量需要在屏幕或网页上达到能影响周围背景的决策：深色的背景如果在元素间留下了太少的空间会为视觉造成极大的困难。</li>
<li><strong>内容的性质</strong> 相比于大量的文本区块，深色背景可以为基于图形元素的界面提供更好的展示效果。</li>
</ul>
<p><img src="http://ac-Myg6wSTV.clouddn.com/695e0a55cb834721ce2b.gif" alt=""></p>
<figcaption><a href="https://dribbble.com/shots/2736160-GIF-Animation-for-Recipes-and-Cooking" target="_blank" rel="external">关于食谱和烹饪的 GIF 动画</a> 来自 <a href="https://dribbble.com/SergeyValiukh" target="_blank" rel="external">Sergey Valiukh</a></figcaption>
<h3 id="tui-jian-yue-du">推荐阅读</h3>
<ul>
<li><a href="http://lite.mst.edu/media/research/ctel/documents/LITE-2003-04.pdf" target="_blank" rel="external"><strong>The Impact of Web Page Text-Background Color Combinations on Readability, Retention, Aesthetics, and Behavioral Intention</strong></a></li>
<li><a href="https://books.google.com.ua/books?id=rvt4a_AmKFQC&amp;pg=PA11&amp;lpg=PA11&amp;dq=visual+perception+readability&amp;source=bl&amp;ots=6_rkYdGkj9&amp;sig=_UYthMOEgDSLJEFftDA895epms8&amp;hl=ru&amp;sa=X&amp;ved=0ahUKEwjiwPjRmsPMAhVOKywKHf1FCSM4FBDoAQgtMAM#v=onepage&amp;q=visual%20perception%20readability&amp;f=false" target="_blank" rel="external"><strong>Visual Perception: An Introduction</strong></a></li>
<li><a href="https://books.google.com.ua/books?id=9RktoatXGQ0C&amp;pg=PA350&amp;lpg=PA350&amp;dq=visual+perception+readability&amp;source=bl&amp;ots=NTLfJ_Akj6&amp;sig=bpw4URR8U-QwWZwOndhoYs-wJWE&amp;hl=ru&amp;sa=X&amp;ved=0ahUKEwjiwPjRmsPMAhVOKywKHf1FCSM4FBDoAQgZMAA#v=onepage&amp;q=visual%20perception%20readability&amp;f=false" target="_blank" rel="external"><strong>Art and Visual Perception: A Psychology of the Creative Eye</strong></a></li>
<li><a href="http://www.writer2001.com/colwebcontrast.htm" target="_blank" rel="external"><strong>Colour Choices on Web Pages: Contrast vs Readability</strong></a></li>
<li><a href="http://www.webdesignerdepot.com/2009/08/the-dos-and-donts-of-dark-web-design/" target="_blank" rel="external"><strong>The Dos and Don’ts of Dark Web Design</strong></a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文链接 : &lt;a href=&quot;https://medium.com/@tubikstudio/dark-side-of-ui-benefits-of-dark-background-12f560bf7165#.k0d00u47a&quot; target=
    
    </summary>
    
      <category term="UED" scheme="http://qcyoung.com/categories/UED/"/>
    
    
      <category term="UI" scheme="http://qcyoung.com/tags/UI/"/>
    
      <category term="UX" scheme="http://qcyoung.com/tags/UX/"/>
    
      <category term="网页设计" scheme="http://qcyoung.com/tags/%E7%BD%91%E9%A1%B5%E8%AE%BE%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>【译】JSON Web Tokens (JWT) 与 Sessions</title>
    <link href="http://qcyoung.com/2016/07/04/%E3%80%90%E8%AF%91%E3%80%91JSON%20Web%20Tokens%20(JWT)%20%E4%B8%8E%20Sessions/"/>
    <id>http://qcyoung.com/2016/07/04/【译】JSON Web Tokens (JWT) 与 Sessions/</id>
    <published>2016-07-04T15:21:10.000Z</published>
    <updated>2016-10-24T05:55:28.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文链接 : <a href="https://float-middle.com/json-web-tokens-jwt-vs-sessions/" target="_blank" rel="external">JSON Web Tokens (JWT) vs Sessions</a><br>
原文作者 : <a href="https://float-middle.com/author/jacek-ciolek/" target="_blank" rel="external">Jacek Ciolek</a><br>
译文出自 : <a href="http://zcfy.cc/" target="_blank" rel="external">众成翻译</a><br>
译者 : <a href="http://qcyoung.com">yangzj1992</a><br>
校对者: <a href="https://www.zhihu.com/people/ha-ha-qiu-52" target="_blank" rel="external">lisa</a><br>
首发于: <a href="http://zcfy.cc/article/json-web-tokens-jwt-vs-sessions-685.html" target="_blank" rel="external">众成翻译</a></p>
</blockquote>
<h2 id="shi-yao-shi-jwt">什么是 JWT?</h2>
<blockquote>
<p>本质上它是一段签名的 JSON 格式的数据。由于它是带有签名的，因此接收者便可以验证它的真实性。同时由于它是 JSON 格式的因此它的体积也很小。如果你想了解有关它的正式定义，可以在 <a href="https://tools.ietf.org/html/rfc7519" target="_blank" rel="external">RFC 7519</a> 中找到。</p>
</blockquote>
<p><em>这篇文章发布于<a href="https://news.ycombinator.com/item?id=11929267" target="_blank" rel="external">黑客新闻</a>上。在这里也可以看一下关于这篇文章的<a href="https://float-middle.com/i-got-featured-on-hacker-news-case-study/" target="_blank" rel="external">案例分析</a>，它主要包含了文章内容的公开分析、SEO 影响、性能影响以及更多其他的内容。</em></p>
<p>数据签名已经不是什么新事物了 - 令人值得兴奋的是如何在不依靠 sessions 的情况下使用 JWT 创建真正的 RESTful 服务，目前这个想法已经被事实证明有一段时间了。下面是介绍它在现实中具体实现的工作原理 - 首先在这里我来做一个类比：</p>
<p>想象一下你刚从国外度完假回来，你在边境上说 - 你可以让我通过，我是这里的公民。这样的回答很好也没有问题，但是你要如何去支持你的说法呢？最有可能的方案是你携带了护照来证明你的身份。这里我们假设边境工作人员也都被要求去核实护照是真正由你的国家的护照办签发的。那么护照就会被核实，这样他们也才会放你回国。</p>
<p>现在，让我们从 JWT 的角度看一下这个故事，它们各自又都扮演着什么样的角色：</p>
<ul>
<li>
<p><strong>护照办</strong> - 发布 JWT 的身份验证服务。</p>
</li>
<li>
<p><strong>护照</strong> - 你通过&quot;护照办&quot;获得的 JWT 签名。你的身份对于任何人都是可读的，但是只有它是真实的时候相关方才会对其核实。</p>
</li>
<li>
<p><strong>公民资格</strong> - 在 JWT 中包含的你的声明（你的护照）。</p>
</li>
<li>
<p><strong>边境</strong> - 你的应用程序的安全层，在被允许访问受保护的资源之前由它来核实你的 JWT 令牌身份，在这种情况下指的是 - 国家。</p>
</li>
<li>
<p><strong>国家</strong> - 你想要获取的资源（例如 API）。</p>
</li>
</ul>
<h2 id="kan-a-mei-you-session">看啊！没有 session!</h2>
<p>简单来说，JWT 非常的酷，因为你不用再为了鉴别用户而在你的服务器上去保留你的 session 数据。这个工作流将会变得像下面这样：</p>
<ul>
<li>
<p>用户调用身份验证服务，通常是发送了用户名及密码。</p>
</li>
<li>
<p>身份验证服务响应并返回了签名的 JWT，上面包含了用户是谁的内容。</p>
</li>
<li>
<p>用户向安全服务发送请求收到安全服务返回的令牌。</p>
</li>
<li>
<p>安全层检验令牌上的签名并且在签名为真实的时候授权予以通过。</p>
</li>
</ul>
<p>让我们考虑一下这样做的结果。</p>
<h3 id="mei-you-hui-hua-cun-chu">没有会话存储</h3>
<p>没有 sessions 意味着你没有会话存储。但除非您的应用程序需要横向扩展，否则这也不太重要，如果你的应用程序是运行在多个服务器上的，那么共享 session 数据将会成为一个负担。你需要一个专门的服务器来只存储会话数据或是共享磁盘空间或是在负载均衡上粘滞会话。当你不使用 sessions 时上面的这些也就自然不再需要了。</p>
<h3 id="mei-you-dui-sessions-de-la-ji-shou-ji">没有对 sessions 的垃圾收集</h3>
<p>通常来讲 sessions 需要留意过期和垃圾收集的情况。JWT 可以在用户数据中包含自己的过期日期。因此安全层在检验 JWT 的授权时可以同时核对它的过期时间来拒绝访问。</p>
<h3 id="zhen-zheng-de-res-tful-fu-wu">真正的 RESTful 服务</h3>
<p>只有在无 sessions 的情况下你可以创建真正的 RESTful 服务，因为它被认为是<a href="https://en.wikipedia.org/wiki/Representational_state_transfer#Stateless" target="_blank" rel="external">无状态的</a>。 JWT 很小所以它可以在每一个请求中被一起发出去，就像一个 session cookie一样。然而与 session cookie 不同的是，它并不指向服务器上的任何存储数据， JWT 本身包含了这些数据。</p>
<h2 id="zhen-shi-de-jwt-dao-di-shi-shi-yao-yang-de">真实的 JWT 到底是什么样的?</h2>
<p>在我们更深入讨论之前，有一件事需要了解。JWT 自身并不是一个东西。它是 <a href="https://tools.ietf.org/html/rfc7515" target="_blank" rel="external">JSON 网络签名（JWS)</a>或 <a href="https://tools.ietf.org/html/rfc7516" target="_blank" rel="external">JSON 网络加密 (JWE)</a>中的一种类型。它的定义如下：</p>
<blockquote>
<p>一个 JWT 的声明内容会被编码为一个 JSON 对象，它被作为 JSON 网络签名结构的有效载荷或是作为 JSON 网络加密结构的明文信息。</p>
</blockquote>
<p>前者给我们的只是一个签名并且它包含的数据（或是平时所称呼的 <code>claims</code> 的命名）是对任何人都可读的。后者则提供了加密的内容，所以只有拥有密钥的人可以解密它。JWS 在实现上更加容易并且基本用法上是不需要加密的 - 毕竟如果你在客户端上有密钥的话，你还不如把所有的东西不加密的好。因此 JWS 在大多数情况下都是适用的，也因此在之后我将主要关注 JWS。</p>
<h3 id="na-yao-jwt-jws-shi-you-shi-yao-gou-cheng-de">那么 JWT/JWS 是由什么构成的？</h3>
<ul>
<li>
<p><strong>头部</strong> - 关于签名算法的信息，以 JSON 格式的负载类型(JWT)等等。</p>
</li>
<li>
<p><strong>负载</strong> - JSON 格式的实际的数据（或是声明）。</p>
</li>
<li>
<p><strong>签名</strong> - 额... 就是签名。</p>
</li>
</ul>
<p>我将在之后具体解释这些细节。现在让我们先来分析下基础要素。</p>
<p>上述所提到的每一部分（头部，负载和签名）是基于 base64url 编码的，然后他们用 <code>.</code> 作为分隔符粘连起来组成 JWT。 下面是这个实现方式可能看上去的样子：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> header = &#123;  </span>
<span class="line">        <span class="comment">// The signing algorithm.</span></span>
<span class="line">        <span class="string">"alg"</span>: <span class="string">"HS256"</span>,</span>
<span class="line">        <span class="comment">// The type (typ) property says it's "JWT",</span></span>
<span class="line">        <span class="comment">// because with JWS you can sign any type of data.</span></span>
<span class="line">        <span class="string">"typ"</span>: <span class="string">"JWT"</span></span>
<span class="line">    &#125;,</span>
<span class="line">    <span class="comment">// Base64 representation of the header object.</span></span>
<span class="line">    headerB64 = btoa(<span class="built_in">JSON</span>.stringify(header)),</span>
<span class="line">    <span class="comment">// The payload here is our JWT claims.</span></span>
<span class="line">    payload = &#123;</span>
<span class="line">        <span class="string">"name"</span>: <span class="string">"John Doe"</span>,</span>
<span class="line">        <span class="string">"admin"</span>: <span class="literal">true</span></span>
<span class="line">    &#125;,</span>
<span class="line">    <span class="comment">// Base64 representation of the payload object.</span></span>
<span class="line">    payloadB64 = btoa(<span class="built_in">JSON</span>.stringify(payload)),</span>
<span class="line">    <span class="comment">// The signature is calculated on the base64 representation</span></span>
<span class="line">    <span class="comment">// of the header and the payload.</span></span>
<span class="line">    signature = signatureCreatingFunction(headerB64 + <span class="string">'.'</span> + payloadB64),</span>
<span class="line">    <span class="comment">// Base64 representation of the signature.</span></span>
<span class="line">    signatureB64 = btoa(signature),</span>
<span class="line">    <span class="comment">// Finally, the whole JWS - all base64 parts glued together with a '.'</span></span>
<span class="line">    jwt = headerB64 + <span class="string">'.'</span> + payloadB64 + <span class="string">'.'</span> + signatureB64;</span>
</pre></td></tr></table></figure>
<p>由此得到的 JWS 结果看上去整洁而优雅，有点像这样：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0.OLvs36KmqB9cmsUrMpUutfhV52_iSz4bQMYJjkI_TLQ`</span>
</pre></td></tr></table></figure>
<p>你也可以试着在 <a href="https://jwt.io/#debugger" target="_blank" rel="external">jwt.io</a> 这个网站上来创建令牌试试。</p>
<p>有一点相当重要，那就是签名是依据头部和负载计算出来的。因此头部和负载的授权也很容易同样被检验：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line">[headerB64, payloadB64, signatureB64] = jwt.split(<span class="string">'.'</span>);</span>
<span class="line"></span>
<span class="line"><span class="keyword">if</span> (atob(signatureB64) === signatureCreatingFunction(headerB64 + <span class="string">'.'</span> + payloadB64) &#123;</span>
<span class="line">    <span class="comment">// good</span></span>
<span class="line">&#125; <span class="keyword">else</span></span>
<span class="line">    <span class="comment">// no good</span></span>
<span class="line">&#125;)</span>
</pre></td></tr></table></figure>
<h3 id="jwt-tou-bu-zhong-ke-yi-cun-fang-shi-yao">JWT 头部中可以存放什么？</h3>
<p>事实上，JWT 头部被称为 JOSE 头部。JOSE 表示的是 JSON 对象的签名和加密。也正如你期望的那样，JWS 和 JWE 都是这样的一个头部，然而它们各自之间存在着一套稍微不同的注册参数。下面是在 JWS 中使用的头部注册参数列表。所有的参数除了第一个参数（alg）以外，其他参数都是可选的：</p>
<ul>
<li>
<p><strong>alg</strong> 算法 （必选项）</p>
</li>
<li>
<p><strong>typ</strong> 类型 （如果是 JWT 那么就带有一个值 <code>JWT</code>，如果存在的话）</p>
</li>
<li>
<p><strong>kid</strong> 密钥 ID</p>
</li>
<li>
<p><strong>cty</strong> 内容类型</p>
</li>
<li>
<p><strong>jku</strong> JWK 指定 URL</p>
</li>
<li>
<p><strong>jwk</strong> JSON 网络值</p>
</li>
<li>
<p><strong>x5u</strong> X.509 URL</p>
</li>
<li>
<p><strong>x5c</strong> X.509 证书链</p>
</li>
<li>
<p><strong>x5t</strong> X.509 证书 SHA-1 指纹</p>
</li>
<li>
<p><strong>x5t#S256</strong> X.509 证书 SHA-256 指纹</p>
</li>
<li>
<p><strong>crit</strong> 临界值</p>
</li>
</ul>
<p>前两个参数是最常用的，所以典型的头部看起来有点类似下面这样：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
</pre></td><td class="code"><pre><span class="line">&#123;</span>
<span class="line">    <span class="attr">"alg"</span>: <span class="string">"HS256"</span>,</span>
<span class="line">    <span class="attr">"typ"</span>: <span class="string">"JWT"</span></span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>上面列出的第三个参数 <code>kid</code> 是基于安全原因使用的。<code>cty</code> 参数在另一方面应该只被用于处理嵌套的 JWT。剩下的参数你可以在<a href="https://tools.ietf.org/html/rfc7515#section-4.1" target="_blank" rel="external">规范文档</a>中阅读了解，我认为它们不适合在这篇文章中被提及。</p>
<h4 id="code-alg-code-suan-fa"><code>alg</code> （算法）</h4>
<p><code>alg</code> 参数的值可以是 <a href="https://tools.ietf.org/html/rfc7518#section-3.1" target="_blank" rel="external">JSON 网络算法(JWA)</a>中的任意指定值 - 这是我所知道的另一个规范。下面是 JWS 的注册列表：</p>
<ul>
<li>
<p><strong>HS256</strong> - HMAC 使用 SHA-256 算法</p>
</li>
<li>
<p><strong>HS384</strong> - HMAC 使用 SHA-384 算法</p>
</li>
<li>
<p><strong>HS512</strong> - HMAC 使用 SHA-512 算法</p>
</li>
<li>
<p><strong>RS256</strong> - RSASSA-PKCS1-v1_5 使用 SHA-256 算法</p>
</li>
<li>
<p><strong>RS384</strong> - RSASSA-PKCS1-v1_5 使用 SHA-384 算法</p>
</li>
<li>
<p><strong>RS512</strong> - RSASSA-PKCS1-v1_5 使用 SHA-512 算法</p>
</li>
<li>
<p><strong>ES256</strong> - ECDSA 使用 P-256 和 SHA-256 算法</p>
</li>
<li>
<p><strong>ES384</strong> - ECDSA 使用 P-384 和 SHA-384 算法</p>
</li>
<li>
<p><strong>ES512</strong> - ECDSA 使用 P-521 和 SHA-512 算法</p>
</li>
<li>
<p><strong>PS256</strong> - RSASSA-PSS 使用 SHA-256 和基于 SHA-256 算法的 MGF1</p>
</li>
<li>
<p><strong>PS384</strong> - RSASSA-PSS 使用 SHA-384 和基于 SHA-384 算法的 MGF1</p>
</li>
<li>
<p><strong>PS512</strong> - RSASSA-PSS 使用 SHA-512 和基于 SHA-512 算法的 MGF1</p>
</li>
<li>
<p><strong>none</strong> - 没有数字签名或 MAC 执行</p>
</li>
</ul>
<p>请注意最后一个值 <code>none</code>，从安全性的角度来看这是最有趣的。<a href="https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/" target="_blank" rel="external">这是已知的被用来进行降级防御攻击的方法</a>。它是如何工作的呢？想象一个客户端生成的带有一些声明的 JWT 。它在头部指定 <code>none</code> 值的签名算法并进行发送验证。如果攻击者比较单纯，那么它会使 <code>alg</code> 参数为真来确保被授权通过，然而实际上则是不会被允许的。</p>
<p>底线是，你的应用的安全层应该总是对头部的 <code>alg</code> 参数进行校验。那里就是 <code>kid</code> 参数用的上的地方。</p>
<h4 id="code-typ-code-lei-xing"><code>typ</code> （类型）</h4>
<p>这一个参数非常简单。如果它是已知的，那么它就是 JWT，因为应用不会去索取其他的值，如果这个参数没有值就会被忽视掉。因此它是可选的。如果需要被指定值，它应该按大写字母拼写 - <code>JWT</code> 。</p>
<p>在某些情况下，当应用程序接受到没有 JWT 类型的请求却又包含了 JWT 时，去重新指定它是很重要的，因为这样应用程序才不会崩溃。</p>
<h4 id="code-kid-code-mi-yao-id"><code>kid</code> （密钥 id）</h4>
<p>如果你的应用程序中的安全层只使用了一个算法来签名 JWTs，你不用太担心 <code>alg</code> 参数，因为你会总是使用相同的密钥和算法来校验令牌的完整性。但是，如果你的应用程序使用了一堆不同的算法和密钥，你就需要能够分辨出是由谁签署的令牌。</p>
<p>正如我们之前看到的，单独依靠 <code>alg</code> 参数可能会导致一些...不便。然而，如果你的应用维护了一个密钥/算法的列表，并且每一对都有一个名称(id)，你可以添加这个密钥 id 到头部，这样在之后验证 JWT 时你会有更多的信心去选择算法。这就是头部参数 <code>kid</code> - 你的应用中用来签名令牌所使用的密钥 id 。这个 id 是由你来任意指定的。最重要的是 - 这是你给的 id ，所以你可以验证。</p>
<h4 id="code-cty-code-nei-rong-lei-xing"><code>cty</code> （内容类型）</h4>
<p>这里把<a href="https://tools.ietf.org/html/rfc7519#section-5.2" target="_blank" rel="external">规范</a>介绍的很清楚，所以这里我就只是引用了：</p>
<blockquote>
<p>在通常情况下，在不使用嵌套签名或是加密操作时，是不推荐使用这个头部参数的。而在使用嵌套签名或加密时，这个头部参数必须存在；在这种情况下，它的值必须是 &quot;JWT&quot;，来表明这是一个在 JWT 中嵌套的 JWT。虽然媒体类型名字对大小写并不敏感，但这里为了与现有遗留实现兼容还是推荐始终用 <code>JWT</code> 大写字母来拼写。</p>
</blockquote>
<h3 id="zai-jwt-sheng-ming-zhong-ke-yi-you-shi-yao">在 JWT 声明中可以有什么？</h3>
<p><code>claims</code> 这个名称是否让你感到困惑？在最初它也确实让我很困惑。我相信你需要重复读几次来尝试适应它。简而言之，<code>claims</code> 是 JWT 的主要内容 - 是我们十分关心的签名的数据。它被叫做 <code>claims</code> 是因为通常它就是声明这个意思 - 客户端声明了用户名，用户角色或者其他什么的来让它可以获得对资源的访问。</p>
<p>还记得我在最开始提到的那个可爱的故事吗？你的公民资格就是你的声明而你的护照则就是 - JWT</p>
<p>你可以在声明中放置任何你想要的参数，这儿有一个<a href="https://tools.ietf.org/html/rfc7519#section-4.1" target="_blank" rel="external">注册表</a>应当被视为公认的参考实现方法。请注意这里的每一个参数都是可选的并且大多数是应用程序特定的，下面就是这个列表：</p>
<ul>
<li>
<p><strong>exp</strong> - 过期时间</p>
</li>
<li>
<p><strong>nbf</strong> - 有效起始日期</p>
</li>
<li>
<p><strong>iat</strong> - 发行时间</p>
</li>
<li>
<p><strong>sub</strong> - 主题</p>
</li>
<li>
<p><strong>iss</strong> - 发行者</p>
</li>
<li>
<p><strong>aud</strong> - 受众</p>
</li>
<li>
<p><strong>jti</strong> - JWT ID</p>
</li>
</ul>
<p>值得注意的是，除了最后三个(issuer ，audience 和 JWT ID)参数通常是在更复杂的情况下（例如包含多个发行者时）才被使用。下面让我们来讨论一下它们吧。</p>
<h4 id="code-exp-code-guo-qi-shi-jian"><code>exp</code> （过期时间）</h4>
<p><code>exp</code> 是时间戳值表示着在什么时候令牌会失效。规范上要求&quot;当前日期/时间&quot;必须在指定的 <code>exp</code> 值之前，从而保证令牌可以得到处理。这里也表明了存在一些余地（几分钟）来应对时间差。</p>
<h4 id="code-nbf-code-you-xiao-qi-shi-shi-jian"><code>nbf</code> （有效起始时间）</h4>
<p><code>nbf</code> 是时间戳值表示着在什么时候令牌开始生效。规范上要求&quot;当前日期/时间&quot;必须与指定的 <code>nbf</code> 值相等或在其之后，从而保证令牌可以得到处理。这里也表明了存在一些余地（几分钟）来应对时间差。</p>
<h4 id="code-iat-code-fa-xing-shi-jian"><code>iat</code> （发行时间）</h4>
<p><code>iat</code> 是时间戳值表示什么时候令牌被发行。</p>
<h4 id="code-sub-code-zhu-ti"><code>sub</code> （主题）</h4>
<p><code>sub</code> 在规范上被要求&quot;是JWT 中的声明中通常用于陈述主题的值&quot;。这里主题必须是内容中唯一的发行者或全局上的唯一值。<code>sub</code> 声明可以用来鉴别用户，例如 <a href="https://developer.atlassian.com/static/connect/docs/latest/concepts/understanding-jwt.html#token-structure-claims" target="_blank" rel="external">JIRA</a> 文档上那样。</p>
<h4 id="code-iss-code-fa-xing-zhe"><code>iss</code> （发行者）</h4>
<p><code>iss</code> 是被用来确认令牌的发行者的字符串值。如果值中包含 <code>:</code> 那么它就是一个 URI。如果有很多的发行者而在一个安全层中应用程序需要去识别发行人时，它将会是有用的。例如 <a href="https://help.salesforce.com/HTViewHelpDoc?id=remoteaccess_oauth_jwt_flow.htm" target="_blank" rel="external">Salesforce</a> 要求了去使用 OAuth client_id 来作为 <code>iss</code> 的值。</p>
<h4 id="code-aud-code-shou-zhong"><code>aud</code> （受众）</h4>
<p><code>aud</code> 是被用来确认令牌的可能接受者的字符串值或数组。如果值中包含 <code>:</code> 那么它就是一个 URI。
通常使用 URI 资源的声明是有效的。例如，在 <a href="https://tools.ietf.org/html/rfc7523#section-3" target="_blank" rel="external">OAuth</a> 中，接受者是授权服务器。应用程序处理令牌时，在针对不同的接受者的情况下，必须验证接受者是否是正确的或者拒绝令牌。</p>
<h4 id="code-jti-code-jwt-id"><code>jti</code> (JWT id)</h4>
<p>令牌的唯一标识符。每个发布的令牌的 <code>jti</code> 必须是唯一的，即使有很多发行人也是一样。<code>jti</code> 声明可以用于一次性的不能重放的令牌。</p>
<h2 id="ru-he-zai-wo-de-ying-yong-zhong-shi-yong-jwt">如何在我的应用中使用 JWT ?</h2>
<p>在最常见的场景中，客户端的浏览器将在认证服务中认证并接受返回的 JWT。然后客户端用某种方式（如内存，localStorage)存储这个令牌并与受保护的资源一起发送返回。通常令牌发送时是作为 cookie 或是 HTTP 请求中 <code>Authorization</code> 头部。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">GET /api/secured-resource HTTP/1.1  </span>
<span class="line">Host: example.com  </span>
<span class="line">Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0.OLvs36KmqB9cmsUrMpUutfhV52_iSz4bQMYJjkI_TLQ</span>
</pre></td></tr></table></figure>
<p>首选头部方法是出于安全的原因 - cookies 会很容易受 <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery" target="_blank" rel="external">CSRF</a>（跨站请求伪造）的影响，除非 CSRF 令牌是使用过的。</p>
<p>其次，cookies 只能发送返回到被发出的相同的域下（或者最多二级域下）。如果身份验证服务驻留在不同的域下，那么 cookies 得需要更强烈的创造性才行。</p>
<h3 id="ru-he-tong-guo-jwt-deng-chu">如何通过 JWT 登出?</h3>
<p>因为没有 session 数据存储在服务端了，所以不能再通过破坏 session 来注销了。因此登出成为了客户端的职责 - 一旦客户丢失了令牌不能再被授权，就可以被认为是登出了。</p>
<h2 id="zong-jie">总结</h2>
<p>我认为 JWTs 是一个在脱离 sessions 的情况下非常聪明的授权方式。它允许创建真正的服务端无状态的基于 RESTful 的服务，这也意味着不需要 session 存储。</p>
<p>与浏览器自动发送 session cookie 到任意匹配域/路径组合（老实说，在大多数情况下这里只有域的情况）的 URL 不一样的是，JWTs 可以选择性的只向需要身份授权的资源来发送。</p>
<p>对于客户端和服务端来说，它的实现非常简单，特别是已经有<a href="https://jwt.io/#libraries-io" target="_blank" rel="external">专门的库</a>来制造签名和验证令牌了。</p>
<p>感谢阅读！</p>
<p>如果你喜欢这篇文章的话，欢迎分享它。同样也十分欢迎你对它进行评论！</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文链接 : &lt;a href=&quot;https://float-middle.com/json-web-tokens-jwt-vs-sessions/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;JSON Web Tokens (J
    
    </summary>
    
      <category term="身份认证" scheme="http://qcyoung.com/categories/%E8%BA%AB%E4%BB%BD%E8%AE%A4%E8%AF%81/"/>
    
    
      <category term="JWT" scheme="http://qcyoung.com/tags/JWT/"/>
    
      <category term="RESTful" scheme="http://qcyoung.com/tags/RESTful/"/>
    
      <category term="session" scheme="http://qcyoung.com/tags/session/"/>
    
      <category term="web安全" scheme="http://qcyoung.com/tags/web%E5%AE%89%E5%85%A8/"/>
    
      <category term="token" scheme="http://qcyoung.com/tags/token/"/>
    
      <category term="JSON" scheme="http://qcyoung.com/tags/JSON/"/>
    
  </entry>
  
  <entry>
    <title>【译】如何编写避免垃圾开销的实时 JavaScript 代码</title>
    <link href="http://qcyoung.com/2016/06/12/%E3%80%90%E8%AF%91%E3%80%91%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99%E9%81%BF%E5%85%8D%E5%9E%83%E5%9C%BE%E5%BC%80%E9%94%80%E7%9A%84%E5%AE%9E%E6%97%B6%20Javascript%20%E4%BB%A3%E7%A0%81/"/>
    <id>http://qcyoung.com/2016/06/12/【译】如何编写避免垃圾开销的实时 Javascript 代码/</id>
    <published>2016-06-12T08:33:21.000Z</published>
    <updated>2017-04-21T08:51:03.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文链接 : <a href="https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript" target="_blank" rel="external">How to write low garbage real-time Javascript</a><br>
原文作者 : <a href="https://www.scirra.com/users/ashley" target="_blank" rel="external">Ashley </a><br>
译文出自 : <a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a><br>
译者 : <a href="http://qcyoung.com">yangzj1992</a><br>
校对者: <a href="https://github.com/L9m" target="_blank" rel="external">L9m</a>, <a href="https://github.com/ldhlfzysys" target="_blank" rel="external">Dwight</a>, <a href="https://github.com/godofchina" target="_blank" rel="external">宁金</a><br>
首发于: <a href="http://gold.xitu.io/entry/575d14937db2a2005437df32/detail" target="_blank" rel="external">掘金</a></p>
</blockquote>
<p><em>编辑于 2012 年 3 月 27 日: 哇，这篇文章已经写了有很长一段时间了，十分感谢那些精彩的回复！其中有一些对于一些技术的指正，如使用 'delete' 。我知道了使用它可能会导致其他的降速问题，因此，我们在引擎中极少使用它。一如既往的你还需要对所有的事进行权衡并且需要通过其他关注点来平衡垃圾回收机制，这也只是一个在我们引擎中发现的的实用、简单的技术列表，它并不是一个完整的参考列表。但是我希望它还是有用的！</em></p>
<p>一个用 Javascript 编写的 HTML5 游戏，要达到流畅体验的一个最大阻碍就是<strong>垃圾回收 ( GC )  卡顿</strong>。 Javascript 并没有一个显式的内存管理，意味着你创造东西后却不能释放它们占用的内存。因此迟早浏览器便会替你决定去清理它们：这时代码执行就会被暂停，浏览器会找出哪一部分内存是现在仍在被使用的，并把其他所有东西占用的内存释放掉。这篇博文将会去探究避开 GC 开销的技术细节，这对方便进行使用任何插件或是使用 Construct 2 进行 <a href="http://www.scirra.com/manual/15/sdk" title="Construct 2 Javascript Plugin and Behavior SDK" target="_blank" rel="external">Javascript SDK</a>开发都应该能派上用场。</p>
<p>浏览器有很多技术性手段来减少 GC 卡顿，但是如果你的代码创造了许多垃圾，迟早浏览器也将会暂停并进行清理。随着对象逐步创建的过程中，之后浏览器又突然清理，这最后将导致内存使用情况图表呈现 z 字形。例如，下面是 Chrome 在玩太空爆破手时的内存使用情况。</p>
<p><img src="https://www.scirra.com/images/chromememoryusage.png" alt="Chrome garbage-collected memory usage"></p>
<p><em>当在玩一个 Javascript 游戏时会呈现 z 字形的内存占用情况。这可能是一个内存泄漏错误，但是实际上是 JavaScript 的正常操作。</em></p>
<p>此外，游戏以 60 fps 运行时只有 16 ms 的时间来渲染每一帧，但是 GC 会很轻易的产生最少 100 ms 以上明显的卡顿，在更糟的情况下，这会导致不断卡顿的游戏体验，因此对于像游戏引擎一样实时运行的 Javascript 代码，解决办法是努力尝试在典型帧的持续时间内<em>你不要创建任何东西</em>。这实际上是相当困难的，因为有许多看上去无害的 Javascript 语句实际上却创造了垃圾，它们<em>都</em>必须从每帧动画的代码路径里删除掉。在 Construct 2 中我们竭尽全力减少每一处引擎的垃圾开销，但是你可以从图表中看到上面仍然有许多小的对象被创建所以 Chrome 还会每隔数秒进行一次清除。要注意这里只是一个小的清理 - 这里并没有大量的内存被清理出来，因为一个更高更极端的z曲线会更引起关注，但是它可能已经足够好了，因为小型的垃圾集合执行会更快并且偶尔的小卡顿也一般不太引人注意 - 因此我们应该看到了，有时我们确实很难避免产生新的资源分配。</p>
<p>同样重要的包括第三方插件以及开发人员行为也需要遵守这些原则，否则，一个写的不好的插件可以产生许多垃圾并会让游戏十分卡顿，尽管主引擎 Construct 2 已经是一个非常低垃圾开销的引擎了。</p>
<h2 id="jian-dan-de-ji-qiao">简单的技巧</h2>
<p>首先，最明显的是，关键词 <code>new</code> 指示了资源的分配，例如 <code>new Foo()</code>  在可能的情况下，它会在启动时尝试创建一个对象，并且尽可能长时间、简单的<strong>重新使用相同的对象</strong>。</p>
<p>不太明显的是，这里有三种快捷语法方式来相似的调用 <code>new</code> :</p>
<p><code>{}</code> <em>（创建一个新对象）</em>
<code>[]</code> <em>（创建一个新数组）</em>
<code>function () { ... }</code> <em>（创建一个新函数，也会被垃圾收集）</em></p>
<p>对于对象，用避免 <code>{}</code> 一样的方式来避免 <code>new</code> - 尝试去回收对象。请注意这包括像 <code>{ &quot;foo&quot;: &quot;bar&quot; }</code> 这样带属性的对象，也就是我们在函数中常用的一次性返回多个值。或许将每一次的返回值写入一个相同的(全局)对象来返回的写法是更好的 - 在文档中要仔细记录这一点，因为如果你保持引用这样的返回对象，可能在每次调用改变的时候发生错误。</p>
<p>实际上你可以回收一个存在的对象（如果它没有原型链）通过删除它的所有属性，将它还原为一个空的对象如 <code>{}</code> 一样。为此你可以使用 <code>cr.wipe(obj)</code> 函数，它的定义如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// remove all own properties on obj,</span></span>
<span class="line">effectively reverting it to a <span class="keyword">new</span> object</span>
<span class="line">cr.wipe = <span class="function"><span class="keyword">function</span> (<span class="params">obj</span>)</span>
<span class="line"></span>&#123;</span>
<span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> p <span class="keyword">in</span> obj)</span>
<span class="line">    &#123;</span>
<span class="line">        <span class="keyword">if</span> (obj.hasOwnProperty(p))</span>
<span class="line">            <span class="keyword">delete</span> obj[p];</span>
<span class="line">    &#125;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>因此在某些情况下，你可以调用 <code>cr.wipe(obj)</code> 并为其再次添加属性来重用一个对象。比起重新简单分配 <code>{}</code> 现场清除一个对象可能需要更长的时间，但是在实时处理的代码中更重要的是避免产生垃圾，从而减少未来可能产生的卡顿情况。</p>
<p>分配 <code>[]</code> 到一个数组中被经常用来作为一个快捷方式去清除这个数组（例如 <code>arr = [];</code>），但请注意这将创建一个新的空数组并使旧的数组成为一个垃圾！更好的写法是 <code>arr.length = 0;</code> ，这种方式具有相同的效果但却继续使用了相同的数组对象。</p>
<p>函数则有一点棘手，函数通常在执行时创建并且不倾向于在运行时进行过多分配 - 但这意味着它们在动态创建时很容易被忽视。一个例子是返回函数的函数。主要的游戏循环使用了 <code>setTimeout</code> 或者 <code>requestAnimationFrame</code> 方法来调用一个成员函数类似如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line">setTimeout(</span>
<span class="line">    (<span class="function"><span class="keyword">function</span> (<span class="params">self</span>) </span>&#123;</span>
<span class="line">        <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">            self.tick(); </span>
<span class="line">        &#125;; </span>
<span class="line">    &#125;)(<span class="keyword">this</span>)</span>
<span class="line">, <span class="number">16</span>);</span>
</pre></td></tr></table></figure>
<p>这看起来像是一个合理的方式来每 16ms 调用一次 <code>this.tick()</code> 。然而，这也意味着每一次执行 tick 函数都会返回一个新函数！这可以通过永久存储函数的方法来避免，例如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">// at startup</span></span>
<span class="line"><span class="keyword">this</span>.tickFunc = (<span class="function"><span class="keyword">function</span> (<span class="params">self</span>) </span>&#123; <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">self.tick(); &#125;; &#125;)(<span class="keyword">this</span>);</span>
<span class="line"></span>
<span class="line"><span class="comment">// in the tick() function</span></span>
<span class="line">setTimeout(<span class="keyword">this</span>.tickFunc, <span class="number">16</span>);</span>
</pre></td></tr></table></figure>
<p>这将在每次执行 tick 函数时重复使用相同的函数来代替产生一个新的函数。这个方法可以应用到任意其他地方的返回函数中或是运行创建的函数中。</p>
<h2 id="jin-jie-ji-qiao">进阶技巧</h2>
<p>随着我们的进展，进一步的避免产生垃圾变得更加困难，由于 Javascript 本身就是围绕着 GC 所设计的。许多 Javascript 中方便的库函数也总是创建了新的对象。这儿没有什么你可以做的但是当你返回文档查阅那些返回值时。例如，数组中的 <code>slice()</code> 方法会返回一个数组（基于保持不变的原始数组范围内），字符串的 <code>substr</code> 会返回一个新的字符串（基于保持不变的原始字符串字符的范围），等等。调用这些函数都会产生垃圾，而你能做的就是不要去调用它们，或是在极端情况下重写你的函数使它们不再产生垃圾。例如在 Construct 2 这种引擎，由于各种原因一个经常的操作是通过索引去删除数组里的一个元素。这个方法的快捷使用方式如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sliced = arr.slice(index + <span class="number">1</span>);</span>
<span class="line">arr.length = index;</span>
<span class="line">arr.push.apply(arr, sliced);</span>
</pre></td></tr></table></figure>
<p>然而 <code>slice()</code> 返回一个原始数组的后半部分来组成了一个新的数组，并且在被 (<code>arr.push.apply</code>)复制后产生了垃圾。由于这是我们引擎中一个生产垃圾的热门处，它被改写为了一个迭代版本：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = index, len = arr.length - <span class="number">1</span>; i &lt; len; i++)</span>
<span class="line">    arr[i] = arr[i + <span class="number">1</span>];</span>
<span class="line"></span>
<span class="line">arr.length = len;</span>
</pre></td></tr></table></figure>
<p>显然重写大量的库函数是相当痛苦的，所以你需要仔细的权衡需求实现的方便性以及垃圾产生之间的平衡。如果它在每帧中被调用了很多次，你可能最好重写这个你需要的函数库。</p>
<p>这里可以很容易的使用 <code>{}</code> 语法来沿着递归函数传递数据。通过一个数组来表示一个堆栈，在这个堆栈中对递归的每一级进行 push 和 pop 是更好的。更好的是，实际上你并不需要在数组中 pop  - 你应该将数组中最后一个对象像垃圾一样处理掉。来代替使用一个 <code>top index</code> 变量进行简单减量。然后为了代替 pushing ,则增加 <code>top index</code> 并且如果有的话就重用数组中的下一个对象，否则执行真正的 push。</p>
<p>此外，<strong>在所有可能的情况下避免向量对象</strong>（如 vector2 中的 x 和 y 属性）。虽然可能函数返回这些对象会让它们立刻改变或返回这两个值时会方便些，你可以在<em>每一帧</em>中轻松地结束数百个这样的创建对象，这将导致可怕的 GC 性能。这些函数必须分离出来在每个单独的组件中工作，例如：使用 <code>getX()</code> 和 <code>getY()</code> 来代替 <code>getPosition()</code> 来返回一个 vector2 对象。</p>
<p>有时候你无法摆脱一个库是一个产生垃圾的噩梦。 Box2Dweb 是一个典型的例子：它每一帧产生了数百个 b2Vec2 对象并且不断的在浏览器产生垃圾，并最终导致垃圾处理器产生显著的卡顿效果。在这种情况下最好的办法是创建一个缓存回收机制。我们一直在测试 Box2D (<a href="https://github.com/illandril/box2dweb-closure" target="_blank" rel="external">Box2Dweb-closure</a>) 的修正版本，它似乎可以使 GC 暂停进行缓解（虽然没有完全解决）。查阅 <a href="https://github.com/illandril/box2dweb-closure/blob/master/src/common/math/b2Vec2.js" target="_blank" rel="external">b2Vec2.js</a> 的 <code>Get</code> 和 <code>Free</code> 代码。这里有一个名字叫 <code>free cache</code> 的数组，在之后的整个代码中如果不再使用 b2Vec2，它就会在 free cache 中被释放，当需要请求一个新的 b2Vec2，而它如果在 free cache 中还存在那么它就会被重用，否则才会分配一个新的。这并不完美，在一些测试后通常只有一半的 b2Vec2s 被创建并回收，但它确实帮助 GC 缓解了压力从而减少了频繁的卡顿。</p>
<h2 id="jie-lun">结论</h2>
<p>在 Javascript 中很难去完全避免垃圾。它的垃圾收集模式根本上是不符合像游戏这样的实时软件的需求的。从 Javascript 代码中需要进行大量的工作来消除垃圾，因为有很多直接的代码含有创建大量垃圾的副作用。然而，只要仔细小心一些，Javascript 也是可以在实时项目中不产生或是制造很少的垃圾开销，而对于需要保持高度响应性的游戏和应用程序这也是至关重要的。</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文链接 : &lt;a href=&quot;https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript&quot; target=&quot;_blank&quot; rel=&quot;external&quot;
    
    </summary>
    
      <category term="JavaScript" scheme="http://qcyoung.com/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="http://qcyoung.com/tags/JavaScript/"/>
    
      <category term="浏览器" scheme="http://qcyoung.com/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
    
      <category term="性能优化" scheme="http://qcyoung.com/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/"/>
    
  </entry>
  
  <entry>
    <title>【个人总结】正则表达式语法及常用正则</title>
    <link href="http://qcyoung.com/2016/06/07/%E3%80%90%E4%B8%AA%E4%BA%BA%E6%80%BB%E7%BB%93%E3%80%91%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%AD%E6%B3%95%E5%8F%8A%E5%B8%B8%E7%94%A8%E6%AD%A3%E5%88%99/"/>
    <id>http://qcyoung.com/2016/06/07/【个人总结】正则表达式语法及常用正则/</id>
    <published>2016-06-07T10:13:05.000Z</published>
    <updated>2016-10-14T07:01:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>项目中常用正则表达式来进行校验，然而确实总是遗忘一些相关的语法，需要谷歌搜索一些不太好想或写的正则规则。所以在此总结一份。争取依靠个人总结不断更新这篇日志，节约之后 90% 的查阅搜索时间。</p>
<h2 id="gai-shu">概述</h2>
<p>正则表达式就是事先声明一组规则，用于匹配字符串中的字符。</p>
<h2 id="ji-ben-yu-fa">基本语法</h2>
<h3 id="xiu-shi-fu">修饰符</h3>
<table>
<thead>
<tr>
<th style="text-align:center">修饰符</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">i</td>
<td style="text-align:center">执行对大小写不敏感的匹配。</td>
</tr>
<tr>
<td style="text-align:center">g</td>
<td style="text-align:center">执行全局匹配（查找所有匹配而非在找到第一个匹配后停止）。</td>
</tr>
<tr>
<td style="text-align:center">m</td>
<td style="text-align:center">执行多行匹配。</td>
</tr>
</tbody>
</table>
<h3 id="yuan-zi-fu">元字符</h3>
<p>在正则表达式的模式中，有一些字符是有特殊含义的，被称为元字符。元字符都是针对单个字符匹配的。</p>
<table>
<thead>
<tr>
<th style="text-align:center">元字符</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">.</td>
<td style="text-align:center">查找单个字符，除了换行和行结束符。</td>
</tr>
<tr>
<td style="text-align:center">\w</td>
<td style="text-align:center">匹配大小写英文字符及数字 0 到 9 之间的任意一个及下划线，相当于 [a-zA-Z0-9_]</td>
</tr>
<tr>
<td style="text-align:center">\W</td>
<td style="text-align:center">不匹配大小写英文字符及数字 0 到 9 之间的任意一个，相当于 [^a-zA-Z0-9_]</td>
</tr>
<tr>
<td style="text-align:center">\s</td>
<td style="text-align:center">匹配任何空白字符，相当于 [\f\n\r\t\v]</td>
</tr>
<tr>
<td style="text-align:center">\S</td>
<td style="text-align:center">匹配任何非空白字符，相当于 [^\s]</td>
</tr>
<tr>
<td style="text-align:center">\d</td>
<td style="text-align:center">匹配任何 0 到 9 之间的单个数字，相当于 [0-9]</td>
</tr>
<tr>
<td style="text-align:center">\D</td>
<td style="text-align:center">不匹配任何 0 到 9 之间的单个数字，相当于 [^0-9]</td>
</tr>
<tr>
<td style="text-align:center">\b</td>
<td style="text-align:center">匹配单词边界。</td>
</tr>
<tr>
<td style="text-align:center">\B</td>
<td style="text-align:center">匹配非单词边界。</td>
</tr>
<tr>
<td style="text-align:center">\0</td>
<td style="text-align:center">查找 NUL 字符。</td>
</tr>
<tr>
<td style="text-align:center">\n</td>
<td style="text-align:center">查找换行符。</td>
</tr>
<tr>
<td style="text-align:center">\f</td>
<td style="text-align:center">查找换页符。</td>
</tr>
<tr>
<td style="text-align:center">\r</td>
<td style="text-align:center">查找回车符。</td>
</tr>
<tr>
<td style="text-align:center">\t</td>
<td style="text-align:center">查找制表符。</td>
</tr>
<tr>
<td style="text-align:center">\v</td>
<td style="text-align:center">查找垂直制表符。</td>
</tr>
<tr>
<td style="text-align:center">\xxx</td>
<td style="text-align:center">查找以八进制数 xxx 规定的字符。</td>
</tr>
<tr>
<td style="text-align:center">\xdd</td>
<td style="text-align:center">查找以十六进制数 dd 规定的字符。</td>
</tr>
<tr>
<td style="text-align:center">[\u4e00-\u9fa5]</td>
<td style="text-align:center">匹配任意单个汉字（这里用的是 Unicode 编码表示汉字的 )</td>
</tr>
<tr>
<td style="text-align:center">^</td>
<td style="text-align:center">匹配字符串的开头</td>
</tr>
<tr>
<td style="text-align:center">$</td>
<td style="text-align:center">匹配字符串的结尾</td>
</tr>
</tbody>
</table>
<h3 id="fang-gua-hao">方括号</h3>
<p>方括号用于查找某个范围内的字符</p>
<table>
<thead>
<tr>
<th style="text-align:center">表达式</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">[...]</td>
<td style="text-align:center">匹配方括号中的所有字符</td>
</tr>
<tr>
<td style="text-align:center">[^...]</td>
<td style="text-align:center">匹配非方括号中的所有字符</td>
</tr>
</tbody>
</table>
<h3 id="liang-ci">量词</h3>
<table>
<thead>
<tr>
<th style="text-align:center">表达式</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">n*</td>
<td style="text-align:center">匹配任何包含零个或多个 n 的字符串。</td>
</tr>
<tr>
<td style="text-align:center">n+</td>
<td style="text-align:center">匹配任何包含至少一个 n 的字符串。</td>
</tr>
<tr>
<td style="text-align:center">n?</td>
<td style="text-align:center">匹配任何包含零个或一个 n 的字符串。</td>
</tr>
<tr>
<td style="text-align:center">n{X}</td>
<td style="text-align:center">匹配包含 X 个 n 的序列的字符串。</td>
</tr>
<tr>
<td style="text-align:center">n{X,Y}</td>
<td style="text-align:center">匹配包含 X 或 Y 个 n 的序列的字符串。</td>
</tr>
<tr>
<td style="text-align:center">n{X,}</td>
<td style="text-align:center">匹配包含至少 X 个 n 的序列的字符串。</td>
</tr>
<tr>
<td style="text-align:center">n$</td>
<td style="text-align:center">匹配任何结尾为 n 的字符串。</td>
</tr>
<tr>
<td style="text-align:center">^n</td>
<td style="text-align:center">匹配任何开头为 n 的字符串。</td>
</tr>
<tr>
<td style="text-align:center">?=n</td>
<td style="text-align:center">匹配任何其后紧接指定字符串 n 的字符串。</td>
</tr>
<tr>
<td style="text-align:center">?!n</td>
<td style="text-align:center">匹配任何其后没有紧接指定字符串 n 的字符串。</td>
</tr>
</tbody>
</table>
<h3 id="duan-yan">断言</h3>
<table>
<thead>
<tr>
<th style="text-align:center">表达式</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">(exp)</td>
<td style="text-align:center">匹配 exp,并捕获文本到自动命名的组里</td>
</tr>
<tr>
<td style="text-align:center">(?<name>exp)</name></td>
<td style="text-align:center">匹配 exp,并捕获文本到名称为 name 的组里，也可以写成(?'name'exp)</td>
</tr>
<tr>
<td style="text-align:center">(?:exp)</td>
<td style="text-align:center">匹配 exp,不捕获匹配的文本，也不给此分组分配组号</td>
</tr>
<tr>
<td style="text-align:center">(?=exp)</td>
<td style="text-align:center">正向先行断言——代表字符串中的一个位置，紧接该位置之后的字符序列能够匹配 exp。</td>
</tr>
<tr>
<td style="text-align:center">(?!exp)</td>
<td style="text-align:center">负向先行断言——代表字符串中的一个位置，紧接该位置之后的字符序列不能匹配 exp</td>
</tr>
<tr>
<td style="text-align:center">(?&lt;=exp)</td>
<td style="text-align:center">正向后行断言,代表字符串中的一个位置，紧接该位置之前的字符序列能够匹配 exp。</td>
</tr>
<tr>
<td style="text-align:center">(?&lt;!exp)</td>
<td style="text-align:center">负向后行断言,代表字符串中的一个位置，紧接该位置之前的字符序列不能匹配 exp。</td>
</tr>
<tr>
<td style="text-align:center">(?#comment)</td>
<td style="text-align:center">这种类型的分组不对正则表达式的处理产生任何影响，用于提供注释让人阅读</td>
</tr>
</tbody>
</table>
<h2 id="chang-yong-shi-li">常用实例</h2>
<h3 id="xiao-yan-shu-zi-de-biao-da-shi">校验数字的表达式</h3>
<ul>
<li>
<p>数字：<code>^[0-9]*$</code></p>
</li>
<li>
<p>n 位的数字：<code>^\d{n}$</code></p>
</li>
<li>
<p>至少 n 位的数字：<code>^\d{n,}$</code></p>
</li>
<li>
<p>m-n 位的数字：<code>^\d{m,n}$</code></p>
</li>
<li>
<p>零和非零开头的数字：<code>^(0|[1-9][0-9]*)$</code></p>
</li>
<li>
<p>非零开头的最多带两位小数的数字：<code>^([1-9][0-9]*)+(.[0-9]{1,2})?$</code></p>
</li>
<li>
<p>带 1-2 位小数的正数或负数：<code>^(\-)?\d+(\.\d{1,2})?$</code></p>
</li>
<li>
<p>正数、负数、和小数：<code>^(\-|\+)?\d+(\.\d+)?$</code></p>
</li>
<li>
<p>有两位小数的正实数：<code>^[0-9]+(.[0-9]{2})?$</code></p>
</li>
<li>
<p>有 1~3 位小数的正实数：<code>^[0-9]+(.[0-9]{1,3})?$</code></p>
</li>
<li>
<p>非零的正整数：<code>^[1-9]\d*$</code> 或 <code>^([1-9][0-9]*){1,3}$</code> 或 <code>^\+?[1-9][0-9]*$</code></p>
</li>
<li>
<p>非零的负整数：<code>^\-[1-9][]0-9&quot;*$</code> 或 <code>^-[1-9]\d*$</code></p>
</li>
<li>
<p>非负整数：<code>^\d+$</code> 或 <code>^[1-9]\d*|0$</code></p>
</li>
<li>
<p>非正整数：<code>^-[1-9]\d*|0$</code> 或 <code>^((-\d+)|(0+))$</code></p>
</li>
<li>
<p>非负浮点数：<code>^\d+(\.\d+)?$</code> 或 <code>^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$</code></p>
</li>
<li>
<p>非正浮点数：<code>^((-\d+(\.\d+)?)|(0+(\.0+)?))$</code> 或 <code>^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$</code></p>
</li>
<li>
<p>正浮点数：<code>^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$</code> 或 <code>^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$</code></p>
</li>
<li>
<p>负浮点数：<code>^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$</code> 或 <code>^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$</code></p>
</li>
<li>
<p>浮点数：<code>^(-?\d+)(\.\d+)?$</code> 或 <code>^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$</code></p>
</li>
</ul>
<h3 id="xiao-yan-zi-fu-de-biao-da-shi">校验字符的表达式</h3>
<ul>
<li>
<p>汉字：<code>^[\u4e00-\u9fa5]{0,}$</code></p>
</li>
<li>
<p>英文和数字：<code>^[A-Za-z0-9]+$</code> 或 <code>^[A-Za-z0-9]{4,40}$</code></p>
</li>
<li>
<p>长度为 3-20 的所有字符：<code>^.{3,20}$</code></p>
</li>
<li>
<p>由 26 个英文字母组成的字符串：<code>^[A-Za-z]+$</code></p>
</li>
<li>
<p>由 26 个大写英文字母组成的字符串：<code>^[A-Z]+$</code></p>
</li>
<li>
<p>由 26 个小写英文字母组成的字符串：<code>^[a-z]+$</code></p>
</li>
<li>
<p>由数字和 26 个英文字母组成的字符串：<code>^[A-Za-z0-9]+$</code></p>
</li>
<li>
<p>由数字、26 个英文字母或者下划线组成的字符串：<code>^\w+$ 或 ^\w{3,20}$</code></p>
</li>
<li>
<p>中文、英文、数字包括下划线：<code>^[\u4E00-\u9FA5A-Za-z0-9_]+$</code></p>
</li>
<li>
<p>中文、英文、数字但不包括下划线等符号：<code>^[\u4E00-\u9FA5A-Za-z0-9]+$</code> 或 <code>^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$</code></p>
</li>
<li>
<p>可以输入含有 ^%&amp;',;=?$&quot; 等字符：<code>[^%&amp;',;=?$\x22]+</code></p>
</li>
<li>
<p>禁止输入含有 ~ 的字符：<code>[^~\x22]+</code></p>
</li>
</ul>
<h3 id="te-shu-xu-qiu-biao-da-shi">特殊需求表达式</h3>
<ul>
<li>
<p>Email 地址：<code>^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$</code></p>
</li>
<li>
<p>域名：<code>[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?</code></p>
</li>
<li>
<p>InternetURL：<code>[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&amp;=]*)?$</code></p>
</li>
<li>
<p>手机号码：<code>^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$</code></p>
</li>
<li>
<p>电话号码（&quot;XXX-XXXXXXX&quot;、&quot;XXXX-XXXXXXXX&quot;、&quot;XXX-XXXXXXX&quot;、&quot;XXX-XXXXXXXX&quot;、&quot;XXXXXXX&quot;和&quot;XXXXXXXX）：<code>^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$</code></p>
</li>
<li>
<p>国内电话号码（0511-4405222、021-87888822）：<code>\d{3}-\d{8}|\d{4}-\d{7}</code></p>
</li>
<li>
<p>身份证号（15 位、18 位数字）：
15 位： <code>^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$</code>
18 位： <code>^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$</code></p>
</li>
<li>
<p>短身份证号码（数字、字母 x 结尾）：<code>^([0-9]){7,18}(x|X)?$</code> 或 <code>^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$</code></p>
</li>
<li>
<p>帐号是否合法（字母开头，允许 5-16 字节，允许字母数字下划线）：<code>^[a-zA-Z][a-zA-Z0-9_]{4,15}$</code></p>
</li>
<li>
<p>密码（以字母开头，长度在 6~18 之间，只能包含字母、数字和下划线）：<code>^[a-zA-Z]\w{5,17}$</code></p>
</li>
<li>
<p>强密码（必须包含大小写字母和数字的组合，不能使用特殊字符，长度在 8-10 之间）：<code>^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$</code></p>
</li>
<li>
<p>日期格式：<code>^\d{4}-\d{1,2}-\d{1,2}</code></p>
</li>
<li>
<p>一年的 12 个月（01～09 和 1～12）：<code>^(0?[1-9]|1[0-2])$</code></p>
</li>
<li>
<p>一个月的 31 天（01～09 和 1～31）：<code>^((0?[1-9])|((1|2)[0-9])|30|31)$</code></p>
</li>
<li>
<p>xml 文件：<code>^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$</code></p>
</li>
<li>
<p>双字节字符：<code>[^\x00-\xff]</code> （包括汉字在内，可以用来计算字符串的长度（一个双字节字符长度计 2，ASCII字符计 1））</p>
</li>
<li>
<p>空白行的正则表达式：<code>\n\s*\r</code> （可以用来删除空白行）</p>
</li>
<li>
<p>HTML 标记的正则表达式：<code>&lt;(\S*?)[^&gt;]*&gt;.*?&lt;/\1&gt;|&lt;.*? /&gt;</code> （网上流传的版本太糟糕，上面这个也仅仅能部分，对于复杂的嵌套标记依旧无能为力）</p>
</li>
<li>
<p>首尾空白字符的正则表达式：<code>^\s*|\s*$或(^\s*)|(\s*$)</code> （可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等），非常有用的表达式）</p>
</li>
<li>
<p>校验金额（2位小数）： <code>^[0-9]+(.[0-9]{2})?$</code></p>
</li>
<li>
<p>腾讯 QQ 号：<code>[1-9][0-9]{4,}</code> （腾讯 QQ 号从 10000 开始）</p>
</li>
<li>
<p>中国邮政编码：<code>[1-9]\d{5}(?!\d)</code> （中国邮政编码为 6 位数字）</p>
</li>
<li>
<p>IP 地址：<code>\d+\.\d+\.\d+\.\d+</code> （提取IP地址时有用）</p>
</li>
<li>
<p>IP 地址：<code>((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))</code></p>
</li>
<li>
<p>IPV6 地址： <code>(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))</code></p>
</li>
<li>
<p>URL 链接： <code>((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&amp;%_\./-~-]*)?</code></p>
</li>
<li>
<p>EMOJI 表情: <code>([\uE000-\uF8FF]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDDFF])</code></p>
</li>
<li>
<p>检查 IE 版本 <code>^.*MSIE [5-8](?:\\.[0-9]+)?(?!.*Trident\\/[5-9]\\.0).*$</code></p>
</li>
</ul>
<h2 id="can-kao-zi-liao">参考资料</h2>
<p>这里的一篇图灵文章可以帮助你了解正则表达式的更深层原理:<a href="http://www.ituring.com.cn/tupubarticle/5512" target="_blank" rel="external">模式、自动机和正则表达式</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;项目中常用正则表达式来进行校验，然而确实总是遗忘一些相关的语法，需要谷歌搜索一些不太好想或写的正则规则。所以在此总结一份。争取依靠个人总结不断更新这篇日志，节约之后 90% 的查阅搜索时间。&lt;/p&gt;
&lt;h2 id=&quot;gai-shu&quot;&gt;概述&lt;/h2&gt;
&lt;p&gt;正则表达式就是事先
    
    </summary>
    
      <category term="工具资源" scheme="http://qcyoung.com/categories/%E5%B7%A5%E5%85%B7%E8%B5%84%E6%BA%90/"/>
    
    
      <category term="JavaScript" scheme="http://qcyoung.com/tags/JavaScript/"/>
    
      <category term="工具资源" scheme="http://qcyoung.com/tags/%E5%B7%A5%E5%85%B7%E8%B5%84%E6%BA%90/"/>
    
  </entry>
  
  <entry>
    <title>【译】基于 Meteor1.3 和 React 创建简单 App</title>
    <link href="http://qcyoung.com/2016/05/11/%E3%80%90%E8%AF%91%E3%80%91%E5%9F%BA%E4%BA%8E%20Meteor1.3%20%E5%92%8C%20React%20%E5%88%9B%E5%BB%BA%E7%AE%80%E5%8D%95%20App/"/>
    <id>http://qcyoung.com/2016/05/11/【译】基于 Meteor1.3 和 React 创建简单 App/</id>
    <published>2016-05-11T10:13:05.000Z</published>
    <updated>2016-10-24T05:53:16.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>原文链接 : <a href="https://medium.com/@kenrogers/build-a-journaling-app-with-meteor-1-3-beta-react-react-bootstrap-and-mantra-7965d9e9fc23#.bjcr4yhbf" target="_blank" rel="external">Build A Journaling App with Meteor 1.3 (Beta), React, React-Bootstrap, and Mantra</a><br>
原文作者 : <a href="https://medium.com/@kenrogers" target="_blank" rel="external">Ken Rogers</a><br>
译文出自 : <a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a><br>
译者 : <a href="http://qcyoung.com">yangzj1992</a><br>
校对者: <a href="https://github.com/geeeeeeeeek" target="_blank" rel="external">Zhongyi Tong</a>, <a href="https://github.com/lx7575000" target="_blank" rel="external">刘鑫</a><br>
首发于: <a href="http://gold.xitu.io/#/entry/5732a5af79df540060df2e53" target="_blank" rel="external">掘金</a></p>
</blockquote>
<p>由于目前 Meteor 1.3 正式版仍在开发中，在这份 Meteor 指南里我们采用了目前可以获取到的 Meteor 1.3 beta 版本进行开发。尽管 Meteor 1.3 版本很棒并有着许多精彩的改进，但部分人对于到底应该如何使用它来进行开发仍有一些困惑。 MDG(Meteor Development Group) 目前正在编写 Meteor 1.3 版指南，随着 1.3 正式版的发布，我们将会获得 Meteor 1.3 最佳开发实践的确切信息。</p>
<p><strong>旁注：我写了一本关于使用 Meteor 1.3 ，React ，React-Bootstrap 遵循 Mantra 框架规范进行应用开发的书，点击<a href="http://kenrogers.co/meteor-react" target="_blank" rel="external">这里</a>可以了解更多并免费获取前三章的内容</strong>。</p>
<p>我写这份指南的目的是让开发者现在就能用上 Meteor 1.3。当你阅读本指南时，需要留意 1.3 版本目前仍处于 beta 阶段，因此内容可能发生任何变化。我会尽我所能的更新这份指南来适应最新版本。如果你发现了什么过期的内容，希望能指出来让我知道。</p>
<p>在这份指南中，我们将要构建一个简单的任务清单，开个玩笑，不会再是任务清单了。我们将用 Meteor 1.3 ，React 和 React-Bootstrap 构建一个基本的日志应用。</p>
<p>我们将采用 Arunoda 的 Mantra 规范。如果你对 Mantra 不够熟悉，你可以访问<a href="https://github.com/kadirahq/mantra" target="_blank" rel="external">这里</a>了解更多。 基本来说， Mantra 应用程序架构规范向我们提供了一个宜于维护的方式去构建 Meteor 应用。</p>
<p>在我们开始前，你需要安装好 Meteor 并需要对 Meteor 的原理及使用方法具备一定的理解。如果你并不熟悉，可以看看<a href="https://www.meteor.com/tutorials/react/creating-an-app" target="_blank" rel="external">官方 Meteor 向导</a>。</p>
<p>首先我们将通过一些资源来熟悉 Meteor 1.3 和 Mantra ，然后运用它们创建一个简单的日志应用。</p>
<h4 id="liao-jie-meteor-1-3">了解 Meteor 1.3</h4>
<p>首先我们要介绍 Meteor 1.3 并且了解它的主要改动包含什么。在 1.3 版本中它最大的改动是完全支持 ES2015 并提供了模块功能。</p>
<p>一开始你会发现这和我们以往开发 Meteor 应用很不一样，但一旦你习惯了你会发现体验是相当不错的，尤其是你要使用 Mantra 的架构的话。</p>
<p>这里有一篇关于 Meteor 1.3 的模块机制是怎样工作的精彩介绍：<a href="https://github.com/meteor/meteor/blob/release-1.3/packages/modules/README.md" target="_blank" rel="external">https://github.com/meteor/meteor/blob/release-1.3/packages/modules/README.md</a></p>
<p>使用模块可以让我们更容易的去写更多的代码，更加模块化。这样我们可以更好地组织我们的应用，由于 Meteor 1.3 也添加了对 npm 包的支持，我们不必再像过去那样只有 Meteor 包支持的情况下进行开发了。</p>
<p>接下来，你可以看看这三篇文章来了解如何在 Meteor 1.3 中配置 React ，并用它来处理数据。第二篇会向你介绍容器组件，这是使用 Mantra 开发的一个重要部分。</p>
<ol>
<li><a href="https://voice.kadira.io/getting-started-with-meteor-1-3-and-react-15e071e41cd1#.qn4zj3420" target="_blank" rel="external">https://voice.kadira.io/getting-started-with-meteor-1-3-and-react-15e071e41cd1#.qn4zj3420</a></li>
<li><a href="https://voice.kadira.io/let-s-compose-some-react-containers-3b91b6d9b7c8#.pd37xdmpn" target="_blank" rel="external">https://voice.kadira.io/let-s-compose-some-react-containers-3b91b6d9b7c8#.pd37xdmpn</a></li>
<li><a href="https://voice.kadira.io/using-meteor-data-and-react-with-meteor-1-3-13cb0935dedb#.3oe66g4ye" target="_blank" rel="external">https://voice.kadira.io/using-meteor-data-and-react-with-meteor-1-3-13cb0935dedb#.3oe66g4ye</a></li>
</ol>
<h4 id="di-yi-bu-xiang-mu-she-zhi">第一步 — 项目设置</h4>
<p>通常来说，我们需要做的第一件事就是通过 Meteor 1.3 来创建我们的 Meteor 项目，像下面这样。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">meteor create journal --release <span class="number">1.3</span>-modules-beta<span class="number">.8</span></span>
</pre></td></tr></table></figure>
<p><strong>但是稍等一下</strong>，构建一个 Mantra 应用需要非常多的项目设置
，为了加快开发速度，我已经使用 Meteor 1.3，React，Mantra 创建创建了一个样板项目。我们就用它来代替初始方案直接开始。</p>
<p>如果你想知道这些具体做了什么，查看 <a href="https://kadirahq.github.io/mantra/" target="_blank" rel="external">Mantra 规范</a>和 <a href="https://github.com/mantrajs/mantra-sample-blog-app" target="_blank" rel="external">Mantra 博客应用实例</a>。</p>
<p>现在我们安装完样板项目，它完全包含了遵循 Mantra 规范的 Meteor 项目中所有你需要的核心文件和目录。</p>
<p>你可以通过以下命令 clone 项目：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> git@github.com:kenrogers/mantraplate.git</span>
</pre></td></tr></table></figure>
<p>然后切换到刚创建的目录中运行</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install</span>
</pre></td></tr></table></figure>
<p>这样会安装本应用依赖的所有的包。你可以查看示例项目来熟悉整个目录结构。</p>
<p>它包含完整的布局，路由系统以及具有注册，登录登出功能的用户系统。</p>
<p>在这份指南中，我们将要讨论这些内容是如何组合在一起的，以及如何使用户在应用中添加日志记录的功能。</p>
<p>在我们添加内容前我们来看看样例项目的目录结构，你可以发现，在客户端文件夹中我们将整个应用分成一个个模块，这些模块是你的应用的主要组成部分。</p>
<p>我们总是需要一个核心模块，如果你的 APP 比较简单，这个核心模块就是你所唯一需要的。在我们的 APP 中包含了核心模块和用户模块，这里还要加入一个条目模块来添加我们的日志记录。</p>
<p>这样的模块结构让我们可以轻松地组织我们的代码。</p>
<p>在用户模块中，看看 containers 和 components 文件夹中的 NewUser 文件，。container 文件夹如下所示。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> NewUser <span class="keyword">from</span> <span class="string">'../components/NewUser.jsx'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;useDeps, composeWithTracker, composeAll&#125; <span class="keyword">from</span> <span class="string">'mantra-core'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">const</span> composer = <span class="function">(<span class="params">&#123;context, clearErrors&#125;, onData</span>) =&gt;</span> &#123;</span>
<span class="line"> <span class="keyword">const</span> &#123;LocalState&#125; = context();</span>
<span class="line"> <span class="keyword">const</span> error = LocalState.get(<span class="string">'CREATE_USER_ERROR'</span>);</span>
<span class="line"> onData(<span class="literal">null</span>, &#123;error&#125;);</span>
<span class="line"> <span class="keyword">return</span> clearErrors;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">const</span> depsMapper = <span class="function">(<span class="params">context, actions</span>) =&gt;</span> (&#123;</span>
<span class="line"> <span class="attr">create</span>: actions.users.create,</span>
<span class="line"> <span class="attr">clearErrors</span>: actions.users.clearErrors,</span>
<span class="line"> <span class="attr">context</span>: <span class="function"><span class="params">()</span> =&gt;</span> context</span>
<span class="line">&#125;);</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> composeAll(</span>
<span class="line"> composeWithTracker(composer),</span>
<span class="line"> useDeps(depsMapper)</span>
<span class="line">)(NewUser);</span>
</pre></td></tr></table></figure>
<p>你可以看到我们在这里实际上并没有进行任何渲染，我们只是做一些设置和清理的工作，然后在 NewUser 组件中我们才实际上渲染了视图。</p>
<p>如果你运行应用并访问 /register 路由，打开 React 开发者工具，你可以看到 react-komposer 正在后台执行。它会创建一个容器组件负责处理底层子组件的数据或是 UI 组件。</p>
<p>当我们获取数据时容器组件的用途将会得到具体的展现，但是这里我们不这样处理。</p>
<h4 id="di-er-bu-yuan-xing-zhi-zuo">第二步 — 原型制作</h4>
<p>对于这个日志程序我们准备使用 React-Bootstrap 。它可以很方便地使用 Bootstrap 来创建 React 应用。这种方式易于上手，并且保持了模块化，正如我们所愿。</p>
<p>让我们设置好并添加一个简单的表单。</p>
<p>首先让我们为项目添加 <code>react-bootstrap</code></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">npm install react-bootstrap</span>
</pre></td></tr></table></figure>
<p>因为 React-Bootstrap 并不依赖任何特定的 Bootstrap 库，所以我们需要自行添加，现在让我们添加 Twitter 的官方 Meteor 包。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">meteor add twbs:bootstrap</span>
</pre></td></tr></table></figure>
<p>首先我们用 React-Bootstrap 来修改 MainLayout.jsx 文件的内容如下：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;Grid, Row&#125; <span class="keyword">from</span> <span class="string">'react-bootstrap'</span>;</span>
<span class="line"><span class="keyword">const</span> Layout = <span class="function">(<span class="params">&#123;content = (</span>) =&gt;</span> <span class="literal">null</span> &#125;) =&gt; (</span>
<span class="line"> <span class="xml"><span class="tag">&lt;<span class="name">grid</span>&gt;</span></span>
<span class="line">  <span class="tag">&lt;<span class="name">row</span>&gt;</span></span>
<span class="line">   <span class="tag">&lt;<span class="name">h1</span>&gt;</span>Journal<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span>
<span class="line">   &#123;content()&#125;</span>
<span class="line">  <span class="tag">&lt;/<span class="name">row</span>&gt;</span></span>
<span class="line"> <span class="tag">&lt;/<span class="name">grid</span>&gt;</span></span></span>
<span class="line">);</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Layout;</span>
</pre></td></tr></table></figure>
<p>在这里，我们从 react-boostrap 包中引入 Grid 和 Row 组件，并且像使用 div 一样为它们添加合适的 bootstrap 类。想要了解更多关于这个优秀的包的工作原理，可以在<a href="https://react-bootstrap.github.io/components.html" target="_blank" rel="external">这里</a>查看组件列表。</p>
<p>现在让我们修改 NewUser 和 Login UI 的组件让他们更友好地贴近 Bootstrap 。打开 NewUser.jsx 文件进行如下修改：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123; Col, Panel, Input, ButtonInput, Glyphicon &#125; <span class="keyword">from</span> <span class="string">'react-bootstrap'</span>;</span>
<span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NewUser</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span>
<span class="line"> render() &#123;</span>
<span class="line"> <span class="keyword">const</span> &#123;error&#125; = <span class="keyword">this</span>.props;</span>
<span class="line"> <span class="keyword">return</span> (</span>
<span class="line">   &lt;col xs="&#123;12&#125;" sm="&#123;6&#125;" smoffset="&#123;3&#125;"&gt;</span>
<span class="line">    &lt;panel&gt;</span>
<span class="line">     &lt;h1&gt;Register&lt;/h1&gt;</span>
<span class="line">     &#123;error ? &lt;p style="&#123;&#123;color:" 'red'&#125;&#125;=""&gt;&#123;error&#125;&lt;/p&gt; : null&#125;</span>
<span class="line">     &lt;form&gt;</span>
<span class="line">      &lt;input ref="”email”" type="”email”" placeholder="”Email”"&gt;</span>
<span class="line">      &lt;input ref="”password”" type="”password”" placeholder="”Password”"&gt;</span>
<span class="line">      &lt;buttoninput onclick="&#123;this.createUser.bind(this)&#125;" bsstyle="”primary”" type="”submit”" value="”Sign" up”=""&gt;</span>
<span class="line">     &lt;/buttoninput&gt;&lt;/form&gt;</span>
<span class="line">    &lt;/panel&gt;</span>
<span class="line"></span>
<span class="line">  )</span>
<span class="line"> &#125;</span>
<span class="line">createUser(e) &#123;</span>
<span class="line"> e.preventDefault();</span>
<span class="line"> const &#123;create&#125; = this.props;</span>
<span class="line"> const &#123;email, password&#125; = this.refs;</span>
<span class="line"> create(email.getValue(), password.getValue());</span>
<span class="line"> email.getInputDOMNode().value = '';</span>
<span class="line"> password.getInputDOMNode().value = '';</span>
<span class="line"> &#125;</span>
<span class="line">&#125;</span>
<span class="line">export default NewUser;</span>
</pre></td></tr></table></figure>
<p>这个表单十分简单，它仅仅负责显示自身并调用 create 方法。这里我们简单介绍一下。</p>
<p>在我们的 actions 文件夹中，它们负责处理我们应用的逻辑，下面这一行</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">create(email.getValue(), password.getValue());</span>
</pre></td></tr></table></figure>
<p>将调用该方法并创建实际用户。 Mantra 重点强调了希望把一切分离成单独的文件。因此，我们将文件分为展示、逻辑、以及这个应用程序的每个组件。</p>
<p>现在让我们修改登录表单如下：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123; Col, Panel, Input, ButtonInput, Glyphicon &#125; <span class="keyword">from</span> <span class="string">'react-bootstrap'</span>;</span>
<span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Login</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span>
<span class="line"> render() &#123;</span>
<span class="line">  <span class="keyword">const</span> &#123;error&#125; = <span class="keyword">this</span>.props;</span>
<span class="line">  <span class="keyword">return</span> (</span>
<span class="line">   &lt;col xs="&#123;12&#125;" sm="&#123;6&#125;" smoffset="&#123;3&#125;"&gt;</span>
<span class="line">    &lt;panel&gt;</span>
<span class="line">     &lt;h1&gt;Login&lt;/h1&gt;</span>
<span class="line">     &#123;error ? &lt;p style="&#123;&#123;color:" 'red'&#125;&#125;=""&gt;&#123;error&#125;&lt;/p&gt; : null&#125;</span>
<span class="line">     &lt;form&gt;</span>
<span class="line">      &lt;input ref="”email”" type="”email”" placeholder="”Email”"&gt;</span>
<span class="line">      &lt;input ref="”password”" type="”password”" placeholder="”Password”"&gt;</span>
<span class="line">      &lt;buttoninput onclick="&#123;this.login.bind(this)&#125;" bsstyle="”primary”" type="”submit”" value="”Login”/"&gt;</span>
<span class="line">     &lt;/buttoninput&gt;&lt;/form&gt;</span>
<span class="line">    &lt;/panel&gt;</span>
<span class="line"></span>
<span class="line">  )</span>
<span class="line"> &#125;</span>
<span class="line">login(e) &#123;</span>
<span class="line"> e.preventDefault();</span>
<span class="line"> const &#123;loginUser&#125; = this.props;</span>
<span class="line"> const &#123;email, password&#125; = this.refs;</span>
<span class="line"> loginUser(email.getValue(), password.getValue());</span>
<span class="line"> email.getInputDOMNode().value = '';</span>
<span class="line"> password.getInputDOMNode().value = '';</span>
<span class="line"> &#125;</span>
<span class="line">&#125;</span>
<span class="line">export default Login;</span>
</pre></td></tr></table></figure>
<p>这基本上是一个相同的表单，但我们将用登录方法来代替它的逻辑。</p>
<p>React-Boostrap 非常易于使用，我们只需要安装好项目，使用 import 函数引入每个我们想要引用的组件，就像其他类型一样渲染这些组件。</p>
<p>我们处理使用数据的方法则有一些不同，因为它是组件，而不是我们实际需要处理的输入内容，我们需要使用特殊的 React-Bootstrap 函数 getValue() 来帮我们轻松地取值。</p>
<h4 id="di-san-bu-tian-jia-tiao-mu-mo-kuai">第三步 — 添加条目模块</h4>
<p>现在，我们将添加新的模块来管理我们的日志条目，首先让我们设置目录和文件。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line">mkdir client/modules/entries</span>
<span class="line"><span class="built_in">cd</span> client/modules/entries</span>
<span class="line">mkdir actions components containers</span>
<span class="line">touch index.js</span>
<span class="line">touch actions/index.js actions/entries.js</span>
<span class="line">touch components/NewEntry.jsx components/Entry.jsx components/EntryList.jsx</span>
<span class="line">touch containers/NewEntry.js containers/Entry.js containers/EntryList.js</span>
</pre></td></tr></table></figure>
<p>好了，现在我们有了应用中所需要的所有文件和文件夹。让我们来做一些真正的开发工作吧。</p>
<p>首先，让我们再来看一下我们创建的应用结构。这里我们制造了一个简单的 Mantra 模块。我们通过这些目录文件来看看他们是怎么做到交互的。通过这些将会让你很好地理解如何使用 Meteor 1.3 和 Mantra 。</p>
<p><strong>索引</strong></p>
<p>Mantra 有一个庞大的单一入口。这个索引文件负责导入内容随后导出路由和动作，这样在我们导入模块时即可使用。通过这种方式我们不用担心再单独导入每个文件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> actions <span class="keyword">from</span> <span class="string">'./actions'</span>;</span>
<span class="line"><span class="keyword">import</span> routes <span class="keyword">from</span> <span class="string">'../core/routes.jsx'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line"> routes,</span>
<span class="line"> actions</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p><strong>动作</strong></p>
<p>动作文件夹负责我们应用的所有逻辑。你可以看到我们在这里创建了两个文件。首先是一个索引文件。这是一个类似目的模块的索引文件。我们向里面添加下面的内容。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> entries <span class="keyword">from</span> <span class="string">'./entries'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line"> entries</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>上面所做的就是导入条目文件，在条目文件中有我们的动作逻辑。这只是为了更容易地从其他文件导入我们的逻辑。</p>
<p>接下来我们要添加实际逻辑，这些包含了我们的应用逻辑。这里我们要添加一个创建条目的函数方法。</p>
<p>你可以通过查看例子中 users 模块的方法文件来了解这是怎么工作的。</p>
<p>在 actions.js 中添加下面的内容来补全条目模块。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span>
<span class="line"> create(&#123;Meteor, LocalState, FlowRouter&#125;, text) &#123;</span>
<span class="line">  <span class="keyword">if</span> (!text) &#123;</span>
<span class="line">   <span class="keyword">return</span> LocalState.set(<span class="string">'CREATE_ENTRY_ERROR'</span>, <span class="string">'Text is required.'</span>);</span>
<span class="line">  &#125;</span>
<span class="line">  LocalState.set(<span class="string">'CREATE_ENTRY_ERROR'</span>, <span class="literal">null</span>);</span>
<span class="line">  Meteor.call(<span class="string">'entries.create'</span>, text, (err) =&gt; &#123;</span>
<span class="line">   <span class="keyword">if</span> (err) &#123;</span>
<span class="line">    <span class="keyword">return</span> LocalState.set(<span class="string">'CREATE_ENTRY_ERROR'</span>, err.message);</span>
<span class="line">   &#125;</span>
<span class="line">  &#125;);</span>
<span class="line"> &#125;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<p>当我们填写表格来创建一个新条目时，这就是会被执行的方法，我们就快设置好这些组件了，让我们先别管服务端的东西，为我们的条目创建集合和方法。</p>
<p>在 lib 目录中打开 collections.js 文件然后添加条目集合。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> Entries = <span class="keyword">new</span> Mongo.Collection(<span class="string">'entries'</span>);</span>
</pre></td></tr></table></figure>
<p>现在在 server 目录下的 methods 目录中添加 entries.js 文件，并添加以下内容来创建一个创建新条目的方法。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;Entries&#125; <span class="keyword">from</span> <span class="string">'/lib/collections'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;Meteor&#125; <span class="keyword">from</span> <span class="string">'meteor/meteor'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;check&#125; <span class="keyword">from</span> <span class="string">'meteor/check'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line"> Meteor.methods(&#123;</span>
<span class="line">  <span class="string">'entries.create'</span>(text) &#123;</span>
<span class="line">   check(text, <span class="built_in">String</span>);</span>
<span class="line">   <span class="keyword">const</span> createdAt = <span class="keyword">new</span> <span class="built_in">Date</span>();</span>
<span class="line">   <span class="keyword">const</span> entry = &#123;text, createdAt&#125;;</span>
<span class="line">   Entries.insert(entry);</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;);</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>这是一个我们刚创建的将要被调用的方法。</p>
<p>我们还需要将下面代码添加到 methods 文件夹中的 index.js 文件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> entries <span class="keyword">from</span> <span class="string">'./entries'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line"> entries();</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p><strong>组件</strong></p>
<p>组件目录存放着我们的 UI 组件。这里的组件只负责显示我们的接口内容，他们不操作任何数据，这些是容器组件需要做的。</p>
<p>让我们创建 UI 组件，然后我们将建立相应的容器组件。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;Grid, Row, Col&#125; <span class="keyword">from</span> <span class="string">'react-bootstrap'</span>;</span>
<span class="line"><span class="keyword">const</span> Entry = <span class="function">(<span class="params">&#123;entry&#125;</span>) =&gt;</span> (</span>
<span class="line"> <span class="xml"><span class="tag">&lt;<span class="name">grid</span>&gt;</span></span>
<span class="line">  <span class="tag">&lt;<span class="name">row</span>&gt;</span></span>
<span class="line">   <span class="tag">&lt;<span class="name">col</span> <span class="attr">xs</span>=<span class="string">"&#123;6&#125;"</span> <span class="attr">xsoffset</span>=<span class="string">"&#123;3&#125;"</span>&gt;</span></span>
<span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span></span>
<span class="line">     &#123;entry.text&#125;</span>
<span class="line">    <span class="tag">&lt;/<span class="name">p</span>&gt;</span> </span>
<span class="line"></span>
<span class="line">  <span class="tag">&lt;/<span class="name">row</span>&gt;</span></span>
<span class="line"> <span class="tag">&lt;/<span class="name">grid</span>&gt;</span></span>
<span class="line">);</span>
<span class="line">export default Entry;</span></span>
</pre></td></tr></table></figure>
<p>这里获取到的 {entry} 对象是我们容器组件要传递给它属性。它包含了我们的数据。</p>
<p>接下来我们创建 NewEntry 组件。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123; Col, Panel, Input, ButtonInput, Glyphicon &#125; <span class="keyword">from</span> <span class="string">'react-bootstrap'</span>;</span>
<span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NewEntry</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span>
<span class="line"> render() &#123;</span>
<span class="line">  <span class="keyword">const</span> &#123;error&#125; = <span class="keyword">this</span>.props;</span>
<span class="line">  <span class="keyword">return</span> (</span>
<span class="line">   &lt;col xs="&#123;12&#125;" sm="&#123;6&#125;" smoffset="&#123;3&#125;"&gt;</span>
<span class="line">    &lt;panel&gt;</span>
<span class="line">     &lt;h1&gt;Add a New Entry&lt;/h1&gt;</span>
<span class="line">     &#123;error ? &lt;p style="&#123;&#123;color:" 'red'&#125;&#125;=""&gt;&#123;error&#125;&lt;/p&gt; : null&#125;</span>
<span class="line">     &lt;form&gt;</span>
<span class="line">      &lt;input ref="”text”" type="”textarea”" placeholder="”Add" your="" entry”=""&gt;</span>
<span class="line">      &lt;buttoninput onclick="&#123;this.newEntry.bind(this)&#125;" bsstyle="”primary”" type="”submit”" value="”Create”/"&gt;</span>
<span class="line">     &lt;/buttoninput&gt;&lt;/form&gt;</span>
<span class="line">    &lt;/panel&gt;</span>
<span class="line"></span>
<span class="line">  )</span>
<span class="line"> &#125;</span>
<span class="line"> newEntry(e) &#123;</span>
<span class="line">  e.preventDefault();</span>
<span class="line">  const &#123;create&#125; = this.props;</span>
<span class="line">  const &#123;text&#125; = this.refs;</span>
<span class="line">  create(text.getValue());</span>
<span class="line">  text.getInputDOMNode().value = '';</span>
<span class="line"> &#125;</span>
<span class="line">&#125;</span>
<span class="line">export default NewEntry;</span>
</pre></td></tr></table></figure>
<p>这里我们使用了更多的 React-Bootstrap 组件，你会留意到为了获取输入的值，我们用了一个特别的 getValue() 方法。这是因为我们的渲染组件实际上并不是输入框，输入框是在这些组件的内部。所以我们需要使用这个函数来访问它。</p>
<p>最后，我们创建一个 EntryList 组件。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;Grid, Row, Col, Panel&#125; <span class="keyword">from</span> <span class="string">'react-bootstrap'</span>;</span>
<span class="line"><span class="keyword">const</span> EntryList = <span class="function">(<span class="params">&#123;entries&#125;</span>) =&gt;</span> (</span>
<span class="line"> <span class="xml"><span class="tag">&lt;<span class="name">grid</span>&gt;</span></span>
<span class="line">  <span class="tag">&lt;<span class="name">row</span>&gt;</span></span>
<span class="line">   &#123;entries.map(entry =&gt; (</span>
<span class="line">    <span class="tag">&lt;<span class="name">col</span> <span class="attr">xs</span>=<span class="string">"&#123;3&#125;"</span> <span class="attr">key</span>=<span class="string">"&#123;entry._id&#125;"</span>&gt;</span></span>
<span class="line">     <span class="tag">&lt;<span class="name">panel</span>&gt;</span></span>
<span class="line">      <span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;entry.title&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span>
<span class="line">      <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"&#123;`/entry/$&#123;entry._id&#125;`&#125;"</span>&gt;</span>View Entry<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span>
<span class="line">     <span class="tag">&lt;/<span class="name">panel</span>&gt;</span></span>
<span class="line"></span>
<span class="line">   ))&#125;</span>
<span class="line">  <span class="tag">&lt;/<span class="name">row</span>&gt;</span></span>
<span class="line"> <span class="tag">&lt;/<span class="name">grid</span>&gt;</span></span>
<span class="line">);</span>
<span class="line">export default EntryList;</span></span>
</pre></td></tr></table></figure>
<p>接下来，我们通过属性来获取数据，设置一些 React-Bootstrap 组件，并为每个入口映射一个对应专属的面板。</p>
<p>现在，让我们来设置这些容器组件，首先从最简单的 NewEntry 容器组件开始。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> NewEntry <span class="keyword">from</span> <span class="string">'../components/NewEntry.jsx'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;useDeps, composeWithTracker, composeAll&#125; <span class="keyword">from</span> <span class="string">'mantra-core'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">const</span> composer = <span class="function">(<span class="params">&#123;context, clearErrors&#125;, onData</span>) =&gt;</span> &#123;</span>
<span class="line"> <span class="keyword">const</span> &#123;LocalState&#125; = context();</span>
<span class="line"> <span class="keyword">const</span> error = LocalState.get(<span class="string">'CREATE_ENTRY_ERROR'</span>);</span>
<span class="line"> onData(<span class="literal">null</span>, &#123;error&#125;);</span>
<span class="line"> <span class="keyword">return</span> clearErrors;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">const</span> depsMapper = <span class="function">(<span class="params">context, actions</span>) =&gt;</span> (&#123;</span>
<span class="line"> <span class="attr">create</span>: actions.entries.create,</span>
<span class="line"> <span class="attr">clearErrors</span>: actions.entries.clearErrors,</span>
<span class="line"> <span class="attr">context</span>: <span class="function"><span class="params">()</span> =&gt;</span> context</span>
<span class="line">&#125;);</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> composeAll(</span>
<span class="line"> composeWithTracker(composer),</span>
<span class="line"> useDeps(depsMapper)</span>
<span class="line">)(NewEntry);</span>
</pre></td></tr></table></figure>
<p>这里你应该已经对 react-komposer 较为熟悉了，我们将用它来创建这一容器组件。它负责创建一个容器组件，用于处理错误、调用合适的动作。在大多数情况下，它还将获取数据并通过属性传给 UI 组件。</p>
<p>depsMapper 通过 react-komposer 中的 useDeps 函数检索动作及上下文内容并将它们传递给 UI 组件。</p>
<p>clearErrors 方法负责清除组件卸载时发生的所有错误。</p>
<p>让我们在创建条目方法时创建这一方法。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
</pre></td><td class="code"><pre><span class="line">clearErrors(&#123;LocalState&#125;) &#123;</span>
<span class="line"> <span class="keyword">return</span> LocalState.set(<span class="string">'SAVING_ERROR'</span>, <span class="literal">null</span>);</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>现在我们将要创建 EntryList 组件的容器。这个稍许有些复杂，因为我们会实际上获取一些数据。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> EntryList <span class="keyword">from</span> <span class="string">'../components/EntryList.jsx'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;useDeps, composeWithTracker, composeAll&#125; <span class="keyword">from</span> <span class="string">'mantra-core'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">const</span> composer = <span class="function">(<span class="params">&#123;context&#125;, onData</span>) =&gt;</span> &#123;</span>
<span class="line"> <span class="keyword">const</span> &#123;Meteor, Collections&#125; = context();</span>
<span class="line"> <span class="keyword">if</span> (Meteor.subscribe(<span class="string">'entries.list'</span>).ready()) &#123;</span>
<span class="line">  <span class="keyword">const</span> entries = Collections.Entries.find().fetch();</span>
<span class="line">  onData(<span class="literal">null</span>, &#123;entries&#125;);</span>
<span class="line"> &#125;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> composeAll(</span>
<span class="line"> composeWithTracker(composer),</span>
<span class="line"> useDeps()</span>
<span class="line">)(EntryList);</span>
</pre></td></tr></table></figure>
<p>这也确实与其他容器组件较为相似，但一个重要的区别在于，我们会检查我们的入口集合条目结合，并将它们分配给一个变量。最终我们通过 onData 函数将这个变量传给 UI 组件。</p>
<p>让我们在 publications 目录下的 entries.js 文件中设置发布</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;Entries&#125; <span class="keyword">from</span> <span class="string">'/lib/collections'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;Meteor&#125; <span class="keyword">from</span> <span class="string">'meteor/meteor'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;check&#125; <span class="keyword">from</span> <span class="string">'meteor/check'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line"> Meteor.publish(<span class="string">'entries.list'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line">  <span class="keyword">const</span> selector = &#123;&#125;;</span>
<span class="line">  <span class="keyword">const</span> options = &#123;</span>
<span class="line">   <span class="attr">fields</span>: &#123;<span class="attr">_id</span>: <span class="number">1</span>, <span class="attr">text</span>: <span class="number">1</span>&#125;,</span>
<span class="line">   <span class="attr">sort</span>: &#123;<span class="attr">createdAt</span>: <span class="number">-1</span>&#125;</span>
<span class="line">  &#125;;</span>
<span class="line">  <span class="keyword">return</span> Entries.find(selector, options);</span>
<span class="line"> &#125;);</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>同时我们将要为此发布创建一个 index 文件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> entries <span class="keyword">from</span> <span class="string">'./entries'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span>
<span class="line"> entries();</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p>我们需要在 server 目录中打开 main.js 文件，取消注释行，导入 publications 和 methods ，所以文件就像这样：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> publications <span class="keyword">from</span> <span class="string">'./publications'</span>;</span>
<span class="line"><span class="keyword">import</span> methods <span class="keyword">from</span> <span class="string">'./methods'</span>;</span>
<span class="line"></span>
<span class="line"><span class="comment">// publications();</span></span>
<span class="line"><span class="comment">// methods();</span></span>
</pre></td></tr></table></figure>
<p>最后我们将要为独立的 Entry 组件创建容器组件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Entry <span class="keyword">from</span> <span class="string">'../components/Entry.jsx'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;useDeps, composeWithTracker, composeAll&#125; <span class="keyword">from</span> <span class="string">'mantra-core'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">const</span> composer = <span class="function">(<span class="params">&#123;context, entryId&#125;, onData</span>) =&gt;</span> &#123;</span>
<span class="line"> <span class="keyword">const</span> &#123;Meteor, Collections&#125; = context();</span>
<span class="line"> <span class="keyword">if</span> (Meteor.subscribe(<span class="string">'entries.single'</span>, entryId).ready()) &#123;</span>
<span class="line">  <span class="keyword">const</span> entry = Collections.Entries.findOne(entryId);</span>
<span class="line">  onData(<span class="literal">null</span>, &#123;entry&#125;);</span>
<span class="line"> &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">  <span class="keyword">const</span> entry = Collections.Entries.findOne(entryId);</span>
<span class="line">  <span class="keyword">if</span> (entry) &#123;</span>
<span class="line">   onData(<span class="literal">null</span>, &#123;entry&#125;);</span>
<span class="line">  &#125; <span class="keyword">else</span> &#123;</span>
<span class="line">   onData();</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;</span>
<span class="line">&#125;;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> composeAll(</span>
<span class="line"> composeWithTracker(composer),</span>
<span class="line"> useDeps()</span>
<span class="line">)(Entry);</span>
</pre></td></tr></table></figure>
<p>此容器使用了一个 entryId （将通过我们之后设立的一个路由进行传递）并且找到一个合适的入口，来通过属性传递它给UI组件。</p>
<p>让我们在之前设置的发布列表中快速设置发布来展示发布条目。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
</pre></td><td class="code"><pre><span class="line">Meteor.publish(<span class="string">'entries.single'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">entryId</span>) </span>&#123;</span>
<span class="line"> check(entryId, <span class="built_in">String</span>);</span>
<span class="line"> <span class="keyword">const</span> selector = &#123;<span class="attr">_id</span>: entryId&#125;;</span>
<span class="line"> <span class="keyword">return</span> Entries.find(selector);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p>现在让我们设置我们的路由吧。</p>
<p><strong>路由</strong></p>
<p>打开 routes 文件来添加一些新的路由，修改 routes 文件类似如下所示。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
<span class="line">45</span>
<span class="line">46</span>
<span class="line">47</span>
<span class="line">48</span>
<span class="line">49</span>
<span class="line">50</span>
<span class="line">51</span>
<span class="line">52</span>
<span class="line">53</span>
<span class="line">54</span>
<span class="line">55</span>
<span class="line">56</span>
<span class="line">57</span>
<span class="line">58</span>
<span class="line">59</span>
<span class="line">60</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span>
<span class="line"><span class="keyword">import</span> &#123;mount&#125; <span class="keyword">from</span> <span class="string">'react-mounter'</span>;</span>
<span class="line"><span class="keyword">import</span> Layout <span class="keyword">from</span> <span class="string">'./components/MainLayout.jsx'</span>;</span>
<span class="line"><span class="keyword">import</span> Home <span class="keyword">from</span> <span class="string">'./components/Home.jsx'</span>;</span>
<span class="line"><span class="keyword">import</span> NewUser <span class="keyword">from</span> <span class="string">'../users/containers/NewUser.js'</span>;</span>
<span class="line"><span class="keyword">import</span> Login <span class="keyword">from</span> <span class="string">'../users/containers/Login.js'</span>;</span>
<span class="line"><span class="keyword">import</span> EntryList <span class="keyword">from</span> <span class="string">'../entries/containers/EntryList.js'</span>;</span>
<span class="line"><span class="keyword">import</span> Entry <span class="keyword">from</span> <span class="string">'../entries/containers/Entry.js'</span>;</span>
<span class="line"><span class="keyword">import</span> NewEntry <span class="keyword">from</span> <span class="string">'../entries/containers/NewEntry.js'</span>;</span>
<span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params">injectDeps, &#123;FlowRouter&#125;</span>) </span>&#123;</span>
<span class="line"> <span class="keyword">const</span> MainLayoutCtx = injectDeps(Layout);</span>
<span class="line"> FlowRouter.route(<span class="string">'/'</span>, &#123;</span>
<span class="line">  <span class="attr">name</span>: <span class="string">'items.list'</span>,</span>
<span class="line">  action() &#123;</span>
<span class="line">   mount(MainLayoutCtx, &#123;</span>
<span class="line">    <span class="attr">content</span>: <span class="function"><span class="params">()</span> =&gt;</span> (<span class="xml"><span class="tag">&lt;<span class="name">entrylist</span>&gt;</span>)</span>
<span class="line">   &#125;);</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;);</span>
<span class="line"> FlowRouter.route('/entry/:entryId', &#123;</span>
<span class="line">  name: 'entries.single',</span>
<span class="line">  action(&#123;entryId&#125;) &#123;</span>
<span class="line">   mount(MainLayoutCtx, &#123;</span>
<span class="line">    content: () =&gt; (<span class="tag">&lt;<span class="name">entry</span> <span class="attr">entryid</span>=<span class="string">"&#123;entryId&#125;/"</span>&gt;</span>)</span>
<span class="line">   &#125;);</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;);</span>
<span class="line">FlowRouter.route('/new-entry', &#123;</span>
<span class="line">  name: 'newEntry',</span>
<span class="line">  action() &#123;</span>
<span class="line">   mount(MainLayoutCtx, &#123;</span>
<span class="line">    content: () =&gt; (<span class="tag">&lt;<span class="name">newentry</span>&gt;</span>)</span>
<span class="line">   &#125;);</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;);</span>
<span class="line"></span>
<span class="line"> FlowRouter.route('/register', &#123;</span>
<span class="line">  name: 'users.new',</span>
<span class="line">  action() &#123;</span>
<span class="line">   mount(MainLayoutCtx, &#123;</span>
<span class="line">    content: () =&gt; (<span class="tag">&lt;<span class="name">newuser</span>&gt;</span>)</span>
<span class="line">   &#125;);</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;);</span>
<span class="line">FlowRouter.route('/login', &#123;</span>
<span class="line">  name: 'users.login',</span>
<span class="line">  action() &#123;</span>
<span class="line">   mount(MainLayoutCtx, &#123;</span>
<span class="line">    content: () =&gt; (<span class="tag">&lt;<span class="name">login</span>&gt;</span>)</span>
<span class="line">   &#125;);</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;);</span>
<span class="line">FlowRouter.route('/logout', &#123;</span>
<span class="line">  name: 'users.logout',</span>
<span class="line">  action() &#123;</span>
<span class="line">   Meteor.logout();</span>
<span class="line">   FlowRouter.go('/');</span>
<span class="line">  &#125;</span>
<span class="line"> &#125;); </span>
<span class="line">&#125;<span class="tag">&lt;/<span class="name">login</span>&gt;</span><span class="tag">&lt;/<span class="name">newuser</span>&gt;</span><span class="tag">&lt;/<span class="name">newentry</span>&gt;</span><span class="tag">&lt;/<span class="name">entry</span>&gt;</span><span class="tag">&lt;/<span class="name">entrylist</span>&gt;</span></span></span>
</pre></td></tr></table></figure>
<p>在运行我们的应用之前我们还需要做最后一件事，打开 main.js 文件并导入我们的 entries 模块，修改内容如下。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;createApp&#125; <span class="keyword">from</span> <span class="string">'mantra-core'</span>;</span>
<span class="line"><span class="keyword">import</span> initContext <span class="keyword">from</span> <span class="string">'./configs/context'</span>;</span>
<span class="line"><span class="comment">// modules</span></span>
<span class="line"><span class="keyword">import</span> coreModule <span class="keyword">from</span> <span class="string">'./modules/core'</span>;</span>
<span class="line"><span class="keyword">import</span> usersModule <span class="keyword">from</span> <span class="string">'./modules/users'</span>;</span>
<span class="line"><span class="keyword">import</span> entriesModule <span class="keyword">from</span> <span class="string">'./modules/entries'</span>;</span>
<span class="line"><span class="comment">// init context</span></span>
<span class="line"><span class="keyword">const</span> context = initContext();</span>
<span class="line"><span class="comment">// create app</span></span>
<span class="line"><span class="keyword">const</span> app = createApp(context);</span>
<span class="line">app.loadModule(coreModule);</span>
<span class="line">app.loadModule(usersModule);</span>
<span class="line">app.loadModule(entriesModule);</span>
<span class="line">app.init();</span>
</pre></td></tr></table></figure>
<h4 id="zui-hou-yun-xing-cheng-gong">最后 — 运行成功</h4>
<p>现在我们设置了我们的所有路由并且应用已经准备好运行，让我们切换目录到根目录并运行</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span>
</pre></td><td class="code"><pre><span class="line">meteor</span>
</pre></td></tr></table></figure>
<p>你可以看到应用程序在 Mantra 提供的默认加载效果中启动，让我们添加一个条目，这样我们应该可以在屏幕上看到效果了。</p>
<p>访问 <code>localhost:3000/new-entry</code> ，填写并提交表单来添加一个条目。</p>
<p>然后访问根目录，你应该可以看到一个可以逐个查看链接的的条目列表。</p>
<p>希望这个简单的 Mantra 引导以及目前的 Meteor 1.3 beta 版本有助于让你更加了解如何运用它们来构建一个应用。</p>
<p><img src="http://ww3.sinaimg.cn/large/a490147fjw1f391r94o1nj20go0lgq5b.jpg" alt=""></p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文链接 : &lt;a href=&quot;https://medium.com/@kenrogers/build-a-journaling-app-with-meteor-1-3-beta-react-react-bootstrap-and-mantra-7
    
    </summary>
    
      <category term="Meteor" scheme="http://qcyoung.com/categories/Meteor/"/>
    
    
      <category term="原创翻译" scheme="http://qcyoung.com/tags/%E5%8E%9F%E5%88%9B%E7%BF%BB%E8%AF%91/"/>
    
      <category term="Meteor" scheme="http://qcyoung.com/tags/Meteor/"/>
    
      <category term="React" scheme="http://qcyoung.com/tags/React/"/>
    
  </entry>
  
  <entry>
    <title>前端动画原理与实现</title>
    <link href="http://qcyoung.com/2016/04/10/%E5%89%8D%E7%AB%AF%E5%8A%A8%E7%94%BB%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/"/>
    <id>http://qcyoung.com/2016/04/10/前端动画原理与实现/</id>
    <published>2016-04-10T02:55:11.000Z</published>
    <updated>2016-10-24T05:36:02.000Z</updated>
    
    <content type="html"><![CDATA[<p>参考月影大大同名<a href="http://matrix.h5jun.com/slide/show?id=117#/" target="_blank" rel="external">分享内容</a>总结了此份博文。经过一番归纳整理后熟悉了很多，整理于此，以供参考。</p>
<h2 id="dong-hua-de-ben-zhi-dong-hua-shi-guan-yu-shi-jian-de-han-shu">动画的本质（动画是关于时间的函数）</h2>
<ul>
<li>定时器 改变元素的属性</li>
<li>浏览器/GPU 的渲染过程</li>
</ul>
<h2 id="dong-hua-de-chong-lei">动画的种类</h2>
<ul>
<li>JavaScript 动画</li>
</ul>
<ol>
<li>DOM 动画</li>
<li>Canvas 动画</li>
</ol>
<ul>
<li>CSS3 动画</li>
</ul>
<ol>
<li>transition</li>
<li>animation</li>
</ol>
<ul>
<li>SVG 动画</li>
</ul>
<h2 id="dong-hua-de-kong-zhi">动画的控制</h2>
<h3 id="li-1-1-jian-dan-dong-hua">例 1-1 简单动画</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> deg = <span class="number">0</span>;</span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>;</span>
<span class="line">  setInterval(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    self.style.transform = <span class="string">'rotate('</span> + (deg++) +<span class="string">'deg)'</span>;</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/WwpMGm/" target="_blank" rel="external">demo</a></p>
<p>弊端：动画应该用当前时间和开始时间的差值来计算当前动画元素的位置而不是像上例那样用属性增量来实现动画，因为每隔多少毫秒增加一点属性的话，浏览器timer并不能保证在那个准确的时间点执行，而且那样做也很难精确控制动画的各个物理量。（造成丢帧）</p>
<p>建议使用 <code>requestAnimationFrame</code>
定义绘制每一帧前的工作。 <code>requestAnimationFrame(callback)</code>
方法可以自动调节频率。callback 工作太多时无法在一帧内完成，会自动降低为 30 FPS, 虽然频率会降低但比丢帧好。
渲染一帧目标（ 1 / 60 FPS = 16 ms-)</p>
<p>具体表格体现如下:</p>
<table>
<thead>
<tr>
<th></th>
<th style="text-align:center">时间</th>
<th style="text-align:center">增量</th>
</tr>
</thead>
<tbody>
<tr>
<td>幅度控制</td>
<td style="text-align:center">✓</td>
<td style="text-align:center">✓</td>
</tr>
<tr>
<td>时间控制</td>
<td style="text-align:center">✓</td>
<td style="text-align:center">×</td>
</tr>
<tr>
<td>速度控制</td>
<td style="text-align:center">✓</td>
<td style="text-align:center">✓</td>
</tr>
<tr>
<td>不会延迟</td>
<td style="text-align:center">✓</td>
<td style="text-align:center">×</td>
</tr>
<tr>
<td>不会掉帧</td>
<td style="text-align:center">×</td>
<td style="text-align:center">✓</td>
</tr>
</tbody>
</table>
<h3 id="li-1-2-gai-jin-jian-dan-dong-hua">例 1-2 改进简单动画</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now();</span>
<span class="line">  setInterval(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> T = <span class="number">1000</span>;</span>
<span class="line">    <span class="keyword">var</span> p = (<span class="built_in">Date</span>.now() - startTime) / T;</span>
<span class="line">    self.style.transform = <span class="string">'rotate('</span> + (<span class="number">360</span> * p) +<span class="string">'deg)'</span>;</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/zqZXLV" target="_blank" rel="external">demo</a></p>
<h2 id="zhi-xian-dong-hua">直线动画</h2>
<h3 id="li-2-1-yun-su-yun-dong">例 2-1 匀速运动</h3>
<p>问：让滑块在 2 秒内向右匀速移动 200px</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      distance = <span class="number">200</span>, T = <span class="number">2000</span>;</span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    self.style.transform = <span class="string">'translateX('</span> + (distance * p) +<span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/eZvVeJ" target="_blank" rel="external">demo</a></p>
<p>基本公式：</p>
<p>时间：$t = T⋅p$</p>
<p>位移：$S_t = S⋅p = v⋅t$</p>
<p>速度：$v = \frac{S⋅p}{t} = \frac{S}{T}$</p>
<p>加速度：$a = 0$</p>
<h3 id="li-2-2-yun-jia-su-yun-dong">例 2-2 匀加速运动</h3>
<p>问：让滑块在 2 秒内向右匀加速移动 200px，速度从 0 开始</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      distance = <span class="number">200</span>, T = <span class="number">2000</span>;</span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    self.style.transform = <span class="string">'translateX('</span> + (distance * p * p) +<span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/xVqNyr" target="_blank" rel="external">demo</a></p>
<p>时间：$t = T⋅p$</p>
<p>位移：$S_t = S⋅p^2 = t^2 ⋅\frac{S}{T^2}$</p>
<p>速度：$v = \frac{2S}{T^2}⋅t = \frac{2Sp}{T}$</p>
<p>加速度：$a = \frac{2S}{T^2}$</p>
<h3 id="li-2-3-yun-jian-su-yun-dong">例 2-3 匀减速运动</h3>
<p>让滑块在 2 秒内向右匀减速移动 200px，速度从最大减为 0</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      distance = <span class="number">200</span>, T = <span class="number">2000</span>;</span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    self.style.transform = <span class="string">'translateX('</span> </span>
<span class="line">      + (distance * p * (<span class="number">2</span>-p)) +<span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/JXWqVr" target="_blank" rel="external">demo</a></p>
<p>时间：$t = T⋅p$</p>
<p>位移：$S_t = \frac{2S}{T}⋅t - t^2 ⋅\frac{S}{T^2} = Sp(2-p)$</p>
<p>速度：$v = \frac{2S(1-p)}{T} = \frac{2S}{T} - \frac{2S}{T^2}⋅t$</p>
<p>加速度：$a = - \frac{2S}{T^2}$</p>
<h2 id="ping-mian-shang-de-yun-dong">平面上的运动</h2>
<h3 id="li-3-1-xie-xian-yun-dong">例3-1 斜线运动</h3>
<p>让滑块沿斜线运动 2s，x、y 移动距离都是 200px</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      distance = <span class="number">200</span>, T = <span class="number">2000</span>;</span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    <span class="keyword">var</span> tx = distance * p;</span>
<span class="line">    <span class="keyword">var</span> ty = tx;</span>
<span class="line"></span>
<span class="line">    self.style.transform = <span class="string">'translate('</span> </span>
<span class="line">      + tx + <span class="string">'px'</span> + <span class="string">','</span> + ty +<span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/XdRWrM" target="_blank" rel="external">demo</a>
（就是 x 轴 y 轴各自方向的匀速运动）</p>
<h3 id="li-3-2-pao-wu-xian-yun-dong">例3-2 抛物线运动</h3>
<p>让滑块做抛物线运动（平抛）</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      disX = <span class="number">200</span>, disY = <span class="number">200</span>, </span>
<span class="line">      T = <span class="number">1000</span> * <span class="built_in">Math</span>.sqrt(<span class="number">2</span> * disY / <span class="number">98</span>); </span>
<span class="line">    <span class="comment">//假设10px是1米，disY = 20米</span></span>
<span class="line"></span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    <span class="keyword">var</span> tx = disX * p;</span>
<span class="line">    <span class="keyword">var</span> ty = disY * p * p;</span>
<span class="line"></span>
<span class="line">    self.style.transform = <span class="string">'translate('</span> </span>
<span class="line">      + tx + <span class="string">'px'</span> + <span class="string">','</span> + ty +<span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/jqmOPb" target="_blank" rel="external">demo</a>
（$y = ax^2 + bx + c$）</p>
<h3 id="li-3-3-jian-xie-zhen-dong">例3-3 简谐振动</h3>
<p>让滑块做简谐摆运动（简谐振动）</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      distance = <span class="number">100</span>, </span>
<span class="line">      T = <span class="number">2000</span>; </span>
<span class="line"></span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    <span class="keyword">var</span> tx = distance * <span class="built_in">Math</span>.sin(<span class="number">2</span> * <span class="built_in">Math</span>.PI * p);</span>
<span class="line"></span>
<span class="line">    self.style.transform = <span class="string">'translateX('</span> </span>
<span class="line">      + tx + <span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/grWOWN" target="_blank" rel="external">demo</a>
（正弦曲线）</p>
<h3 id="li-3-4-zheng-xian-qu-xian">例3-4 正弦曲线</h3>
<p>让滑块沿正弦曲线运动</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      distance = <span class="number">100</span>, </span>
<span class="line">      T = <span class="number">2000</span>; </span>
<span class="line"></span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    <span class="keyword">var</span> ty = distance * <span class="built_in">Math</span>.sin(<span class="number">2</span> * <span class="built_in">Math</span>.PI * p);</span>
<span class="line">    <span class="keyword">var</span> tx = <span class="number">2</span> * distance * p;</span>
<span class="line"></span>
<span class="line">    self.style.transform = <span class="string">'translate('</span> </span>
<span class="line">      + tx + <span class="string">'px,'</span> + ty + <span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/EKoopm" target="_blank" rel="external">demo</a></p>
<h3 id="li-3-5-yuan-zhou-yun-dong">例3-5 圆周运动</h3>
<p>让滑块做圆周运动</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      r = <span class="number">100</span>, T = <span class="number">2000</span>; </span>
<span class="line"></span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    <span class="keyword">var</span> rotation = <span class="number">360</span> * p;</span>
<span class="line"></span>
<span class="line">    self.style.transformOrigin = <span class="string">'0 '</span> + r + <span class="string">'px'</span>;</span>
<span class="line">    self.style.transform = <span class="string">'rotate('</span> </span>
<span class="line">      + rotation + <span class="string">'deg)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/WwjbrE" target="_blank" rel="external">demo</a></p>
<h3 id="li-3-6-yuan-zhou-yun-dong">例3-6 圆周运动</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> self = <span class="keyword">this</span>, startTime = <span class="built_in">Date</span>.now(),</span>
<span class="line">      r = <span class="number">100</span>, T = <span class="number">2000</span>; </span>
<span class="line"></span>
<span class="line">  requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> p = <span class="built_in">Math</span>.min(<span class="number">1.0</span>, (<span class="built_in">Date</span>.now() - startTime) / T);</span>
<span class="line">    <span class="keyword">var</span> tx = -r * <span class="built_in">Math</span>.sin(<span class="number">2</span> * <span class="built_in">Math</span>.PI * p),</span>
<span class="line">        ty = -r * <span class="built_in">Math</span>.cos(<span class="number">2</span> * <span class="built_in">Math</span>.PI * p);</span>
<span class="line"></span>
<span class="line">    self.style.transform = <span class="string">'translate('</span> </span>
<span class="line">      + tx + <span class="string">'px,'</span> + ty + <span class="string">'px)'</span>;</span>
<span class="line">    <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>) requestAnimationFrame(step);</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/ONmPgp" target="_blank" rel="external">demo</a></p>
<p>圆的轨迹方程</p>
<ul>
<li>代数方程： $x^2 + y^2 = r^2$</li>
<li>参数方程（更优）：$x = R⋅cos(ωt)$  $y = R⋅sin(ωt)$</li>
<li>极坐标方程（更优）：$ρ = R$</li>
</ul>
<h3 id="dong-hua-de-yao-su-fen-xi">动画的要素分析</h3>
<p>动画时长：$T$</p>
<p>动画进程：$p = \frac{t}{T}(p ∈ [0,1])$</p>
<p>easing：$e = f(p)$</p>
<p>动画方程：$[x,y] = G(e)$</p>
<p>动画开始、进行中、结束：onStart、onProgress、onFinished</p>
<h3 id="dong-hua-de-jian-yi-feng-zhuang">动画的简易封装</h3>
<p>对动画进行简易封装</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
</pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span>
<span class="line"> * [Animator description]</span>
<span class="line"> * @param &#123;[type]&#125; duration [时间]</span>
<span class="line"> * @param &#123;[type]&#125; progress [运动公式]</span>
<span class="line"> * @param &#123;[type]&#125; easing   [缓动函数]</span>
<span class="line"> */</span></span>
<span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Animator</span>(<span class="params">duration, progress, easing</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">this</span>.duration = duration;</span>
<span class="line">  <span class="keyword">this</span>.progress = progress;</span>
<span class="line">  <span class="keyword">this</span>.easing = easing || <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;<span class="keyword">return</span> p&#125;;</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">Animator.prototype = &#123;</span>
<span class="line">  <span class="attr">start</span>: <span class="function"><span class="keyword">function</span>(<span class="params">finished</span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> startTime = <span class="built_in">Date</span>.now();</span>
<span class="line">    <span class="keyword">var</span> duration = <span class="keyword">this</span>.duration, </span>
<span class="line">        self = <span class="keyword">this</span>;</span>
<span class="line"></span>
<span class="line">    requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">      <span class="keyword">var</span> p = (<span class="built_in">Date</span>.now() - startTime) / duration;<span class="comment">//当前时间</span></span>
<span class="line">      <span class="keyword">var</span> next = <span class="literal">true</span>;</span>
<span class="line"></span>
<span class="line">      <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>)&#123;</span>
<span class="line">        self.progress(self.easing(p), p);<span class="comment">//传p执行动画</span></span>
<span class="line">      &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">        <span class="keyword">if</span>(<span class="keyword">typeof</span> finished === <span class="string">'function'</span>)&#123; <span class="comment">//结束判断</span></span>
<span class="line">          next = finished() === <span class="literal">false</span>;</span>
<span class="line">        &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">          next = finished === <span class="literal">false</span>;</span>
<span class="line">        &#125;</span>
<span class="line"></span>
<span class="line">        <span class="keyword">if</span>(!next)&#123;</span>
<span class="line">          self.progress(self.easing(<span class="number">1.0</span>), <span class="number">1.0</span>);<span class="comment">//动画中断</span></span>
<span class="line">        &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">          startTime += duration;<span class="comment">//循环时间</span></span>
<span class="line">          self.progress(self.easing(p), p);</span>
<span class="line">        &#125;</span>
<span class="line">      &#125;</span>
<span class="line">    </span>
<span class="line">      <span class="keyword">if</span>(next) requestAnimationFrame(step);<span class="comment">//反复执行</span></span>
<span class="line">    &#125;);</span>
<span class="line">  &#125;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<h3 id="li-4-1-chi-xu-yuan-zhou-yun-dong">例4-1 持续圆周运动</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> animator = <span class="keyword">new</span> Animator(<span class="number">2000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> tx = <span class="number">-100</span> * <span class="built_in">Math</span>.sin(<span class="number">2</span> * <span class="built_in">Math</span>.PI * p),</span>
<span class="line">        ty = <span class="number">-100</span> * <span class="built_in">Math</span>.cos(<span class="number">2</span> * <span class="built_in">Math</span>.PI * p);</span>
<span class="line"></span>
<span class="line">    block.style.transform = <span class="string">'translate('</span> </span>
<span class="line">      + tx + <span class="string">'px,'</span> + ty + <span class="string">'px)'</span>;     </span>
<span class="line">  &#125;);</span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  animator.start(<span class="literal">false</span>);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/grWxNx" target="_blank" rel="external">demo</a></p>
<h3 id="li-4-2-zhe-xian-yun-dong">例4-2 折线运动</h3>
<p>让滑块先向右然后再向下运动</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> tx = <span class="number">100</span> * p;</span>
<span class="line"></span>
<span class="line">    block.style.transform = <span class="string">'translateX('</span> </span>
<span class="line">      + tx + <span class="string">'px)'</span>;     </span>
<span class="line">  &#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a2 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> ty = <span class="number">100</span> * p;</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translate(100px,'</span> </span>
<span class="line">    + ty + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  a1.start(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    a2.start();</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/oxwEXj" target="_blank" rel="external">demo</a>这里有两个连续的动画，所以我们最好对动画做一个队列的封装。</p>
<h3 id="dong-hua-dui-lie-feng-zhuang">动画队列封装</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">AnimationQueue</span>(<span class="params">animators</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">this</span>.animators = animators || [];</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">AnimationQueue.prototype = &#123;</span>
<span class="line">  <span class="attr">status</span>: <span class="string">'ready'</span>,</span>
<span class="line">  <span class="attr">append</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> args = [].slice.call(<span class="built_in">arguments</span>);</span>
<span class="line">    <span class="keyword">this</span>.animators.push.apply(<span class="keyword">this</span>.animators, args);</span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">flush</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">if</span>(<span class="keyword">this</span>.animators.length)&#123;</span>
<span class="line">      <span class="keyword">var</span> self = <span class="keyword">this</span>;</span>
<span class="line"></span>
<span class="line">      <span class="function"><span class="keyword">function</span> <span class="title">play</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">        <span class="keyword">var</span> animator = self.animators.shift();</span>
<span class="line">        animator.start(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">          <span class="keyword">if</span>(self.animators.length)&#123;</span>
<span class="line">            play();</span>
<span class="line">          &#125;</span>
<span class="line">        &#125;);</span>
<span class="line">      &#125;</span>
<span class="line">      play();</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<h3 id="li-4-3-ju-xing-xian">例4-3 矩形线</h3>
<p>让滑块沿一个矩形边界运动</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> tx = <span class="number">100</span> * p;</span>
<span class="line">  block.style.transform = <span class="string">'translateX('</span> </span>
<span class="line">    + tx + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a2 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> ty = <span class="number">100</span> * p;</span>
<span class="line">  block.style.transform = <span class="string">'translate(100px,'</span> </span>
<span class="line">    + ty + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a3 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> tx = <span class="number">100</span> * (<span class="number">1</span>-p);</span>
<span class="line">  block.style.transform = <span class="string">'translate('</span> </span>
<span class="line">    + tx + <span class="string">'px, 100px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a4 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> ty = <span class="number">100</span> * (<span class="number">1</span>-p);</span>
<span class="line">  block.style.transform = <span class="string">'translateY('</span>  </span>
<span class="line">    + ty + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line"><span class="keyword">var</span> animators = <span class="keyword">new</span> AnimationQueue();</span>
<span class="line">  animators.append(a1, a2, a3, a4);</span>
<span class="line">  animators.flush();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/XdgZpL" target="_blank" rel="external">demo</a></p>
<h3 id="gai-liang-dong-hua-dui-lie">改良动画队列</h3>
<p>添加了 animator 是否为 Animator 的实例判断</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
</pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">AnimationQueue</span>(<span class="params">animators</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">this</span>.animators = animators || [];</span>
<span class="line">&#125;</span>
<span class="line"></span>
<span class="line">AnimationQueue.prototype = &#123;</span>
<span class="line">  <span class="attr">status</span>: <span class="string">'ready'</span>,</span>
<span class="line">  <span class="attr">append</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> args = [].slice.call(<span class="built_in">arguments</span>);</span>
<span class="line">    <span class="keyword">this</span>.animators.push.apply(<span class="keyword">this</span>.animators, args);</span>
<span class="line">  &#125;,</span>
<span class="line">  <span class="attr">flush</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">if</span>(<span class="keyword">this</span>.animators.length)&#123;</span>
<span class="line">      <span class="keyword">var</span> self = <span class="keyword">this</span>;</span>
<span class="line"></span>
<span class="line">      <span class="function"><span class="keyword">function</span> <span class="title">play</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">        <span class="keyword">var</span> animator = self.animators.shift();</span>
<span class="line"></span>
<span class="line">        <span class="keyword">if</span>(animator <span class="keyword">instanceof</span> Animator)&#123;</span>
<span class="line">          animator.start(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">            <span class="keyword">if</span>(self.animators.length)&#123;</span>
<span class="line">              play();</span>
<span class="line">            &#125;</span>
<span class="line">          &#125;);</span>
<span class="line">        &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">          animator.apply(self);</span>
<span class="line">          <span class="keyword">if</span>(self.animators.length)&#123;</span>
<span class="line">            play();</span>
<span class="line">          &#125;</span>
<span class="line">        &#125;</span>
<span class="line">      &#125;</span>
<span class="line">      play();</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">&#125;;</span>
</pre></td></tr></table></figure>
<h3 id="li-4-4-xun-huan-zhe-xian-dong-hua">例4-4 循环折线动画</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> tx = <span class="number">100</span> * p;</span>
<span class="line">  block.style.transform = <span class="string">'translateX('</span> </span>
<span class="line">    + tx + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a2 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> ty = <span class="number">100</span> * p;</span>
<span class="line">  block.style.transform = <span class="string">'translate(100px,'</span> </span>
<span class="line">    + ty + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a3 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> tx = <span class="number">100</span> * (<span class="number">1</span>-p);</span>
<span class="line">  block.style.transform = <span class="string">'translate('</span> </span>
<span class="line">    + tx + <span class="string">'px, 100px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a4 = <span class="keyword">new</span> Animator(<span class="number">1000</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> ty = <span class="number">100</span> * (<span class="number">1</span>-p);</span>
<span class="line">  block.style.transform = <span class="string">'translateY('</span>  </span>
<span class="line">    + ty + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> animators = <span class="keyword">new</span> AnimationQueue();</span>
<span class="line">  animators.append(a1, a2, a3, a4, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">this</span>.append(a1, a2, a3, a4, <span class="built_in">arguments</span>.callee);</span>
<span class="line">  &#125;);</span>
<span class="line">  animators.flush();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/RaxRQp" target="_blank" rel="external">demo</a></p>
<h2 id="jing-dian-xiao-qiu-yun-dong">经典小球运动</h2>
<h3 id="li-5-1-dan-tiao-de-xiao-qiu-zi-you-luo-ti-dan-qi-xun-huan">例5-1 弹跳的小球（自由落体、弹起、循环）</h3>
<p>小球方程
设 20px 为 1 米，下落距离为 200px（10 米）</p>
<p>$g \approx 10米/秒$</p>
<p>$S = \frac{1}{2}gT^2 = 10米$</p>
<p>$T = \sqrt\frac{2S}{g} = \sqrt2\approx1.414秒 = 1414毫秒$</p>
<p>下落阶段：$S_t = Sp^2$</p>
<p>上升阶段：$S_t = S - S_p(2 - p)$</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">1414</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> ty = <span class="number">200</span> * p * p;</span>
<span class="line">  block.style.transform = <span class="string">'translateY('</span> </span>
<span class="line">    + ty + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a2 = <span class="keyword">new</span> Animator(<span class="number">1414</span>,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> ty = <span class="number">200</span> - <span class="number">200</span> * p * (<span class="number">2</span>-p);</span>
<span class="line">  block.style.transform = <span class="string">'translateY('</span> </span>
<span class="line">    + ty + <span class="string">'px)'</span>;     </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> animators = <span class="keyword">new</span> AnimationQueue();</span>
<span class="line">  animators.append(a1,a2, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    <span class="keyword">this</span>.append(a1, a2, <span class="built_in">arguments</span>.callee);</span>
<span class="line">  &#125;);</span>
<span class="line">  animators.flush();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/BKJrZd" target="_blank" rel="external">demo</a></p>
<h3 id="li-5-2-dan-tiao-de-xiao-qiu-neng-liang-sun-hao">例5-2 弹跳的小球（能量损耗）</h3>
<p>设每一个周期损耗为0.7：$T = 0.7T$
`
上升距离：$S = 0.49S$</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
</pre></td><td class="code"><pre><span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> T = <span class="number">1414</span>;</span>
<span class="line"></span>
<span class="line">  <span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(T,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> s = <span class="keyword">this</span>.duration * <span class="number">200</span> / T;</span>
<span class="line">    <span class="keyword">var</span> ty = s * (p * p - <span class="number">1</span>);</span>
<span class="line">    block.style.transform = <span class="string">'translateY('</span> </span>
<span class="line">      + ty + <span class="string">'px)'</span>;     </span>
<span class="line">  &#125;);</span>
<span class="line"></span>
<span class="line">  <span class="keyword">var</span> a2 = <span class="keyword">new</span> Animator(T,  <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">    <span class="keyword">var</span> s = <span class="keyword">this</span>.duration * <span class="number">200</span> / T;</span>
<span class="line">    <span class="keyword">var</span> ty = - s * p * (<span class="number">2</span>-p);</span>
<span class="line">    block.style.transform = <span class="string">'translateY('</span> </span>
<span class="line">      + ty + <span class="string">'px)'</span>;     </span>
<span class="line">  &#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> animators = <span class="keyword">new</span> AnimationQueue();</span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    a2.duration *= <span class="number">0.7</span>;</span>
<span class="line">    <span class="keyword">if</span>(a2.duration &lt;= <span class="number">0.0001</span>)&#123;</span>
<span class="line">      animators.animators.length = <span class="number">0</span>;</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;</span>
<span class="line">  animators.append(a1 ,foo, a2,</span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    a1.duration *= <span class="number">0.7</span>;</span>
<span class="line">    <span class="keyword">this</span>.append(a1, foo, a2, b);</span>
<span class="line">  &#125;);</span>
<span class="line">  animators.flush();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/jqYzZp" target="_blank" rel="external">demo</a></p>
<h3 id="li-5-3-gun-dong-de-xiao-qiu">例5-3 滚动的小球</h3>
<p>小球直径：$d = 50px$</p>
<p>圆周长：$l = πd$</p>
<p>周期：$T = 2秒$</p>
<p>滚动时间：$t_{max} = 4秒$</p>
<p>滚动距离等于：$S = πd·\frac{t_{max}}{T} = 314px$</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">4000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> rotation = <span class="string">'rotate('</span> + <span class="number">720</span> * p + <span class="string">'deg)'</span>;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">50</span> + <span class="number">314</span> * p + <span class="string">'px'</span>;</span>
<span class="line"></span>
<span class="line">  block.style.transform = rotation;</span>
<span class="line">  block.style.left = x;</span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  a1.start();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/GZydrd" target="_blank" rel="external">demo</a></p>
<h3 id="li-5-4-shuai-chu-xiao-qiu-guan-xing">例5-4 甩出小球（惯性）</h3>
<p>1）小球做半径 100px 的匀速圆周运动，周期 2s
2）在 2.8s 后小球从手中甩出</p>
<p>甩出小球公式：</p>
<p>$x = − rsin(πt)$</p>
<p>$v_x = − πrcos(πt)$</p>
<p>$y = r − rcos(πt)$</p>
<p>$v_y = πrsin(πt)$</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">2800</span>, <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">-100</span> * <span class="built_in">Math</span>.sin(<span class="number">2.8</span> * <span class="built_in">Math</span>.PI * p);</span>
<span class="line">  <span class="keyword">var</span> y = <span class="number">100</span> - <span class="number">100</span> * <span class="built_in">Math</span>.cos(<span class="number">2.8</span> * <span class="built_in">Math</span>.PI * p);</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translate('</span> + x + <span class="string">'px,'</span></span>
<span class="line">    + y + <span class="string">'px)'</span>;</span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a2 = <span class="keyword">new</span> Animator(<span class="number">5000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">-100</span> * <span class="built_in">Math</span>.sin(<span class="number">2.8</span> * <span class="built_in">Math</span>.PI) </span>
<span class="line">      <span class="number">-100</span> * <span class="built_in">Math</span>.cos(<span class="number">2.8</span> * <span class="built_in">Math</span>.PI) * <span class="built_in">Math</span>.PI * <span class="number">5</span> * p;</span>
<span class="line"></span>
<span class="line">  <span class="keyword">var</span> y = <span class="number">100</span> - <span class="number">100</span> * <span class="built_in">Math</span>.cos(<span class="number">2.8</span> * <span class="built_in">Math</span>.PI) </span>
<span class="line">      + <span class="number">100</span> * <span class="built_in">Math</span>.sin(<span class="number">2.8</span> * <span class="built_in">Math</span>.PI) * <span class="built_in">Math</span>.PI * <span class="number">5</span> * p;</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translate('</span> + x + <span class="string">'px,'</span></span>
<span class="line">    + y + <span class="string">'px)'</span>;    </span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">a1.start(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    a2.start();</span>
<span class="line">  &#125;);</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/ONzZzr" target="_blank" rel="external">demo</a></p>
<h3 id="li-5-5-yun-jia-su-dao-yun-su-dao-jian-su-yun-dong">例5-5 匀加速到匀速到减速运动</h3>
<p>滑块 1s 匀加速运动 100px，匀速运动 100px，
然后再经过 50px 速度减为 0。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">1000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">100</span> * p * p;</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translateX('</span> + x + <span class="string">'px)'</span>;</span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a2 = <span class="keyword">new</span> Animator(<span class="number">500</span>, <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">100</span> + <span class="number">100</span> * p;</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translateX('</span> + x + <span class="string">'px)'</span>;</span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a3 = <span class="keyword">new</span> Animator(<span class="number">500</span>, <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">200</span> + <span class="number">50</span> * p * (<span class="number">2</span> - p);</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translateX('</span> + x + <span class="string">'px)'</span>;</span>
<span class="line">&#125;);</span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line"><span class="keyword">var</span> animators = <span class="keyword">new</span> AnimationQueue();</span>
<span class="line">  animators.append(a1, a2, a3);</span>
<span class="line">  animators.flush();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/YqYLea" target="_blank" rel="external">demo</a></p>
<p>此类型运动运动过程均可以用熟悉的速度-时间图表示</p>
<iframe src="/project/method-draw-image.svg" width="100%" height="400px" id="framedemo" frameborder="0" scrolling="no"></iframe>
<h2 id="bei-sai-er-qu-xian">贝塞尔曲线</h2>
<p>我们可以使用二阶贝塞尔曲线来构造平滑动画，这里如果对贝塞尔曲线还不太了解，可以看看下面这篇详解
<a href="http://www.html-js.com/article/1628" target="_blank" rel="external">贝塞尔曲线扫盲</a></p>
<p><img src="http://htmljs.b0.upaiyun.com/uploads/1415845715278-bezier-quadratic-animation.gif" alt="贝塞尔曲线模拟"></p>
<p>这里有一些贝塞尔曲线的实现资源库和文档：</p>
<p><a href="https://github.com/gre/bezier-easing" target="_blank" rel="external">bezier-easing</a></p>
<p><a href="http://cubic-bezier.com/" target="_blank" rel="external">cubic-bezier</a></p>
<p><a href="http://easings.net/zh-cn" target="_blank" rel="external">缓动函数速查表</a></p>
<p>借助资源库我们可以方便实现简单的贝塞尔曲线动画</p>
<h3 id="li-6-1-ease-in-out-quint-dong-hua">例6-1 easeInOutQuint动画</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> easing = BezierEasing(<span class="number">0.86</span>, <span class="number">0</span>, <span class="number">0.07</span>, <span class="number">1</span>);</span>
<span class="line"><span class="comment">//easeInOutQuint</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">2000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">ep,p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">200</span> * ep;</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translateX('</span> + x + <span class="string">'px)'</span>;</span>
<span class="line">&#125;, easing);</span>
<span class="line"></span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  a1.start();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/eZyaNv" target="_blank" rel="external">demo</a></p>
<h3 id="li-6-2-ease-in-out-back-dong-hua">例6-2 easeInOutBack动画</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> easing = BezierEasing(<span class="number">0.68</span>, <span class="number">-0.55</span>, <span class="number">0.265</span>, <span class="number">1.55</span>);</span>
<span class="line"><span class="comment">//easeInOutQuint</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">2000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">ep,p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">200</span> * ep;</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translateX('</span> + x + <span class="string">'px)'</span>;</span>
<span class="line">&#125;, easing);</span>
<span class="line"></span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  a1.start();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/repgLw" target="_blank" rel="external">demo</a></p>
<h3 id="li-6-3-ease-in-out-back-xy-dong-hua">例6-3 easeInOutBackXY动画</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> easing = BezierEasing(<span class="number">0.68</span>, <span class="number">-0.55</span>, <span class="number">0.265</span>, <span class="number">1.55</span>);</span>
<span class="line"><span class="comment">//easeInOutQuint</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">2000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">ep,p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">200</span> * ep;</span>
<span class="line">  <span class="keyword">var</span> y = <span class="number">-200</span> * p; </span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translate('</span> + x + <span class="string">'px,'</span> + y + <span class="string">'px)'</span>;</span>
<span class="line">&#125;, easing);</span>
<span class="line"></span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  a1.start();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/vGpwXb" target="_blank" rel="external">demo</a></p>
<h3 id="li-6-4-3-dease-in-out-back-xy-dong-hua">例6-4 3DeaseInOutBackXY动画</h3>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> easing = BezierEasing(<span class="number">0.68</span>, <span class="number">-0.55</span>, <span class="number">0.265</span>, <span class="number">1.55</span>);</span>
<span class="line"><span class="comment">//easeInOutQuint</span></span>
<span class="line"></span>
<span class="line"><span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">2000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">ep,p</span>)</span>&#123;</span>
<span class="line">  <span class="keyword">var</span> y = <span class="number">-200</span> * ep;</span>
<span class="line">  <span class="keyword">var</span> x = <span class="number">200</span> * p; </span>
<span class="line">  <span class="keyword">var</span> r = <span class="number">360</span> * ep;</span>
<span class="line"></span>
<span class="line">  block.style.transform = <span class="string">'translate('</span> + x + <span class="string">'px,'</span> </span>
<span class="line">    + y + <span class="string">'px) rotateY('</span> + r + <span class="string">'deg)'</span>;</span>
<span class="line">&#125;, easing);</span>
<span class="line"></span>
<span class="line"></span>
<span class="line">block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  a1.start();</span>
<span class="line">&#125;);</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/KzZLmN" target="_blank" rel="external">demo</a></p>
<h2 id="zhu-zheng-dong-hua">逐帧动画</h2>
<h3 id="li-7-1-flappy-bird">例7-1 FlappyBird</h3>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
</pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">type</span>=<span class="string">"text/css"</span>&gt;</span><span class="css"></span>
<span class="line"><span class="selector-class">.sprite</span> &#123;<span class="attribute">display</span>:inline-block; <span class="attribute">overflow</span>:hidden; <span class="attribute">background-repeat</span>: no-repeat;<span class="attribute">background-image</span>:<span class="built_in">url</span>(http://res.h5jun.com/matrix/8PQEganHkhynPxk-CUyDcJEk.png);&#125;</span>
<span class="line"></span>
<span class="line"><span class="selector-class">.bird0</span> &#123;<span class="attribute">width</span>:<span class="number">86px</span>; <span class="attribute">height</span>:<span class="number">60px</span>; <span class="attribute">background-position</span>: -<span class="number">178px</span> -<span class="number">2px</span>&#125;</span>
<span class="line"><span class="selector-class">.bird1</span> &#123;<span class="attribute">width</span>:<span class="number">86px</span>; <span class="attribute">height</span>:<span class="number">60px</span>; <span class="attribute">background-position</span>: -<span class="number">90px</span> -<span class="number">2px</span>&#125;</span>
<span class="line"><span class="selector-class">.bird2</span> &#123;<span class="attribute">width</span>:<span class="number">86px</span>; <span class="attribute">height</span>:<span class="number">60px</span>; <span class="attribute">background-position</span>: -<span class="number">2px</span> -<span class="number">2px</span>&#125;</span>
<span class="line"></span>
<span class="line"> <span class="selector-id">#bird</span>&#123;</span>
<span class="line">   <span class="attribute">position</span>: absolute;</span>
<span class="line">   <span class="attribute">left</span>: <span class="number">100px</span>;</span>
<span class="line">   <span class="attribute">top</span>: <span class="number">100px</span>;</span>
<span class="line">   <span class="attribute">zoom</span>: <span class="number">0.5</span>;</span>
<span class="line"> &#125;</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"bird"</span> <span class="attr">class</span>=<span class="string">"sprite bird1"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>&gt;</span><span class="javascript"></span>
<span class="line"><span class="keyword">var</span> i = <span class="number">0</span>;</span>
<span class="line">setInterval(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  bird.className = <span class="string">"sprite "</span> + <span class="string">'bird'</span> + ((i++) % <span class="number">3</span>);</span>
<span class="line">&#125;, <span class="number">1000</span>/<span class="number">10</span>);</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/EKozmQ" target="_blank" rel="external">demo</a></p>
<h2 id="css-3-dong-hua">CSS3动画</h2>
<ul>
<li>
<p>transitions属性:</p>
<p>支持浏览器：<a href="http://caniuse.com/#feat=css-transitions" target="_blank" rel="external">IE10+,GC,FF</a></p>
<p>timing functions属性：</p>
<p><code>linear</code>（规定以相同速度开始至结束的过渡效果（等于 cubic-bezier(0,0,1,1)）</p>
<p><code>ease</code>（规定慢速开始，然后变快，然后慢速结束的过渡效果（cubic-bezier(0.25,0.1,0.25,1)）</p>
<p><code>ease-in</code>（规定以慢速开始的过渡效果（等于 cubic-bezier(0.42,0,1,1)）</p>
<p><code>ease-out</code>（规定以慢速结束的过渡效果（等于 cubic-bezier(0,0,0.58,1)）</p>
<p><code>ease-in-out</code>（规定以慢速开始和结束的过渡效果（等于 cubic-bezier(0.42,0,0.58,1)）</p>
<p><code>cubic-bezier(n,n,n,n)</code></p>
</li>
</ul>
<h3 id="li-8-1-yuan-zhou-yun-dong">例8-1 圆周运动</h3>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
</pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="css"></span>
<span class="line">  <span class="selector-id">#block</span>&#123;</span>
<span class="line">  	<span class="attribute">position</span>:absolute;</span>
<span class="line">    <span class="attribute">left</span>: <span class="number">200px</span>;</span>
<span class="line">    <span class="attribute">top</span>: <span class="number">100px</span>;</span>
<span class="line">    <span class="attribute">width</span>: <span class="number">20px</span>;</span>
<span class="line">    <span class="attribute">height</span>: <span class="number">20px</span>;</span>
<span class="line">    <span class="attribute">background</span>: <span class="number">#0c8</span>;</span>
<span class="line">    <span class="attribute">text-align</span>: center;</span>
<span class="line">    <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span>
<span class="line">    <span class="attribute">transform-origin</span>: <span class="number">0</span> <span class="number">100px</span>;</span>
<span class="line">    <span class="attribute">transform</span>: <span class="built_in">rotate</span>(0deg);</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="selector-id">#block</span><span class="selector-class">.play</span> &#123;</span>
<span class="line">    <span class="attribute">transform</span>: <span class="built_in">rotate</span>(360deg);</span>
<span class="line">  	<span class="attribute">transition</span>: transform <span class="number">2.0s</span> linear;</span>
<span class="line">  &#125;</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"block"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span>
<span class="line">  block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  	block.className = <span class="string">'play'</span>;</span>
<span class="line">  &#125;);</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/Myrdro" target="_blank" rel="external">demo</a></p>
<h3 id="li-8-2-bei-sai-er-qu-xian">例8-2 贝塞尔曲线</h3>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
</pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="css"></span>
<span class="line">  <span class="selector-id">#block</span>&#123;</span>
<span class="line">  	<span class="attribute">position</span>:absolute;</span>
<span class="line">    <span class="attribute">left</span>: <span class="number">50px</span>;</span>
<span class="line">    <span class="attribute">top</span>: <span class="number">200px</span>;</span>
<span class="line">    <span class="attribute">width</span>: <span class="number">20px</span>;</span>
<span class="line">    <span class="attribute">height</span>: <span class="number">20px</span>;</span>
<span class="line">    <span class="attribute">background</span>: <span class="number">#0c8</span>;</span>
<span class="line">    <span class="attribute">text-align</span>: center;</span>
<span class="line">    <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span>
<span class="line">  &#125;</span>
<span class="line">  <span class="selector-id">#block</span><span class="selector-class">.play</span> &#123;</span>
<span class="line">    <span class="attribute">transform</span>: <span class="built_in">translateX</span>(200px);</span>
<span class="line">  	<span class="attribute">transition</span>: transform <span class="number">2.0s</span> <span class="built_in">cubic-bezier</span>(0.68, -0.55, 0.265, 1.55);</span>
<span class="line">  &#125;</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"block"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span>
<span class="line">  block.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">  	block.className = <span class="string">'play'</span>;</span>
<span class="line">  &#125;);</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/BKJgBQ" target="_blank" rel="external">demo</a></p>
<h3 id="li-8-3-transition-fu-gai">例8-3 transition覆盖</h3>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-id">#block</span><span class="selector-class">.play</span> &#123;</span>
<span class="line">  <span class="attribute">border-radius</span>: <span class="number">0</span>;</span>
<span class="line">  <span class="attribute">transform</span>: <span class="built_in">scale</span>(2.0);</span>
<span class="line">  <span class="attribute">background</span>: <span class="number">#c80</span>;</span>
<span class="line">  <span class="attribute">transition</span>: all <span class="number">2.0s</span> <span class="built_in">cubic-bezier</span>(0.68, -0.55, 0.265, 1.55) <span class="number">3s</span>;</span>
<span class="line">&#125;</span>
<span class="line"><span class="selector-id">#block</span><span class="selector-class">.play2</span> &#123;</span>
<span class="line">  <span class="comment">/* transition 覆盖*/</span></span>
<span class="line">  <span class="attribute">background</span>: <span class="number">#c8f</span>;</span>
<span class="line">  <span class="attribute">transition</span>: all <span class="number">2.0s</span> linear <span class="number">0.5s</span>; </span>
<span class="line">  <span class="attribute">transform</span>: <span class="built_in">scale</span>(2.0) <span class="built_in">rotate</span>(360deg);</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/aNEgOX" target="_blank" rel="external">demo</a></p>
<ul>
<li>
<p>animations:</p>
<p>支持浏览器：<a href="http://caniuse.com/#feat=css-animation" target="_blank" rel="external">IE10+,GC,FF</a></p>
<p>主要属性：</p>
<p><code>keyframes'name</code>（规定需要绑定到选择器的 keyframe 名称）</p>
<p><code>duration</code>（规定完成动画所花费的时间，以秒或毫秒计）</p>
<p><code>timing functions</code>（规定动画的速度曲线）</p>
<p><code>delay</code>（规定在动画开始之前的延迟）</p>
<p><code>iteration count</code>（规定动画应该播放的次数）</p>
<p><code>direction</code>（规定是否应该轮流反向播放动画）</p>
</li>
</ul>
<h3 id="li-8-4-wang-fu-yuan-zhou-yun-dong">例8-4 往复圆周运动</h3>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-id">#block</span>&#123;</span>
<span class="line">  <span class="attribute">position</span>:absolute;</span>
<span class="line">  <span class="attribute">left</span>: <span class="number">200px</span>;</span>
<span class="line">  <span class="attribute">top</span>: <span class="number">100px</span>;</span>
<span class="line">  <span class="attribute">width</span>: <span class="number">20px</span>;</span>
<span class="line">  <span class="attribute">height</span>: <span class="number">20px</span>;</span>
<span class="line">  <span class="attribute">background</span>: <span class="number">#0c8</span>;</span>
<span class="line">  <span class="attribute">text-align</span>: center;</span>
<span class="line">  <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span>
<span class="line">  <span class="attribute">animation</span>: roll <span class="number">2.0s</span> linear <span class="number">0s</span> infinite alternate;</span>
<span class="line">  <span class="attribute">transform-origin</span>: <span class="number">0</span> <span class="number">100px</span>;</span>
<span class="line">&#125;</span>
<span class="line"> @<span class="keyword">keyframes</span> roll&#123;</span>
<span class="line">  0%&#123;<span class="attribute">transform</span>:<span class="built_in">rotate</span>(0deg)&#125;</span>
<span class="line">  100%&#123;<span class="attribute">transform</span>:<span class="built_in">rotate</span>(360deg)&#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/BKJgpR" target="_blank" rel="external">demo</a></p>
<h3 id="li-8-5-wang-fu-yuan-zhou-yun-dong-bei-sai-er-qu-xian">例8-5 往复圆周运动（贝塞尔曲线）</h3>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
</pre></td><td class="code"><pre><span class="line"><span class="selector-id">#block</span>&#123;</span>
<span class="line">  <span class="attribute">position</span>:absolute;</span>
<span class="line">  <span class="attribute">left</span>: <span class="number">200px</span>;</span>
<span class="line">  <span class="attribute">top</span>: <span class="number">100px</span>;</span>
<span class="line">  <span class="attribute">width</span>: <span class="number">20px</span>;</span>
<span class="line">  <span class="attribute">height</span>: <span class="number">20px</span>;</span>
<span class="line">  <span class="attribute">background</span>: <span class="number">#0c8</span>;</span>
<span class="line">  <span class="attribute">text-align</span>: center;</span>
<span class="line">  <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span>
<span class="line">  <span class="attribute">animation</span>: roll <span class="number">4.0s</span> linear <span class="number">0s</span> infinite;</span>
<span class="line">  <span class="attribute">transform-origin</span>: <span class="number">0</span> <span class="number">100px</span>;</span>
<span class="line">&#125;</span>
<span class="line"> @<span class="keyword">keyframes</span> roll&#123;</span>
<span class="line">  0%&#123;<span class="attribute">transform</span>:<span class="built_in">rotate</span>(0deg)&#125;</span>
<span class="line">  50%&#123;<span class="attribute">transform</span>:<span class="built_in">rotate</span>(360deg)&#125;</span>
<span class="line">  100%&#123;<span class="attribute">transform</span>:<span class="built_in">rotate</span>(0deg)&#125;</span>
<span class="line">&#125;</span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/oxprWg" target="_blank" rel="external">demo</a></p>
<ul>
<li>
<p>动画组合：</p>
<p>主要内容：</p>
<p><code>animation-fill-mode</code></p>
<p><code>webkitAnimationEnd</code></p>
</li>
</ul>
<h3 id="li-8-6-zu-he-dong-hua">例8-6 组合动画</h3>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
<span class="line">17</span>
<span class="line">18</span>
<span class="line">19</span>
<span class="line">20</span>
<span class="line">21</span>
<span class="line">22</span>
<span class="line">23</span>
<span class="line">24</span>
<span class="line">25</span>
<span class="line">26</span>
<span class="line">27</span>
<span class="line">28</span>
<span class="line">29</span>
<span class="line">30</span>
<span class="line">31</span>
<span class="line">32</span>
<span class="line">33</span>
<span class="line">34</span>
<span class="line">35</span>
<span class="line">36</span>
<span class="line">37</span>
<span class="line">38</span>
<span class="line">39</span>
<span class="line">40</span>
<span class="line">41</span>
<span class="line">42</span>
<span class="line">43</span>
<span class="line">44</span>
<span class="line">45</span>
<span class="line">46</span>
<span class="line">47</span>
<span class="line">48</span>
<span class="line">49</span>
<span class="line">50</span>
<span class="line">51</span>
<span class="line">52</span>
<span class="line">53</span>
<span class="line">54</span>
<span class="line">55</span>
<span class="line">56</span>
<span class="line">57</span>
<span class="line">58</span>
<span class="line">59</span>
<span class="line">60</span>
<span class="line">61</span>
<span class="line">62</span>
<span class="line">63</span>
<span class="line">64</span>
<span class="line">65</span>
<span class="line">66</span>
<span class="line">67</span>
<span class="line">68</span>
</pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="css"></span>
<span class="line">  <span class="selector-id">#block</span>&#123;</span>
<span class="line">  	<span class="attribute">position</span>:absolute;</span>
<span class="line">    <span class="attribute">left</span>: <span class="number">150px</span>;</span>
<span class="line">    <span class="attribute">top</span>: <span class="number">200px</span>;</span>
<span class="line">    <span class="attribute">width</span>: <span class="number">20px</span>;</span>
<span class="line">    <span class="attribute">height</span>: <span class="number">20px</span>;</span>
<span class="line">    <span class="attribute">background</span>: <span class="number">#0c8</span>;</span>
<span class="line">    <span class="attribute">text-align</span>: center;</span>
<span class="line">    <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span>
<span class="line">    <span class="attribute">animation</span>: anim <span class="number">2.0s</span> linear <span class="number">0s</span> forwards;</span>
<span class="line">  &#125;</span>
<span class="line">  @<span class="keyword">keyframes</span> anim&#123;</span>
<span class="line">    0%&#123;<span class="attribute">border-radius</span>: <span class="number">50%</span>&#125;</span>
<span class="line">    50%&#123;<span class="attribute">border-radius</span>: <span class="number">0</span>; <span class="attribute">background</span>: <span class="number">#c80</span>;&#125;</span>
<span class="line">    100%&#123;<span class="attribute">border-radius</span>: <span class="number">20%</span>; <span class="attribute">transform</span>:<span class="built_in">scale</span>(2.0); <span class="attribute">background</span>: <span class="number">#08c</span>;&#125;</span>
<span class="line">  &#125;</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"block"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>
<span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span>
<span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Animator</span>(<span class="params">duration, progress, easing</span>)</span>&#123;</span>
<span class="line">    <span class="keyword">this</span>.duration = duration;</span>
<span class="line">    <span class="keyword">this</span>.progress = progress;</span>
<span class="line">    <span class="keyword">this</span>.easing = easing || <span class="function"><span class="keyword">function</span>(<span class="params">p</span>)</span>&#123;<span class="keyword">return</span> p&#125;;</span>
<span class="line">  &#125;</span>
<span class="line">  </span>
<span class="line">  Animator.prototype = &#123;</span>
<span class="line">  	<span class="attr">start</span>: <span class="function"><span class="keyword">function</span>(<span class="params">finished</span>)</span>&#123;</span>
<span class="line">      <span class="keyword">var</span> startTime = <span class="built_in">Date</span>.now();</span>
<span class="line">      <span class="keyword">var</span> duration = <span class="keyword">this</span>.duration,</span>
<span class="line">          self = <span class="keyword">this</span>;</span>
<span class="line">      </span>
<span class="line">      requestAnimationFrame(<span class="function"><span class="keyword">function</span> <span class="title">step</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">      	<span class="keyword">var</span> p = (<span class="built_in">Date</span>.now() - startTime) / duration;</span>
<span class="line">        <span class="keyword">var</span> next =  <span class="literal">true</span>;</span>
<span class="line">        </span>
<span class="line">        <span class="keyword">if</span>(p &lt; <span class="number">1.0</span>)&#123;</span>
<span class="line">          self.progress(self.easing(p), p);</span>
<span class="line">        &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">          <span class="keyword">if</span>(<span class="keyword">typeof</span> finished === <span class="string">'function'</span>)&#123;</span>
<span class="line">          	next = finished() === <span class="literal">false</span>;</span>
<span class="line">          &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">          	next = finished === <span class="literal">false</span>;</span>
<span class="line">          &#125;</span>
<span class="line">          </span>
<span class="line">          <span class="keyword">if</span>(!next)&#123;</span>
<span class="line">            self.progress(self.easing(<span class="number">1.0</span>), <span class="number">1.0</span>);</span>
<span class="line">          &#125;<span class="keyword">else</span>&#123;</span>
<span class="line">            startTime += duration;</span>
<span class="line">            self.progress(self.easing(p), p);</span>
<span class="line">          &#125;</span>
<span class="line">        &#125;</span>
<span class="line">        </span>
<span class="line">        <span class="keyword">if</span>(next) requestAnimationFrame(step);</span>
<span class="line">      &#125;);</span>
<span class="line">    &#125;</span>
<span class="line">  &#125;;</span>
<span class="line">  </span>
<span class="line">  <span class="keyword">var</span> easing = BezierEasing(<span class="number">0.68</span>, <span class="number">-0.55</span>, <span class="number">0.265</span>, <span class="number">1.55</span>);</span>
<span class="line">  <span class="keyword">var</span> a1 = <span class="keyword">new</span> Animator(<span class="number">2000</span>, <span class="function"><span class="keyword">function</span>(<span class="params">ep,p</span>)</span>&#123;</span>
<span class="line">	<span class="keyword">var</span> x = <span class="number">150</span> + <span class="number">200</span> * ep;</span>
<span class="line">    block.style.left = x + <span class="string">'px'</span>;</span>
<span class="line">  &#125;, easing);</span>
<span class="line">  </span>
<span class="line">  block.addEventListener(<span class="string">'webkitAnimationEnd'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span>
<span class="line">    a1.start();</span>
<span class="line">  &#125;);</span>
<span class="line"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span>
</pre></td></tr></table></figure>
<p><a href="http://codepen.io/yangzj1992/pen/dMJBeG" target="_blank" rel="external">demo</a></p>
<h2 id="svg-dong-hua">SVG 动画</h2>
<p><a href="http://www.zhangxinxu.com/wordpress/2014/08/so-powerful-svg-smil-animation/" target="_blank" rel="external">超级强大的SVG SMIL animation动画详解</a></p>
<h2 id="dong-hua-xing-neng">动画性能</h2>
<p><a href="http://melonh.com/sharing/slides.html?file=high_performance_animation" target="_blank" rel="external">动画性能</a></p>
<p>具体参考即为如下几点:
由于渲染三阶段分为：Layout---&gt;Paint---&gt;Composite，针对此三者进行优化。
优化目标：15FPS 不流畅 ，30FPS+ 感觉流畅，60FPS 舒适完美</p>
<p>触发 Layout 的方式有：</p>
<ul>
<li>改变 width, height, margin 等和大小、位置相关的属性</li>
<li>读取 size, position 相关得属性
要点：</li>
<li>使用 transform 代替 top, left 的动画</li>
<li>分离读写，减少 Layout</li>
<li>面对解耦代码，使用 rAF 推迟的方法分离读写。</li>
</ul>
<p>触发 Paint 的方式：
当修改 border-radius, box-shadow,color 等展示相关属性时，会触发 paint
要点：</p>
<ul>
<li>简化绘制的复杂度</li>
<li>避免不必要的绘制</li>
<li>减少绘制区域</li>
</ul>
<p>Composite 小结:</p>
<ul>
<li>GPU 是有限度的，不要滥用 GPU 资源生成不必要的 Layer</li>
<li>留意意外生成的 Layer</li>
</ul>
<h2 id="zong-jie">总结</h2>
<p>以上是所有内容，相关代码同时整理了一份在 <a href="https://github.com/yangzj1992/Animation-tutorial" target="_blank" rel="external">github</a> 上，再次感谢月影大大<a href="http://matrix.h5jun.com/slide/show?id=117#/" target="_blank" rel="external">分享</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;参考月影大大同名&lt;a href=&quot;http://matrix.h5jun.com/slide/show?id=117#/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;分享内容&lt;/a&gt;总结了此份博文。经过一番归纳整理后熟悉了很多，整理于此，以供参考。&lt;/p
    
    </summary>
    
      <category term="web动画" scheme="http://qcyoung.com/categories/web%E5%8A%A8%E7%94%BB/"/>
    
    
      <category term="前端知识" scheme="http://qcyoung.com/tags/%E5%89%8D%E7%AB%AF%E7%9F%A5%E8%AF%86/"/>
    
      <category term="浏览器" scheme="http://qcyoung.com/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
    
      <category term="性能优化" scheme="http://qcyoung.com/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/"/>
    
      <category term="web动画" scheme="http://qcyoung.com/tags/web%E5%8A%A8%E7%94%BB/"/>
    
  </entry>
  
</feed>
