<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>格物致知</title>
  <subtitle>Happiness only real when shared.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://amobiz.github.io/"/>
  <updated>2016-08-11T02:43:47.000Z</updated>
  <id>https://amobiz.github.io/</id>
  
  <author>
    <name>Amobiz Chen</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>埋設 DFP - DoubleClick for Publishers 廣告</title>
    <link href="https://amobiz.github.io/2016/08/11/react-dfp/"/>
    <id>https://amobiz.github.io/2016/08/11/react-dfp/</id>
    <published>2016-08-11T00:42:00.000Z</published>
    <updated>2016-08-11T02:43:47.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1459501462159-97d5bded1416?dpr=2&amp;auto=format&amp;crop=entropy&amp;fit=crop&amp;w=767&amp;h=511&amp;q=80&amp;cs=tinysrgb" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>廣告媒合</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/sweeticecreamphotography">Sweet Ice Cream Photography</a></div></div></div></div>
<h1 id="埋設-DFP-DoubleClick-for-Publishers-廣告"><a href="#埋設-DFP-DoubleClick-for-Publishers-廣告" class="headerlink" title="埋設 DFP - DoubleClick for Publishers 廣告"></a>埋設 <a href="https://www.google.com/dfp/" target="_blank" rel="external">DFP - DoubleClick for Publishers</a> 廣告</h1><h3 id="初始設定"><a href="#初始設定" class="headerlink" title="初始設定"></a>初始設定</h3><p>比較跳脫一般經驗法則的是，在載入 <a href="//www.googletagservices.com/tag/js/gpt.js">script</a> 之後，服務仍然不是可用狀態，因此，此時不可呼叫相關服務 API。</p>
<p>譬如，以下是錯誤做法：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$.getScript(<span class="string">&#39;//www.googletagservices.com/tag/js/gpt.js&#39;</span>).then(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">window</span>.googletag.pubads().enableSingleRequest();</span><br><span class="line">    <span class="built_in">window</span>.googletag.enableServices();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>不論是透過直接在 html 中埋入 script tag 的方式載入，或是以上面的方式動態載入 DFP script，都不可以直接呼叫 DFP 的 API。</p>
<p>正確的做法，須將命令以函數的形式，推到 <code>window.googletag.cmd</code> 陣列中。注意，此時 <code>googletag</code> 和 <code>googletag.cmd</code> 可以只是普通物件和陣列。待 DFP 服務啟動後，會在 <code>googletag</code> 中添加功能，然後逐一呼叫被推入 <code>googletag.cmd</code> 陣列的函數。也就是說，在該函數中才能保證 DFP 的服務已備妥，並且才能呼叫 API：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> adUnitPath = <span class="string">&#39;pub-id/slot-id&#39;</span>;</span><br><span class="line"><span class="keyword">var</span> adSize = [<span class="number">300</span>, <span class="number">25</span>];</span><br><span class="line"><span class="keyword">var</span> adElementId = <span class="string">&#39;id-to-html-element&#39;</span>;</span><br><span class="line"></span><br><span class="line">$.getScript(<span class="string">&#39;//www.googletagservices.com/tag/js/gpt.js&#39;</span>).then(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> googletag = <span class="built_in">window</span>.googletag || (<span class="built_in">window</span>.googletag = []);</span><br><span class="line">    googletag.cmd = (googletag.cmd || []);</span><br><span class="line">    googletag.cmd.push(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        initDfp();</span><br><span class="line">        googletag.defineSlot(adUnitPath, adSize, adElementId)</span><br><span class="line">            .addService(googletag.pubads());</span><br><span class="line">        googletag.display(adElementId);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initDfp</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    googletag.pubads().enableSingleRequest();</span><br><span class="line">    googletag.enableServices();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的 <code>adElementId</code>，雖然在 DFP 上使用 generate tag 時，會產生一個 <code>div-gpt-ad-1234567890123-0</code> 格式的字串，要我們填在 html 元素作為 id 使用，但其實只要保握 html 的基本原則：id 不要重複，在這裡使用任意 id 皆可，並不影響廣告呈現。不過 DFP 仍然建議，id 最好能反映廣告版位，以方便投廣告的客戶確認。</p>
<h3 id="多個廣告版位"><a href="#多個廣告版位" class="headerlink" title="多個廣告版位"></a>多個廣告版位</h3><p>上面的做法，如果直接套用在多個廣告上，雖然不會有什麼問題，但是重複執行 DFP 的設定，還是讓人渾身不舒服。</p>
<p>可以將 <code>$.getScript()</code> 和 <code>initDfp()</code> 的處理包裝起來，讓它們永遠只會執行一次：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">once</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> promise;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> args;</span><br><span class="line">        <span class="keyword">if</span> (!promise) &#123;</span><br><span class="line">            args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</span><br><span class="line">            promise = <span class="built_in">Promise</span>.resolve(fn.apply(<span class="literal">null</span>, args));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> promise;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> loadDfp = once(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $.getScript(<span class="string">&#39;//www.googletagservices.com/tag/js/gpt.js&#39;</span>).then(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">window</span>.googletag = <span class="built_in">window</span>.googletag || &#123;&#125;;</span><br><span class="line">        <span class="built_in">window</span>.googletag.cmd = <span class="built_in">window</span>.googletag.cmd || [];</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">window</span>.googletag;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> initDfp = once(<span class="function"><span class="keyword">function</span> (<span class="params">googletag</span>) </span>&#123;</span><br><span class="line">    googletag.pubads().enableSingleRequest();</span><br><span class="line">    googletag.enableServices();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>然後，每個要新增版位的地方，都這樣呼叫：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">loadDfp().then(<span class="function"><span class="keyword">function</span> (<span class="params">googletag</span>) </span>&#123;</span><br><span class="line">    googletag.cmd.push(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        initDfp(googletag);</span><br><span class="line">        googletag.defineSlot(adUnitPath, adSize, adElementId)</span><br><span class="line">          .addService(googletag.pubads());</span><br><span class="line">        googletag.display(adElementId);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>注意到，<code>initDfp()</code> 的呼叫，不能併到 <code>loadDfp()</code> 中執行，只能在 <code>googletag.cmd.push()</code> 的函數中執行，否則就又犯了一開始的錯誤。</p>
<p>上面的寫法，還是有點囉唆，於是，我們還可以進一步包裝成這樣：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">dfp</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    loadDfp().then(<span class="function"><span class="keyword">function</span> (<span class="params">googletag</span>) </span>&#123;</span><br><span class="line">        initDfp(googletag);</span><br><span class="line">        googletag.cmd.push(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            fn(googletag);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">dfp(function(googletag) &#123;</span><br><span class="line">    googletag.defineSlot(adUnitPath, adSize, adElementId)</span><br><span class="line">        .addService(googletag.pubads());</span><br><span class="line">    googletag.display(adElementId);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="動態新增廣告"><a href="#動態新增廣告" class="headerlink" title="動態新增廣告"></a>動態新增廣告</h3><p>上面的做法，適用於內容不會變動的頁面。如果是無限捲動列表形式的頁面，可能需要每隔幾個項目就顯示一則廣告。官方建議的做法，是透過呼叫 <code>disableInitialLoad()</code> 函數禁止初始廣告載入，然後於版面備妥時，再呼叫 <code>refresh()</code> 更新廣告內容。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initDfp</span>(<span class="params">googletag</span>) </span>&#123;</span><br><span class="line">    googletag.pubads().enableSingleRequest();</span><br><span class="line">    googletag.pubads().disableInitialLoad();</span><br><span class="line">    googletag.enableServices();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意上面 <code>googletag.pubads().disableInitialLoad()</code> 的呼叫。此呼叫將改變 <code>display()</code> 函數的行為，使 <code>display()</code> 函數只註冊廣告版位，而不進行顯示。</p>
<p>在動態插入廣告之後，再呼叫 <code>refresh()</code> 函數，更新廣告：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">dfp(function(googletag) &#123;</span><br><span class="line">    var slot = googletag.defineSlot(adUnitPath, adSize, adElementId)</span><br><span class="line">        .addService(googletag.pubads());</span><br><span class="line">    googletag.display(adElementId);</span><br><span class="line">    googletag.pubads().refresh([slot]);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>注意上面如何儲存 <code>slot</code>，然後用以呼叫 <code>refresh()</code> 函數。</p>
<h3 id="自動縮合沒有廣告的版面"><a href="#自動縮合沒有廣告的版面" class="headerlink" title="自動縮合沒有廣告的版面"></a>自動縮合沒有廣告的版面</h3><p>也許我們的版位不是非常熱門，經常開天窗(希望不是)。一旦廣告版位沒有人投遞廣告，該廣告版位就會顯示成空白。如果這是所有的版面的常態的話，可以考慮直接在初始化的時候，設定所有的版面在沒有廣告的時候，自動縮合。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initDfp</span>(<span class="params">googletag</span>) </span>&#123;</span><br><span class="line">    googletag.pubads().enableSingleRequest();</span><br><span class="line">    googletag.pubads().disableInitialLoad();</span><br><span class="line">    googletag.pubads().collapseEmptyDivs();</span><br><span class="line">    googletag.enableServices();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>或者，也可以根據需要，依據版面別進行個別設定：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> adCollapse = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">dfp(<span class="function"><span class="keyword">function</span>(<span class="params">googletag</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> slot = googletag.defineSlot(adUnitPath, adSize, adElementId)</span><br><span class="line">        .addService(googletag.pubads())</span><br><span class="line">        .setCollapseEmptyDiv(adCollapse);</span><br><span class="line">    googletag.display(adElementId);</span><br><span class="line">    googletag.pubads().refresh([slot]);</span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="React-Component-範例"><a href="#React-Component-範例" class="headerlink" title="React Component 範例"></a>React Component 範例</h3><p>最後，測試的過程中寫了一簡單的 <a href="https://github.com/amobiz/react-simple-dfp" target="_blank" rel="external">React 範例程式</a>，提供給大家參考。</p>
<h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul>
<li><a href="https://support.google.com/dfp_sb/answer/1651549?hl=zh-Hant" target="_blank" rel="external">Google 發佈商廣告代碼範例</a></li>
<li><a href="https://support.google.com/dfp_sb/answer/3419382?hl=zh-Hant" target="_blank" rel="external">收合空白的 div 元素</a></li>
<li><a href="https://developers.google.com/doubleclick-gpt/reference" target="_blank" rel="external">API</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1459501462159-97d5bded1416?dpr=2&amp;amp;auto=format&amp;amp;crop=entropy&amp;amp;fit
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="React" scheme="https://amobiz.github.io/tags/React/"/>
    
      <category term="modular css" scheme="https://amobiz.github.io/tags/modular-css/"/>
    
      <category term="advertising" scheme="https://amobiz.github.io/tags/advertising/"/>
    
      <category term="component" scheme="https://amobiz.github.io/tags/component/"/>
    
      <category term="dfp" scheme="https://amobiz.github.io/tags/dfp/"/>
    
      <category term="doubleclick" scheme="https://amobiz.github.io/tags/doubleclick/"/>
    
      <category term="doubleclick for publishers" scheme="https://amobiz.github.io/tags/doubleclick-for-publishers/"/>
    
      <category term="gpt" scheme="https://amobiz.github.io/tags/gpt/"/>
    
  </entry>
  
  <entry>
    <title>Modular CSS</title>
    <link href="https://amobiz.github.io/2016/04/22/modular-css-notes/"/>
    <id>https://amobiz.github.io/2016/04/22/modular-css-notes/</id>
    <published>2016-04-22T14:42:00.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1460750860062-82a52139a0d6?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1000" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>模組化 &lt; 封裝 &lt; 與世隔絕 &lt; 受困於小船<br>絕佳搭配 &lt; 天作之合 &lt; 俊男美女</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/sweeticecreamphotography">Sweet Ice Cream Photography</a></div></div></div></div>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這篇文章記錄個人嘗試 CSS-modules 過程中所遇到的問題，並且順便將目前業界各種對 modular css 的嘗試/進展，做一個相關資訊的摘錄。</p>
<p>還沒有找到適合自己偏好作法的讀者，可以將此文視為簡單的<a href="http://www.gegugu.com/2016/01/11/20196.html" target="_blank" rel="external">技術選型</a>指南，從中挑選適合自己的作法。</p>
<a id="more"></a>
<h2 id="模組化的-CSS"><a href="#模組化的-CSS" class="headerlink" title="模組化的 CSS"></a>模組化的 CSS</h2><p>學過網頁設計的人都知道，CSS 作用的對象，是整個 html 文件。這在過去網頁都是靜態文件為主的年代，完全是合情合理的設計，不過到現代大量 Web App 為主的 SPA 時代，這樣的特性，反而造成 Web App 開發的困擾。而 CSS 規格的發展，又遠遠落後於 JavaScript / HTML5 的發展，因此，出現了各種不同的模組化嘗試。</p>
<h3 id="CSS-in-JS-Inline-Styles"><a href="#CSS-in-JS-Inline-Styles" class="headerlink" title="CSS-in-JS (Inline Styles)"></a>CSS-in-JS (Inline Styles)</h3><p>在這一兩年引起廣泛注目的 ReactJS，其引入的 JSX 語法，倡導在 JavaScript 中撰寫 html markup。更早，甚至於在發表 ReactJS 之前，其團隊就有人介紹 CSS-in-JS 的構想 (YouTube 影片找到後補上)：以 JavaScript 撰寫 CSS，從而避開 global scope 的問題。</p>
<p>原理就是使用 JSON 語法取代 css rule，然後使用程式轉換為 css inline style，再設定給元件。譬如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">head.style.cssText = <span class="string">&#39;width:200px;height:70px;display:bolck&#39;</span>;</span><br></pre></td></tr></table></figure>

<p>現在最受歡迎的 <a href="https://github.com/FormidableLabs/radium" target="_blank" rel="external">radium</a>，引用其 readme 介紹，用法像這樣：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Radium = <span class="built_in">require</span>(<span class="string">&#39;radium&#39;</span>);</span><br><span class="line"><span class="keyword">var</span> React = <span class="built_in">require</span>(<span class="string">&#39;react&#39;</span>);</span><br><span class="line"><span class="keyword">var</span> color = <span class="built_in">require</span>(<span class="string">&#39;color&#39;</span>);</span><br><span class="line"></span><br><span class="line">@Radium</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Button</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">static</span> propTypes = &#123;</span><br><span class="line">    <span class="attr">kind</span>: React.PropTypes.oneOf([<span class="string">&#39;primary&#39;</span>, <span class="string">&#39;warning&#39;</span>]).isRequired</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="comment">// Radium extends the style attribute to accept an array. It will merge</span></span><br><span class="line">    <span class="comment">// the styles in order. We use this feature here to apply the primary</span></span><br><span class="line">    <span class="comment">// or warning styles depending on the value of the kind prop. Since its</span></span><br><span class="line">    <span class="comment">// all just JavaScript, you can use whatever logic you want to decide which</span></span><br><span class="line">    <span class="comment">// styles are applied (props, state, context, etc).</span></span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      <span class="xml"><span class="tag">&lt;<span class="name">button</span></span><br><span class="line">        <span class="attr">style</span>=<span class="string">&#123;[</span></span><br><span class="line">          <span class="attr">styles.base</span>,</span><br><span class="line">          <span class="attr">styles</span>[<span class="attr">this.props.kind</span>]</span><br><span class="line">        ]&#125;&gt;</span></span><br><span class="line">        &#123;this.props.children&#125;</span><br><span class="line">      <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// You can create your style objects dynamically or share them for</span></span><br><span class="line"><span class="comment">// every instance of the component.</span></span><br><span class="line"><span class="keyword">var</span> styles = &#123;</span><br><span class="line">  <span class="attr">base</span>: &#123;</span><br><span class="line">    <span class="attr">color</span>: <span class="string">&#39;#fff&#39;</span>,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Adding interactive state couldn’t be easier! Add a special key to your</span></span><br><span class="line">    <span class="comment">// style object (:hover, :focus, :active, or @media) with the additional rules.</span></span><br><span class="line">    <span class="string">&#39;:hover&#39;</span>: &#123;</span><br><span class="line">      <span class="attr">background</span>: color(<span class="string">&#39;#0074d9&#39;</span>).lighten(<span class="number">0.2</span>).hexString()</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="attr">primary</span>: &#123;</span><br><span class="line">    <span class="attr">background</span>: <span class="string">&#39;#0074D9&#39;</span></span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="attr">warning</span>: &#123;</span><br><span class="line">    <span class="attr">background</span>: <span class="string">&#39;#FF4136&#39;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>雖然解決了 global scope 問題，但似乎引入了更多問題：</p>
<ol>
<li><p>難以搭配使用前處理器，</p>
<p> 這應該是致命問題，至少，autoprefixer 應該已經是標準配備了吧。</p>
</li>
<li><p>難以調整、除錯。</p>
<p> 由於 inline style 只對單一元件有效，在 Chrome DevTools 中，一次也只能改一個元件，不像平常只要修改 rule，馬上就可以套用到所有的對應元件上。</p>
</li>
</ol>
<h3 id="Naming-Conventions"><a href="#Naming-Conventions" class="headerlink" title="Naming Conventions"></a>Naming Conventions</h3><p>使用人為的命名規範方式，來區別模組名稱。</p>
<p>這方面的嘗試堪稱代表的有：<a href="http://oocss.org/" target="_blank" rel="external">OOCSS</a>, <a href="https://smacss.com/" target="_blank" rel="external">SMACSS</a>, <a href="https://en.bem.info/" target="_blank" rel="external">BEM</a>, <a href="https://suitcss.github.io/" target="_blank" rel="external">SUIT</a>，其中又以 BEM 及 SUIT 最受到歡迎。許多人認為 SUIT 是 BEM 的改良，而其語法除了 camelCase 較不討喜之外，相對於 BEM 使用 <code>__</code> 及 <code>--</code> 來區別層級，SUIT 的語法更容易理解。</p>
<p>採用命名規範來處理 css 的模組化問題，最重要的守則，就是：</p>
<aside class="cheatsheet"><p>Blocks should only be used inside a component of the same name.</p>
<p>『最上層區塊的命名必須與元件同名，並且其下的子元素，只能用在元件內部。』</p>
<p>---– <a href="https://medium.com/seek-ui-engineering/block-element-modifying-your-javascript-components-d7f99fcab52b#.lc19hxlo7" target="_blank" rel="external">Block, Element, Modifying Your JavaScript Components</a></p>
</aside>
<p>採用這些方案的好處是，由於是 100% 原生的 CSS 語法，因此有以下優點:</p>
<ol>
<li>不須要任何前處理器工具，導入、除錯最容易，</li>
<li>可以輕易與既有的前處理器工具搭配，</li>
<li>可以避免使用巢狀的選擇器，提昇效能，並且避免結構依賴問題。</li>
</ol>
<p>上面第二項，譬如可以使用 <a href="https://github.com/postcss/postcss" target="_blank" rel="external">PostCSS</a> 外掛 <a href="https://github.com/ileri/postcss-bem" target="_blank" rel="external">postcss-bem</a>，就可以使用巢狀的宣告方式，省卻繁複的名稱重複撰寫問題，輕鬆撰寫符合 SUIT 規範的 CSS：</p>
<figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">@namespace</span> app &#123;</span><br><span class="line">  <span class="variable">@component</span> SearchForm &#123;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0</span>;</span><br><span class="line">    <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Typically, place modifiers above descendents</span></span><br><span class="line">    <span class="variable">@modifier</span> advanced &#123;</span><br><span class="line">      <span class="attribute">padding</span>: <span class="number">1rem</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">@descendent</span> textField &#123;</span><br><span class="line">      <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ccc</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// This creates a state for the textField descendant</span></span><br><span class="line">      <span class="variable">@when</span> invalid &#123;</span><br><span class="line">        <span class="attribute">border</span>: <span class="number">1px</span> solid red;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>輸出：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.app-SearchForm</span> &#123;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.app-SearchForm</span>–<span class="selector-tag">advanced</span> &#123;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">1rem</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.app-SearchForm-textField</span> &#123;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ccc</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.app-SearchForm-textField</span><span class="selector-class">.is-invalid</span> &#123;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid red;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>每個輸出的 class，都將直接對應到目標元件上，從而避免使用巢狀的選擇器：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">name</span>=<span class="string">“search”</span> <span class="attr">class</span>=<span class="string">“app-SearchForm”</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">“name”</span> <span class="attr">class</span>=<span class="string">“app-SearchForm-textField”</span> <span class="attr">required</span>&gt;</span><span class="tag">&lt;/<span class="name">input</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>缺點：</p>
<ol>
<li><p>仍然是 global scope</p>
<p> 雖然稍後會介紹的 CSS-modules 基本上也是 global scope，不過相較於 CSS-modules，BEM/SUIT 不小心出現重複命名的機會還是比較高，不過，當然這可以透過下面提到的 linter 來避免。</p>
</li>
<li><p>手動處理容易出錯</p>
<p> 我一直覺得很奇怪，可能是我自己閱讀的還不夠多，好像很少看到有人提到/抱怨，目前還缺乏 markup 處理工具，幫忙將同樣的結果套用到 html 上。雖然有 <a href="https://github.com/ileri/postcss-bem" target="_blank" rel="external">postcss-bem</a> 幫忙處理 css 部份，但目前只能手動將最終的名稱套用到 html 上，仍然相當不方便，而且容易出錯。雖然有 <a href="https://github.com/postcss/postcss-bem-linter" target="_blank" rel="external">postcss-bem-linter</a> 來幫忙檢查 css 是否符合 BEM/SUIT 規範，卻沒有工具可以用來檢查 html。</p>
</li>
<li><p>名稱太長</p>
<p> 如果認為 class 名稱太長，可以使用 <a href="https://github.com/calebthebrewer/gulp-selectors" target="_blank" rel="external">gulp-selectors</a> 這類工具，進一步做處理。不過也有不少反對意見，主要是認為<a href="http://stackoverflow.com/a/8067603/726650" target="_blank" rel="external">開啟 GZip 壓縮</a>即可，甚至也有人認為這樣會影響 SEO，但是應該是除非動到 <a href="http://microformats.org/" target="_blank" rel="external">microformat</a>，否則<a href="http://webmasters.stackexchange.com/a/25477" target="_blank" rel="external">沒有影響</a>。</p>
</li>
</ol>
<h3 id="CSS-modules"><a href="#CSS-modules" class="headerlink" title="CSS-modules"></a>CSS-modules</h3><p><a href="https://github.com/css-modules/css-modules" target="_blank" rel="external">CSS-modules</a> 基本上只是一個規格，描述使用 <code>:local</code> 與 <code>:global</code> 關鍵字來區別 scope；使用 <code>composes</code> 關鍵字來引用其它規則。</p>
<p>CSS-modules 的作者 <a href="https://github.com/markdalgleish" target="_blank" rel="external">Mark Dalgleish</a>，在他的 <a href="https://medium.com/seek-ui-engineering/the-end-of-global-css-90d2a4a06284#.muzqdfjs7" target="_blank" rel="external">The End of Global CSS</a> 一文中提到，利用 Webpack 在 <a href="https://github.com/webpack/css-loader/commit/d2c9c25721a711b0fe041c597b43646e82d9f145" target="_blank" rel="external">2015/04/22 發佈</a>的 <a href="https://github.com/webpack/css-loader#local-scope" target="_blank" rel="external">local scope</a> 功能，我們可以透過 <code>:local(.className)</code> 的語法，得到轉化之後的 class 名稱：</p>
<p><strong>MyComponent.css</strong></p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-pseudo">:local(.foo)</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-pseudo">:local(.bar)</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: blue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>MyComponent.jsx</strong></p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">&#39;./MyComponent.css&#39;</span>;</span><br><span class="line"><span class="keyword">import</span> React, &#123; Component &#125; <span class="keyword">from</span> <span class="string">&#39;react&#39;</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">MyComponent</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&#123;styles.foo&#125;</span>&gt;</span>Foo<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&#123;styles.bar&#125;</span>&gt;</span>Bar<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面 import 得到的 <code>styles</code>，實際上是一個 JSON 物件，內容是透過 <code>:local()</code> 定義的 class 名稱做為鍵值，對應到計算處理過、保證不重複的亂數名稱數值，像這樣：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;foo&quot;</span>: <span class="string">&quot;_1rJwx92-gmbvaLiDdzgXiJ&quot;</span>,</span><br><span class="line">  <span class="attr">&quot;bar&quot;</span>: <span class="string">&quot;_13LGdX8RMStbBE9w-t0gZ1&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然後再利用插入字串的方式，將產出的 class 名稱，注入到 template 中，而達成某種程度隱藏 class 名稱，避免名稱污染問題。不過雖然 class 名稱已經處理過，基本上卻仍然是全域可見。</p>
<p>Mark Dalgleish 認為，一直不斷地重複寫 <code>:local</code> 太麻煩了，大多時候他都是使用 local scope，僅有極少數時候，才需要用到 global scope，所以後來 css-loader 就同意預設為 local scope 了：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.root</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.primary</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面透過 <code>styles.foo</code> 的方式，手動以插值的方式注入 class 名稱的作法，還是比較繁瑣。我個人比較傾向於 <a href="https://github.com/gajus/react-css-modules" target="_blank" rel="external">react-css-modules</a> 的作法，既然要用到工具來處理，既然已經使用 JSX 語法，就應該一次到位，避免再寫多餘的程式碼來引用 style：</p>
<p><strong>dialog.css</strong></p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.root</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.primary</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>dialog.jsx</strong></p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">&#39;react&#39;</span>;</span><br><span class="line"><span class="keyword">import</span> CSSModules <span class="keyword">from</span> <span class="string">&#39;react-css-modules&#39;</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">&#39;./dialog.css&#39;</span>;</span><br><span class="line"></span><br><span class="line">@CSSModules(styles)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">Dialog</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">styleName</span>=<span class="string">&#39;root&#39;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a</span> <span class="attr">styleName</span>=<span class="string">&#39;primary&#39;</span>&gt;</span>Confirm<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意這裡使用 <code>styleName</code> 來引用 class，同時也不再需要寫 <code>styles.</code> 部份。另外，react-css-modules 還做了檢查，若引用的 class 不存在，會提出警告。</p>
<p>一切都非常美好。</p>
<p>而我個人碰到的問題則是：</p>
<ol>
<li><p>與 PostCSS 搭配的取捨</p>
<p> 原有的專案採用 PostCSS 及 cssnext 外掛，尤其是用到了 <a href="https://github.com/jonathantneal/postcss-nesting" target="_blank" rel="external">nesting</a> 功能，在與 css-loader 搭配上，遇到了『雞生蛋、蛋生雞』問題，無法正確設定套用的順序。目前只能先停用遇到問題的語法。</p>
</li>
<li><p>預設 local scope</p>
<p> 我認為 css-loader 衝過頭了，預設不應該是 local scope，<strong>Local scope 只有在撰寫元件的時候需要用到</strong>。整個網頁還是有許多共用元素需要使用 global scope css。Webpack css-loader 預設開啟 local scope，會造成引用其他 framework 的 style 的極大困難。</p>
<p> 譬如，無法使用這樣的語法來引用 global style：</p>
 <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:global &#123;</span><br><span class="line">        @import &apos;normalize.css&apos;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 其實我個人傾向於希望能夠在要使用 local scope 時，才以 <code>:local</code> block 的方式啟用：</p>
 <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">:local &#123;</span><br><span class="line">    .root &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    .primary &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 這樣就不會影響到既有的程式。</p>
<p> 可惜 css-loader 只能透過 <code>modules</code> 參數啟動，否則就完全不支援 local scope。因此，目前這個問題只能透過針對不同路徑套用不同的 css-loader 的方式解決：</p>
<p> <strong>webpack.config.js</strong></p>
 <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>: &#123;</span><br><span class="line">    <span class="attr">loaders</span>: [&#123;</span><br><span class="line">        <span class="attr">test</span>: <span class="regexp">/.jsx?$/</span>,</span><br><span class="line">        <span class="attr">loader</span>: <span class="string">&#39;babel&#39;</span></span><br><span class="line">    &#125;, &#123;</span><br><span class="line">        <span class="attr">test</span>: <span class="regexp">/.css$/</span>,</span><br><span class="line">        <span class="attr">exclude</span>: path.resolve(dirname, <span class="string">&#39;client&#39;</span>),</span><br><span class="line">        <span class="attr">loader</span>: <span class="string">&#39;style!css?modules&amp;localIdentName=[name][local]&#39;</span></span><br><span class="line">    &#125;, &#123;</span><br><span class="line">        <span class="attr">test</span>: <span class="regexp">/.css$/</span>,</span><br><span class="line">        <span class="attr">include</span>: path.resolve(__dirname, <span class="string">&#39;node_modules&#39;</span>),</span><br><span class="line">        <span class="attr">loader</span>: <span class="string">&#39;style!css&#39;</span></span><br><span class="line">    &#125;]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>Webpack + ReactJS 以外的選擇？</p>
</li>
</ol>
<p>由於目前 CSS-modules 的實作，主要還是基於 Webpack 的 css-loader。而 Webpack 的思維，是以 JavaScript 為核心，透過 <code>require()</code> 函數來描述相關資源的關係，剛好與 ReactJS 的 JSX 的作法相同，兩者可說是天作之合。然而，明明要套用 class 的對象是 html/template，卻必須勞動 JavaScript 在<strong>執行期間</strong>來處理，似乎不是那麼經濟。</p>
<p>雖然 <a href="https://github.com/substack/node-browserify" target="_blank" rel="external">Browserify</a> 有 <a href="https://github.com/css-modules/css-modulesify" target="_blank" rel="external">css-modulesify</a>，但 Browserify 其實是更加以 JavaScript 為中心，因此採用的是與 css-loader 完全相同的作法。</p>
<p>我個人傾向於，工具應該要能夠在前處理期間就做好這些轉換工作，最終的 css 和 html template 都能直接處理好 local class 名稱對應，在 JavaScript 中，只要直接引用 template 即可。</p>
<p>輸入：</p>
<p><strong>dialog.css</strong></p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.root</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.primary</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>dialog.html</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">href</span>=<span class="string">&quot;./dialog.css&quot;</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">type</span>=<span class="string">&quot;text/localcss&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;root&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">a</span> <span class="attr">class</span>=<span class="string">&quot;primary&quot;</span>&gt;</span>Confirm<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>輸出：</p>
<p><strong>dialog.html</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></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><br><span class="line"><span class="selector-class">.root1rJwx92-gmbvaLiDdzgXiJ</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.primary13LGdX8RMStbBE9w-t0gZ1</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;root1rJwx92-gmbvaLiDdzgXiJ&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">a</span> <span class="attr">class</span>=<span class="string">&quot;primary13LGdX8RMStbBE9w-t0gZ1&quot;</span>&gt;</span>Confirm<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>當然，這只是目前實作的問題，除了實作預設為 local scope 值得商榷之外，CSS-modules 規範本身並沒有太大的問題。</p>
<p><a href="http://zhuanlan.zhihu.com/p/20495964" target="_blank" rel="external">CSS Modules 详解及 React 中实践</a> 這篇文章有相當深入精闢的技術解說，想進一步了解 CSS-modules 的朋友千萬不要錯過。</p>
<h3 id="Web-Components-Shadow-DOM"><a href="#Web-Components-Shadow-DOM" class="headerlink" title="Web Components / Shadow DOM"></a>Web Components / Shadow DOM</h3><p><a href="https://www.w3.org/TR/2015/WD-shadow-dom-20151215/" target="_blank" rel="external">Shadow DOM</a> 是 <a href="http://webcomponents.org/" target="_blank" rel="external">Web Components</a> 的附屬規格，目前仍在草案階段，瀏覽器的支援也許還不足，但是實際上已經有相當多成熟的實作。最好的例子就是 <a href="https://www.polymer-project.org/1.0/" target="_blank" rel="external">Polymer</a>，已經正式發表 1.0 版，而 AngularJS 2.0 也即將正式發佈。即使不使用 framework，也可以直接透過 <a href="https://github.com/webcomponents/webcomponentsjs/" target="_blank" rel="external">polyfill</a> 使用。</p>
<p>重點是，在 Shadow DOM 中，不論 markup 或 style，都是私有的，外界只能看到最外層的元件。換句話說，對 Shadow DOM 而言，CSS 就是 local scope 的！</p>
<p>使用 Polymer 定義元件的範例：</p>
<p><strong>contact-card.html_</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dom-module</span> <span class="attr">id</span>=<span class="string">&quot;contact-card&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;import&quot;</span> <span class="attr">type</span>=<span class="string">&quot;css&quot;</span> <span class="attr">href</span>=<span class="string">&quot;contact-card.css&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">content</span>&gt;</span><span class="tag">&lt;/<span class="name">content</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">iron-icon</span> <span class="attr">icon</span>=<span class="string">&quot;star&quot;</span> <span class="attr">hidden</span>$=<span class="string">&quot;&#123;&#123;!starred&#125;&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">iron-icon</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span><br><span class="line">    Polymer(&#123;</span><br><span class="line">      <span class="attr">is</span>: <span class="string">&#39;contact-card&#39;</span>,</span><br><span class="line">      <span class="attr">properties</span>: &#123;</span><br><span class="line">        <span class="attr">starred</span>: <span class="built_in">Boolean</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dom-module</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>使用方式：</p>
<p><strong>index.html</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">contact-card</span> <span class="attr">starred</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;profile.jpg&quot;</span> <span class="attr">alt</span>=<span class="string">&quot;Eric&#39;s photo&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">span</span>&gt;</span>Eric Bidelman<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">contact-card</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>使用 AngularJS 2.0 定義元件的的範例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Component &#125; <span class="keyword">from</span> <span class="string">&#39;angular2/core&#39;</span>;</span><br><span class="line"></span><br><span class="line">@Component(&#123;</span><br><span class="line">  <span class="attr">selector</span>: <span class="string">&#39;contact-card&#39;</span>,</span><br><span class="line">  <span class="attr">templateUrl</span>: <span class="string">&#39;components/contact.component.html&#39;</span>,</span><br><span class="line">  <span class="attr">styleUrls</span>:  [<span class="string">&#39;components/contact.component.css&#39;</span>]</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcatCard</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>就是這麼簡單。即使是使用 polyfill，它也會幫忙處理好 CSS scope 問題 (雖然無法真正隱藏)。只是，又回到在執行期間處理轉換的問題。</p>
<p>2016/04/28 補充：剛<a href="https://medium.com/the-vue-point/announcing-vue-js-2-0-8af1bde7ab9#.36j94ry4c" target="_blank" rel="external">發表 2.0 版</a>的 <a href="https://vuejs.org/" target="_blank" rel="external">Vue.js</a>，早在 1.0 時即有 <a href="https://vuejs.org/guide/application.html#Single-File-Components" target="_blank" rel="external">&quot;Single File Components&quot;</a> 的作法，透過 <a href="http://vuejs.github.io/vue-loader/" target="_blank" rel="external">vue-loader</a> 或 <a href="https://github.com/vuejs/vueify" target="_blank" rel="external">vueify</a> 處理之後，也同樣具有 local scope CSS (注意 <code>scoped</code> 屬性):</p>
<p><strong>my-component.vue</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span><span class="css"></span><br><span class="line"><span class="selector-class">.my-component</span> <span class="selector-tag">h2</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;my-component&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h2</span>&gt;</span>&#123;&#123;msg&#125;&#125;<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="attr">data</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="attr">msg</span>: <span class="string">&#39;hello!&#39;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="Houdini"><a href="#Houdini" class="headerlink" title="Houdini"></a>Houdini</h3><p><a href="https://www.smashingmagazine.com/2016/03/houdini-maybe-the-most-exciting-development-in-css-youve-never-heard-of/" target="_blank" rel="external">Houdini: Maybe The Most Exciting Development In CSS You&#39;ve Never Heard Of</a>， 脫逃大師胡迪尼，將為我們在瀏覽器上面留下隱密的後門、看不見的掛勾，讓我們可以在最千鈞一髮的時刻全身而退。</p>
<p>這方面的資訊雖然還不多，不過，如同上文作者所言，這將發生在不久的將來。屆時一旦 <a href="https://www.smashingmagazine.com/2016/03/houdini-maybe-the-most-exciting-development-in-css-youve-never-heard-of/#css-parser-api" target="_blank" rel="external">CSS PARSER API</a> 完成，要添加客製化的 local scope 語法，應該不是難事。</p>
<h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><p>若專案選擇使用 ReactJS/JSX，得力於 <a href="https://github.com/gajus/react-css-modules" target="_blank" rel="external">react-css-modules</a> 及 <a href="https://webpack.github.io/" target="_blank" rel="external">Webpack</a> 的功勞，可以使用直覺的語法，不用放棄慣用的前處理器，就可以享用 CSS-modules 的好處。</p>
<p>若是喜歡追求業界標準，可以考慮直接採用 Web Components/Shadow DOM，或是架構在此基礎之上的 framework 來實作。</p>
<p>如果不想把專案的建構搞得太複雜，則可以考慮最通用的 BEM/SUIT 命名規範方案。</p>
<h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul>
<li><a href="https://github.com/css-modules/css-modules" target="_blank" rel="external">CSS Modules</a></li>
<li><a href="http://www.sitepoint.com/bem-smacss-advice-from-developers/" target="_blank" rel="external">BEM and SMACSS: Advice From Developers Who’ve Been There</a></li>
<li><a href="http://webdesign.tutsplus.com/tutorials/using-postcss-with-bem-and-suit-methodologies--cms-24592" target="_blank" rel="external">Using PostCSS with BEM and SUIT Methodologies</a></li>
<li><a href="https://hsinyu00.wordpress.com/2016/02/21/cssconf-2015-%E7%AD%86%E8%A8%98%E4%BA%8C-css-modules/" target="_blank" rel="external">CSSConf 2015 筆記(二) – CSS Modules</a></li>
<li><a href="https://boke.io/tan-tan-css-modules/" target="_blank" rel="external">谈谈 CSS Modules</a></li>
<li><a href="http://zhuanlan.zhihu.com/p/20495964" target="_blank" rel="external">CSS Modules 详解及 React 中实践</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1460750860062-82a52139a0d6?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1000&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;模組化 &amp;lt; 封裝 &amp;lt; 與世隔絕 &amp;lt; 受困於小船&lt;br&gt;絕佳搭配 &amp;lt; 天作之合 &amp;lt; 俊男美女&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/sweeticecreamphotography&quot;&gt;Sweet Ice Cream Photography&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;這篇文章記錄個人嘗試 CSS-modules 過程中所遇到的問題，並且順便將目前業界各種對 modular css 的嘗試/進展，做一個相關資訊的摘錄。&lt;/p&gt;
&lt;p&gt;還沒有找到適合自己偏好作法的讀者，可以將此文視為簡單的&lt;a href=&quot;http://www.gegugu.com/2016/01/11/20196.html&quot;&gt;技術選型&lt;/a&gt;指南，從中挑選適合自己的作法。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="Webpack" scheme="https://amobiz.github.io/tags/Webpack/"/>
    
      <category term="css" scheme="https://amobiz.github.io/tags/css/"/>
    
      <category term="CSS-modules" scheme="https://amobiz.github.io/tags/CSS-modules/"/>
    
      <category term="modular css" scheme="https://amobiz.github.io/tags/modular-css/"/>
    
      <category term="Web Components" scheme="https://amobiz.github.io/tags/Web-Components/"/>
    
      <category term="Shadow DOM" scheme="https://amobiz.github.io/tags/Shadow-DOM/"/>
    
      <category term="BEM" scheme="https://amobiz.github.io/tags/BEM/"/>
    
      <category term="SUIT" scheme="https://amobiz.github.io/tags/SUIT/"/>
    
  </entry>
  
  <entry>
    <title>AngularJS 1.5 最佳實務</title>
    <link href="https://amobiz.github.io/2016/04/15/angularjs-1.5-best-practices/"/>
    <id>https://amobiz.github.io/2016/04/15/angularjs-1.5-best-practices/</id>
    <published>2016-04-14T23:02:00.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://angularjs.org/" target="_blank" rel="external"><br><img src="https://angularjs.org/img/AngularJS-large.png" alt=""></a></p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文整理截至目前 AngularJS 1.5 為止， 個人以為的最佳實務做法。</p>
<p>為什麼現在？基於以下三點理由：</p>
<ul>
<li>雖然 AngularJS 2.0 發佈在即，但是既有的 AngularJS 1.x 程式碼仍需要維護。</li>
<li>由於 AngularJS 1.x 受到廣泛的歡迎，前後做了不少的改進，因此網路上到處可見其實已經過時的作法。</li>
<li>基於向後相容的原則，有些功能雖然被保留下來，但未必是最佳作法。</li>
</ul>
<p>『你可以這麼做，不代表你必須這麼做』，基於『JavaScript: The Good Parts』的哲理，本文適合具有 AngularJS 1.x 實務開發經驗，然而卻隨著 AngularJS 的發展，逐漸對於網路上充斥各種主觀、矛盾的說法、用法感到困惑的開發者。</p>
<p>雖然遵循本文提出的原則，可以以趨近於元件化的風格開發 AngularJS 1.5 應用程式，但本文不涉及 AngularJS 1.x 移轉到 AngularJS 2.0 議題。</p>
<a id="more"></a>
<p>在閱讀本文之前，建議讀者可以先閱讀 <a href="https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md" target="_blank" rel="external">Angular 1 Style Guide</a> 這篇文章，本文的程式碼基本上遵循該文倡導的風格，除了少數與下一篇參考文章的建議牴觸之外。</p>
<p>本文主要內容則是基於 <a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef" target="_blank" rel="external">Sane, scalable Angular apps are tricky, but not impossible. Lessons learned from PayPal Checkout.</a> 這篇文章，並補充個人觀點。</p>
<h2 id="最佳實務"><a href="#最佳實務" class="headerlink" title="最佳實務"></a>最佳實務</h2><h3 id="不要使用-ng-controller"><a href="#不要使用-ng-controller" class="headerlink" title="不要使用 ng-controller"></a>不要使用 <code>ng-controller</code></h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#0b67" target="_blank" rel="external">Don’t use ng-controller</a></p>
<p>典型的 <code>ng-controller</code> 用法如下：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">ng-controller</span>=<span class="string">&quot;myController&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">strong</span>&gt;</span>&#123;&#123;foo&#125;&#125;<span class="tag">&lt;/<span class="name">strong</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>這有什麼問題呢？</p>
<ul>
<li><code>ng-controller</code> 違反元件封裝的原則：template 未能與 controller 程式碼封裝在一起，而獨自暴露在 html 中。</li>
<li>無法重複使用：基於第一個理由，你的 controller 無法直接重複使用：必須複製 template。</li>
</ul>
<h3 id="不要在路由中指定-controller"><a href="#不要在路由中指定-controller" class="headerlink" title="不要在路由中指定 controller"></a>不要在路由中指定 controller</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#5b61" target="_blank" rel="external">Don’t specify controllers in your routes</a></p>
<p>不論是 AngularJS 內建的 router 或是一般常用的 ui-router，典型的寫法，都是同時指定 <code>template</code> / <code>templateUrl</code> 及 <code>controller</code>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$routeProvider.</span><br><span class="line">  when(<span class="string">&#39;/phones&#39;</span>, &#123;</span><br><span class="line">    <span class="attr">templateUrl</span>: <span class="string">&#39;partials/phone-list.html&#39;</span>,</span><br><span class="line">    <span class="attr">controller</span>: <span class="string">&#39;PhoneListController&#39;</span></span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure>

<p>這有什麼問題呢？</p>
<p>其實跟『不要使用 <code>ng-controller</code>』的狀況類似，都是與封裝有關：</p>
<ul>
<li>若使用 <code>template</code> 來指定，那麼當要重複使用 controller 時，就必須複製該 template，</li>
<li>若使用 <code>templateUrl</code> 來指定，那麼就有可能不小心指定了錯誤的 template 檔案，造成與 controller 不一致的狀況。</li>
</ul>
<h3 id="盡可能元件化-使用-component-取代-ng-controller"><a href="#盡可能元件化-使用-component-取代-ng-controller" class="headerlink" title="盡可能元件化 (使用 component 取代 ng-controller)"></a>盡可能元件化 (使用 component 取代 <code>ng-controller</code>)</h3><p>出處：</p>
<ul>
<li><a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#e211" target="_blank" rel="external">Make everything a directive</a></li>
<li><a href="https://toddmotto.com/exploring-the-angular-1-5-component-method" target="_blank" rel="external">Exploring the Angular 1.5 .component() method</a></li>
</ul>
<p>基於上面兩點，應該將原本可能使用到 <code>ng-controller</code> 的部份，一律改用 component 來寫。</p>
<p>雖然還是可以使用 <a href="https://docs.angularjs.org/api/ng/provider/$compileProvider#directive" target="_blank" rel="external"><code>$compileProvider.directive()</code></a> 來定義 directive，在 AngularJS 1.5 下，則提供了新的 <a href="https://docs.angularjs.org/api/ng/provider/$compileProvider#component" target="_blank" rel="external"><code>component()</code></a> 函數。這是為了讓 AngularJS 1.X 程式移植到 AngularJS 2.0 比較容易，而特地提供，讓開發者以比較接近 AngularJS 2.0 component 寫法的方式來定義元件：</p>
<p><strong>myComponent.html</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">strong</span>&gt;</span>&#123;&#123;vm.foo&#125;&#125;<span class="tag">&lt;/<span class="name">strong</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><strong>myComponent.js</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myTemplate = <span class="built_in">require</span>(<span class="string">&#39;./myComponent.html&#39;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> myComponent = &#123;</span><br><span class="line">    <span class="attr">bindings</span>: &#123;</span><br><span class="line">        <span class="attr">foo</span>: <span class="string">&#39;=&#39;</span>,</span><br><span class="line">        <span class="attr">value</span>: <span class="string">&#39;@&#39;</span>,</span><br><span class="line">        <span class="attr">oneWay</span>: <span class="string">&#39;&lt;&#39;</span>,</span><br><span class="line">        <span class="attr">twoWay</span>: <span class="string">&#39;=&#39;</span>,</span><br><span class="line">        <span class="attr">callback</span>: <span class="string">&#39;&amp;&#39;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">template</span>: myTemplate,</span><br><span class="line">    <span class="attr">controller</span>: MyComponentController,</span><br><span class="line">    <span class="attr">controllerAs</span>: <span class="string">&#39;vm&#39;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">myapp.component(<span class="string">&#39;myComponent&#39;</span>, myComponent);</span><br><span class="line"></span><br><span class="line">MyComponentController.$inject = [<span class="string">&#39;$http&#39;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyComponentController</span>(<span class="params">$http</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.foo = <span class="string">&#39;bar&#39;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意，使用 <code>component()</code> 函數來建立元件時：</p>
<ul>
<li>必然以 <code>restrict: &#39;E&#39;</code> 的形式建立，也就是只能建立 element 形式的元件，</li>
<li>必然以 <code>scope: {}</code> 的形式建立，也就是建立 &quot;isolated scope&quot;，不會繼承 parent scope。因此，若需要外部 scope 的資料，必須明確在 <code>bindings</code> 中定義屬性，並在使用 element tag 時傳入。</li>
<li>承上，在 <code>component()</code> 函數中，已使用 <code>bindings</code> 取代 <code>scope</code> 及在 1.3 ~ 1.4 之間引入的 <code>bindToController</code> 屬性。所以在 <code>component()</code> 函數中，<code>scope</code> 及 <code>bindToController</code> 屬性無效，也不應再使用。</li>
<li>如果你沒有自行指定 <code>controllerAs</code>，則會自動幫你指定為 <code>$ctrl</code>，也就是說，強迫必須使用 <code>controllerAs</code> 語法 (參考後面『永遠使用 &quot;controllerAs&quot; 語法』)。</li>
</ul>
<p>另外，</p>
<ul>
<li>由於 <code>component()</code> 函數只能用來建立 element 元件，如果元件必須同時或者只支援其他類型，譬如 attribute 形式，則必須使用 <code>directive()</code> 函數定義。</li>
<li>上面使用 <code>require()</code> 引入 template 檔案的作法，是採用 Webpack 的作法。實務上可以直接使用 <code>templateUrl</code> 指定外部檔案, 或直接以內聯的形式定義 <code>template</code> 內容。</li>
<li>推薦像上面這樣，將 <code>myComponent</code> 的設定獨立定義，甚至獨立為一個檔案，這樣將來要移轉為 AngularJS 2.0 元件會比較方便。</li>
<li>將 controller 放在後面獨立定義，並以 constructor 的形式，開頭名稱大寫，是 John Papa <a href="https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#named-vs-anonymous-functions" target="_blank" rel="external">推薦的作法</a>。</li>
</ul>
<p>最後，再提醒一下，定義元件時，使用 <code>myComponent</code> 名稱，這名稱就稱為該元件的 &quot;directive name&quot;。首字小寫，這是 AngularJS 定義元件的推薦慣例。(當作是定義 instance 變數，所以小寫開頭。)<br>而在 html 中使用時，則必須使用 <code>my-component</code>，這稱為該元件的 &quot;tag name&quot;。全部小寫，以 <code>-</code> 分隔單字，雖然 HTML 不區分大小寫，但是使用全小寫是推薦的慣例。<br>而 controller 的名稱為 <code>MyComponent</code>，因為它是一個 constructor。首字大寫，表示這是一個 class，這是 JavaScript 的慣例。</p>
<p>一旦定義好 component，就可以如下使用：</p>
<p><strong>在 markup 中使用</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">ng-app</span>=<span class="string">&quot;myApp&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">my-component</span> <span class="attr">foo</span>=<span class="string">&quot;bar&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">my-component</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><strong>在路由中使用</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$routeProvider.</span><br><span class="line">  when(<span class="string">&#39;/phones&#39;</span>, &#123;</span><br><span class="line">    <span class="attr">template</span>: <span class="string">&#39;&lt;my-component foo=&quot;bar&quot;&gt;&lt;/my-component&gt;&#39;</span></span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure>

<p>好處如下：</p>
<ul>
<li>完全封裝，不須再擔心 controller 與 template 不一致，</li>
<li>需要的屬性可以由外部以偏好的方式，以 expression 的形式指定，傳入的是計算後的值，元件內部不再依賴於外部的特定屬性名稱，</li>
<li>如果需要處理事件，也可以在 <code>bindings</code> 中以 <code>eventName: &#39;&amp;&#39;</code> 的方式定義，同樣由外部以偏好的方式指定，元件不需要依賴於外部 scope 的特定方法名稱，</li>
<li>因為是 &quot;isolated scope&quot;，所以不必擔心 scope 之間的資料存取、覆蓋、同步問題。</li>
</ul>
<p><a href="http://teropa.info/blog/2015/10/18/refactoring-angular-apps-to-components.html" target="_blank" rel="external">Refactoring Angular Apps to Component Style</a> 這篇文章詳細介紹各種將 controller 改用 component 的形式撰寫的步驟，相當值得一讀，強烈推薦。</p>
<h3 id="使用-component-定義-element-元件；使用-directive-定義-attribute-directive"><a href="#使用-component-定義-element-元件；使用-directive-定義-attribute-directive" class="headerlink" title="使用 component() 定義 element 元件；使用 directive() 定義 attribute directive"></a>使用 <code>component()</code> 定義 element 元件；使用 <code>directive()</code> 定義 attribute directive</h3><p>建議只要是 element 形式的 directive，都使用 <code>component()</code> 函數來建立。其它情況則使用 <code>directive()</code> 函數。這裡一併列出建議的 element 元件 及 attribute directive 的參考寫法，做為對照。</p>
<ol>
<li><p>定義 component / element directive：</p>
 <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myTemplate = <span class="string">&#39;&lt;div&gt;&lt;/div&gt;&#39;</span>;</span><br><span class="line"><span class="keyword">var</span> myComponent = &#123;</span><br><span class="line">    <span class="attr">bindings</span>: &#123;</span><br><span class="line">        <span class="attr">value</span>: <span class="string">&#39;@&#39;</span>,</span><br><span class="line">        <span class="attr">oneWay</span>: <span class="string">&#39;&lt;&#39;</span>,</span><br><span class="line">        <span class="attr">twoWay</span>: <span class="string">&#39;=&#39;</span>,</span><br><span class="line">        <span class="attr">callback</span>: <span class="string">&#39;&amp;&#39;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">template</span>: myTemplate,</span><br><span class="line">    <span class="attr">controller</span>: MyComponentController,</span><br><span class="line">    <span class="attr">controllerAs</span>: <span class="string">&#39;vm&#39;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">app.component(<span class="string">&#39;myComponent&#39;</span>, myComponent);</span><br><span class="line"></span><br><span class="line">MyComponentController.$inject = [<span class="string">&#39;$http&#39;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyComponentController</span>(<span class="params">$http</span>) </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>定義 attribute directive：</p>
 <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myDirective = &#123;</span><br><span class="line">    <span class="attr">restrict</span>: <span class="string">&#39;A&#39;</span>,</span><br><span class="line">    <span class="attr">scope</span>: &#123;</span><br><span class="line">        <span class="attr">value</span>: <span class="string">&#39;@&#39;</span>,</span><br><span class="line">        <span class="attr">oneWay</span>: <span class="string">&#39;&lt;&#39;</span>,</span><br><span class="line">        <span class="attr">twoWay</span>: <span class="string">&#39;=&#39;</span>,</span><br><span class="line">        <span class="attr">callback</span>: <span class="string">&#39;&amp;&#39;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">link</span>: myDirectiveLinker</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">app.directive(<span class="string">&#39;myDirective&#39;</span>, myDirective);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">myDirectiveLinker</span>(<span class="params">scope, element, attrs</span>) </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ol>
<h3 id="永遠使用-isolated-scope"><a href="#永遠使用-isolated-scope" class="headerlink" title="永遠使用 isolated scope"></a>永遠使用 isolated scope</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#f77d" target="_blank" rel="external">Always use an isolated scope</a></p>
<p>首先，你只有在定義 directive 時，才能指定 scope 種類 (註)。而 scope 有三種：</p>
<ol>
<li><p>isolated scope: <code>scope: {}</code></p>
<p> 建立一個獨立的 scope。要交換的資料必須明確定義。</p>
</li>
<li><p>new scope: <code>scope: true</code></p>
<p> 建立新的 child scope，並且繼承 parent scope。</p>
</li>
<li><p>no scope: <code>scope: false</code></p>
<p> 不建立 scope，直接使用 parent scope。</p>
</li>
</ol>
<p>這有什麼問題呢？</p>
<ul>
<li><p>既然 directive 就是 element 和 attribute (還有 class 和 comment 但是官方不建議使用)，讓它們可以直接存取父元件的資料不是很奇怪嗎？標準 html 的標籤不都是要自行指定屬性？</p>
</li>
<li><p>繼承了 parent scope，也就意謂著依賴於 parent scope，因此元件的可重用性就大幅降低。</p>
</li>
<li><p>因為 scope 是以類似 prototpye 的方式繼承，親子 scope 之間會發生屬性遮蔽問題 (請參考下一段說明)。</p>
</li>
</ul>
<p>因此，強烈建議一律使用 &quot;isolated scope&quot;，不要使用另外兩種。當需要 parent scope 資料時，請明確定義屬性，並由 parent scope 指定傳入。</p>
<p>註：即使在 <code>ng-controller</code> 中使用 &quot;controllerAs&quot; 語法，也只是幫你以指定的名稱建立一個包裹物件而已，scope 類型仍然是 &quot;new scope&quot;，並無法指定 scope 類型。這也是一個不應該使用 <code>ng-controller</code> 的好理由。</p>
<h3 id="永遠將屬性資料以物件加以包裹，或使用-quot-controllerAs-quot-語法"><a href="#永遠將屬性資料以物件加以包裹，或使用-quot-controllerAs-quot-語法" class="headerlink" title="永遠將屬性資料以物件加以包裹，或使用 &quot;controllerAs&quot; 語法"></a>永遠將屬性資料以物件加以包裹，或使用 &quot;controllerAs&quot; 語法</h3><p>出處：</p>
<ul>
<li><a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#838c" target="_blank" rel="external">Only ever bind to sub-properties of an object</a></li>
<li><a href="http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html" target="_blank" rel="external">Exploring Angular 1.3: Binding to Directive Controllers</a></li>
</ul>
<p>只要遵循上面『盡可能元件化 (使用 component 取代 <code>ng-controller</code>)』的原則，即自動 (強制) 獲得使用 &quot;controllerAs&quot; 及 &quot;isolated scope&quot; 的好處。</p>
<p>如果你不需要知道 AngularJS 過去最被詬病的 scope 黑暗歷史，或者，你已經了解什麼是 &quot;controllerAs&quot;，可以放心跳過這一段。</p>
<p>像這樣的寫法：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">ng-model</span>=<span class="string">&quot;username&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure>

<p>其中 <code>username</code> 將直接繫結到上層 scope 的 <code>username</code> 屬性。這裡的關鍵字是『上層 scope』。</p>
<p>這有什麼問題呢？</p>
<ul>
<li><p>由於某些 AngularJS 的 directive，譬如 <code>ngIf</code>，會建立新的 scope，也就是前面的提到的 &quot;new scope&quot;，注意 &quot;new scope&quot; 會繼承到 parent scope。意思是說，你的 binding 將會根據你的 element 所在的位置，因而繼承了你沒有預料到的『上層 scope』，因此而可能有不同的行為。</p>
</li>
<li><p>其次，由於屬性是以 prototype 的方式繼承，一旦 child scope 建立了同名的屬性，就同時遮蔽了 parent scope 的同名屬性。使得原本希望直接更新 parent scope 屬性的意圖落空。</p>
</li>
</ul>
<p>注意，以下範例為了清楚起見，直接使用 <code>ng-controller</code> 建立新的 scope，實務上仍然建議『盡可能元件化 (使用 component 取代 <code>ng-controller</code>)』。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="NNywWd" data-default-tab="result" data-user="amobiz" class="codepen">See the Pen <a href="http://codepen.io/amobiz/pen/NNywWd/" target="_blank" rel="external">AngularJS Scope Problem</a> by amobiz (<a href="http://codepen.io/amobiz" target="_blank" rel="external">@amobiz</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p><p></p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<div class="hidden"><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">ng-app</span>=<span class="string">&quot;app&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h4</span>&gt;</span>Form 1<span class="tag">&lt;/<span class="name">h4</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">ng-controller</span>=<span class="string">&quot;ParentController&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span>&gt;</span>Username: <span class="tag">&lt;/<span class="name">label</span>&gt;</span>&#123;&#123;username&#125;&#125;<span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">ng-model</span>=<span class="string">&quot;username&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">h4</span>&gt;</span>Form 2<span class="tag">&lt;/<span class="name">h4</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">ng-controller</span>=<span class="string">&quot;ParentController&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span>&gt;</span>Username: <span class="tag">&lt;/<span class="name">label</span>&gt;</span>&#123;&#123;username&#125;&#125;<span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">ng-if</span>=<span class="string">&quot;!registered&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">ng-model</span>=<span class="string">&quot;username&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">angular.module(<span class="string">&#39;app&#39;</span>, [])</span><br><span class="line">.controller(<span class="string">&#39;ParentController&#39;</span>, ParentController)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ParentController</span>(<span class="params">$scope</span>) </span>&#123;</span><br><span class="line">    $scope.username = <span class="string">&#39;My Name&#39;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</div>
<p>注意上面 Form 2，當你輸入資料時，ParentController 的 <code>username</code> 並不會同步更新。</p>
<p>為了避開巢狀 scope 的 prototype properties 繼承問題，可以在 parent scope 中以物件的形式將值包裝起來。<br>這樣，在 child scope 中，只要永遠不直接對 scope 設定屬性，就不用擔心 scope 屬性遮蔽問題。而透過該物件存取其屬性，就能自由與 parent scope 交換存取資料。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="oxEowv" data-default-tab="result" data-user="amobiz" class="codepen">See the Pen <a href="http://codepen.io/amobiz/pen/oxEowv/" target="_blank" rel="external">AngularJS Scope via Object</a> by amobiz (<a href="http://codepen.io/amobiz" target="_blank" rel="external">@amobiz</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p><p></p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<div class="hidden"><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">ng-app</span>=<span class="string">&quot;app&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h4</span>&gt;</span>Form 1<span class="tag">&lt;/<span class="name">h4</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">ng-controller</span>=<span class="string">&quot;ParentController&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span>&gt;</span>Username: <span class="tag">&lt;/<span class="name">label</span>&gt;</span>&#123;&#123;vm.username&#125;&#125;<span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">ng-model</span>=<span class="string">&quot;vm.username&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">h4</span>&gt;</span>Form 2<span class="tag">&lt;/<span class="name">h4</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">ng-controller</span>=<span class="string">&quot;ParentController&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span>&gt;</span>Username: <span class="tag">&lt;/<span class="name">label</span>&gt;</span>&#123;&#123;vm.username&#125;&#125;<span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">ng-if</span>=<span class="string">&quot;!vm.registered&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">ng-model</span>=<span class="string">&quot;vm.username&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">angular.module(<span class="string">&#39;app&#39;</span>, [])</span><br><span class="line">.controller(<span class="string">&#39;ParentController&#39;</span>, ParentController)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ParentController</span>(<span class="params">$scope</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> vm = &#123;&#125;;</span><br><span class="line">    vm.username = <span class="string">&#39;My Name&#39;</span>;</span><br><span class="line"></span><br><span class="line">    $scope.vm = vm;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</div>
<p>注意 <code>vm</code> 在這裡是可以任意自訂的名稱，取名 <code>vm</code> 是 &quot;View Model&quot; 的意思。</p>
<h4 id="指定-quot-controllerAs-quot-之後，controller-中的-this-與-scope-有何不同？"><a href="#指定-quot-controllerAs-quot-之後，controller-中的-this-與-scope-有何不同？" class="headerlink" title="指定 &quot;controllerAs&quot; 之後，controller 中的 this 與 $scope 有何不同？"></a>指定 &quot;controllerAs&quot; 之後，controller 中的 this 與 $scope 有何不同？</h4><p>由於上面的作法，可以優雅地解決 scope 遮蔽問題，AngularJS 1.2 針對這個作法，新增了 &quot;controllerAs&quot; 語法。</p>
<p>首先，不推薦，但你可以使用 <code>ng-controller=&quot;MyController as $ctrl&quot;</code> 的方式啟用這個功能；<br>再者，你可以在 <code>directive()</code> 及 <code>component()</code> 函數，使用 <code>controller: &#39;MyController as $ctrl&#39;</code> 的方式啟用，或者以 <code>controllerAs: &#39;$ctrl&#39;</code> 的方式另外指定。</p>
<p>&quot;ControllerAs&quot; 語法，只是上面介紹的作法的語法蜜糖，當你寫 <code>controller as $ctrl</code> 時，AngularJS 在背後所做的事，基本上就跟上面一樣。</p>
<p>細節是這樣的，首先，記住 controller 函數其實是一個 constructor，AngularJS 會以 <code>new</code> 操作子呼叫你的 controller 函數，以建立一個新的物件，然後，AngularJS 會自動幫你在 <code>$scope</code> 中，以 <code>$ctrl</code> 為屬性名稱，將該物件指定給 <code>$scope</code>。直接以程式展示的話，大概是像這樣：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyController</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.username = <span class="string">&#39;My Name&#39;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">$$MyControllerWrapper$$</span>(<span class="params">$scope</span>) </span>&#123;</span><br><span class="line">    $scope.$ctrl = <span class="keyword">new</span> MyController();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>所以，從另一個角度看，在上面的 controller 中的 <code>this</code>，只是一個普通的 JavaScript 物件，當然也就沒有 $scope 的功能。這就解釋了為什麼使用了 &quot;controllerAs&quot; 語法後，就無法在 controller 中使用 $scope 的功能。因此，如果在 controller 中需要 $scope 功能的話，就必須再另外透過 DI 注入 $scope 物件，就像這樣：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">MyController.$inject = [<span class="string">&#39;$scope&#39;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyController</span>(<span class="params">$scope</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.username = <span class="string">&#39;My Name&#39;</span>;</span><br><span class="line"></span><br><span class="line">    $scope.$watch(<span class="string">&#39;registered&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">newValue, oldValue</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="參考資料-延伸閱讀"><a href="#參考資料-延伸閱讀" class="headerlink" title="參考資料 / 延伸閱讀"></a>參考資料 / 延伸閱讀</h4><p>這一篇解釋得很詳細：</p>
<p><a href="http://codetunnel.io/angularjs-controller-as-or-scope/" target="_blank" rel="external">AngularJS: &quot;Controller as&quot; or &quot;$scope&quot;?</a></p>
<p>這一篇說明為何 1.3 需要引進 <code>bindToController</code>，以及後來在 1.4 的演進：</p>
<p><a href="http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html" target="_blank" rel="external">Exploring Angular 1.3: Binding to Directive Controllers</a>。</p>
<p>John Papa 是一直大力推廣 &quot;controllerAs&quot; 語法的人：</p>
<p><a href="http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/" target="_blank" rel="external">Do You Like Your Angular Controllers with or without Sugar?</a><br><a href="http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/" target="_blank" rel="external">AngularJS&#39;s Controller As and the vm Variable</a></p>
<p>然後，AngularJS 1.5 引進了上面介紹的 <code>component()</code> 函數，同時，似乎是為了有所區別，AngularJS team 決定 (沒有找到相關資料，請看下面的推論) 引入新的 <code>bindings</code> 屬性，用來完全取代 <code>scope</code> 及 <code>bindToController</code>。我想這應該是為了避免使用者誤用，而造成模稜兩可的狀況：</p>
<ol>
<li><p>退回到 1.4，強制定義 <code>scope: {}</code>，而使用 <code>bindToController</code> 定義屬性</p>
<p> 因為在 1.4 時，<code>bindToController</code> 屬性就可以直接定義屬性，其意義與 <code>bindings</code> 完全相同，只不過並未強制 <code>scope: {}</code> 的定義。若這麼做的話，等於是期望使用者這樣定義：</p>
 <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">scope</span>: &#123;&#125;,</span><br><span class="line">    <span class="attr">bindToController</span>: &#123;</span><br><span class="line">        <span class="attr">attribute</span>: <span class="string">&#39;=&#39;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 但是若使用者卻使用 1.3 的語法：</p>
 <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">scope</span>: &#123;</span><br><span class="line">        <span class="attr">attribute</span>: <span class="string">&#39;=&#39;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">bindToController</span>: <span class="literal">true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 那麼，哪個屬性有效呢？記住，我們已經強制定義 <code>scope: {}</code>，所以這裡的 <code>scope</code> 定義無效；而我們期望定義為 hash object 的 <code>bindToController</code>，在這裡當然也找不到正確的屬性定義。</p>
</li>
<li><p>退回到 1.3，仍然使用 <code>scope</code> 定義屬性，強制 <code>bindToController: true</code> 屬性。</p>
<p> 也就是說，期望使用者這樣定義：</p>
 <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">scope</span>: &#123;</span><br><span class="line">        <span class="attr">attribute</span>: <span class="string">&#39;=&#39;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">bindToController</span>: <span class="literal">true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 但是若使用者卻使用 1.4 的功能：</p>
 <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">scope</span>: &#123;&#125;,</span><br><span class="line">    <span class="attr">bindToController</span>: &#123;</span><br><span class="line">        <span class="attr">attribute</span>: <span class="string">&#39;=&#39;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 那麼，又是哪個屬性有效呢？同樣地，我們已經強制定義 <code>bindToController: true</code>，所以這裡的 <code>bindToController</code> 定義無效；而我們期望定義為 hash object 的 <code>scope</code>，在這裡當然也找不到正確的屬性定義。</p>
</li>
</ol>
<p>所以，由此推論，AngularJS team 為了避免上述兩種模稜兩可的狀況，而決定乾脆同時忽略 <code>scope</code> 和 <code>bindToController</code> 的使用者定義，分別強制其值為 <code>{}</code> 及 <code>true</code>，而另外使用 <code>bindings</code> 來定義屬性。</p>
<p>雖然不確定理由是否如上述推論，但同時有三個屬性 (<code>scope</code>, <code>bindToController</code>, <code>bindings</code>)，提供一樣的功能，顯然又是一個為了向後相容，而疊床架屋的例子，雖然解決了問題，但同時也造成了更多混淆。</p>
<h3 id="少用-rootScope"><a href="#少用-rootScope" class="headerlink" title="少用 $rootScope"></a>少用 <code>$rootScope</code></h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#5a49" target="_blank" rel="external">Limit your use of $rootScope</a></p>
<p>應該不須贅言，<code>$rootScope</code> 基本上就等於 global。</p>
<h3 id="盡量讓狀態靠近使用的元件"><a href="#盡量讓狀態靠近使用的元件" class="headerlink" title="盡量讓狀態靠近使用的元件"></a>盡量讓狀態靠近使用的元件</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#ce03" target="_blank" rel="external">Keep your state as close as possible to the components which need it.</a></p>
<p>大部分情況下，你的元件應該能自給自足，或者，頂多需要由父元件提供資料。有些時候，你可能需要在多個元件之間共用資料。除非你是在開發通用的元件，譬如 Tab / TabPanel，這時你可能要考慮使用 <a href="https://toddmotto.com/directive-to-directive-communication-with-require/" target="_blank" rel="external"><code>require</code></a> 屬性。否則，開發一般應用程式時，應該盡量在最靠近它們的共同父元件上提供共用的資料。</p>
<p><strong>index.html</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">ng-app</span>=<span class="string">&quot;app&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><strong>parent.js</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> template = [</span><br><span class="line">    <span class="string">&#39;&lt;child-one foo=&quot;vm.bar&quot;&gt;&lt;/child-one&gt;&#39;</span>,</span><br><span class="line">    <span class="string">&#39;&lt;child-two baz=&quot;vm.bar&quot;&gt;&lt;/child-two&gt;&#39;</span></span><br><span class="line">].join(<span class="string">&#39;&#39;</span>);</span><br><span class="line">app.component(<span class="string">&#39;parent&#39;</span>, &#123;</span><br><span class="line">    <span class="attr">template</span>: template,</span><br><span class="line">    <span class="attr">controller</span>: ParentController,</span><br><span class="line">    <span class="attr">controllerAs</span>: <span class="string">&#39;vm&#39;</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ParentController</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.bar = <span class="string">&#39;woo&#39;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>child-one.js</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">app.component(<span class="string">&#39;childOne&#39;</span>, &#123;</span><br><span class="line">    <span class="attr">bindings</span>: &#123;</span><br><span class="line">        <span class="attr">foo</span>: <span class="string">&#39;=&#39;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">controller</span>: ChildOneController,</span><br><span class="line">    <span class="attr">controllerAs</span>: <span class="string">&#39;vm&#39;</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ChildOneController</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.foo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>child-two.js</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">app.component(<span class="string">&#39;childTwo&#39;</span>, &#123;</span><br><span class="line">    <span class="attr">bindings</span>: &#123;</span><br><span class="line">        <span class="attr">baz</span>: <span class="string">&#39;=&#39;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">controller</span>: ChildTwoController,</span><br><span class="line">    <span class="attr">controllerAs</span>: <span class="string">&#39;vm&#39;</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ChildTwoController</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.baz);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="就使用-factory-函數-忘記-service-和-provider-吧"><a href="#就使用-factory-函數-忘記-service-和-provider-吧" class="headerlink" title="就使用 factory() 函數 (忘記 service() 和 provider() 吧)"></a>就使用 <code>factory()</code> 函數 (忘記 <code>service()</code> 和 <code>provider()</code> 吧)</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#ca4a" target="_blank" rel="external">Forget about services and providers</a></p>
<p>直接先講結論：</p>
<p>在 AngularJS 中，能夠透過 DI 注入使用的，都是服務。</p>
<p>一般常在爭論的，所謂 factory, service 及 provider，其實根本都不是真正的主體，它們都只是用來建立服務的方法。</p>
<p>正確的說，我們透過 <code>factory()</code>, <code>service()</code>, <code>provider()</code>，甚至 <code>constant()</code>, <code>value()</code> 這些方法，建立服務。而不論是用哪一個方法建立的服務，它們本質上完全相同，唯一的不同就只有建立的形式不同。</p>
<p>爭論什麼是 factory, service 及 provider，只是讓問題失焦。甚至於，我主張，不應該說『一個 factory』，而應該這麼說：『一個使用 <code>factory()</code> 函數建立的服務』。</p>
<p>如果你了解它們背後的實作方式，全部都是基於同一個其界面具有 <code>$get()</code> 函數的物件，你就可以根據需要，自在地採用適合你的情境的『形式』來建立服務；如果還是不了解，就根據本項建議，全部採用 <code>factory()</code> 來建立服務。</p>
<h4 id="詳細說明"><a href="#詳細說明" class="headerlink" title="詳細說明"></a>詳細說明</h4><p>許多人都建議只使用 factory，主要理由如下：</p>
<ol>
<li>不管是 factory, service 或 provider，其本質上都相同，背後的實作都是 provider，</li>
<li>它們全都是 singleton，</li>
<li>provider 的存在，只是為了提供更改預設值的初始化功能，但其實有許多方式可以做到，並不是非 provider 不可，</li>
<li>factory 比 service 有彈性。</li>
</ol>
<p>我認為以上的說法還不夠精準，最好改為：</p>
<aside class="cheatsheet"><h5>建議主要以 factory() 函數來建立服務</h5><p>因為：</p>
<ol>
<li>不管是透過 <code>factory()</code>, <code>service()</code> 或 <code>provider()</code> 函數建立的服務，其本質上都相同，背後的實作方式也相同，</li>
<li>它們全都是 singleton，都只會在初始化過程中被執行一次，其執行產出結果就是唯一的 singleton，也就是最終被拿來做為注入使用的物件，</li>
<li>provider 的存在，只是為了提供更改預設值的初始化功能，但其實有許多方式可以做到，並不是非 provider 不可， (作法可參考後面的範例)</li>
<li>透過 <code>factory()</code> 函數建立服務，比透過 <code>service()</code> 函數建立服務更有彈性。</li>
</ol>
</aside>
<p>以下透過模擬它們的實作方式，稍微說明一下它們的差異。簡單地說，它們的差別可以用下面這段虛擬碼表達，為了簡單起見，dependency injection 處理的部份完全不列入。</p>
<p>首先，我們建立的 module，具有 (自動注入) 下列 <a href="https://docs.angularjs.org/api/auto/service/$provide" target="_blank" rel="external"><code>$provide</code></a> 提供的 <code>factory()</code>, <code>service()</code> 及 <code>provider()</code> 等函數 (其它略)：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> $provide = &#123;</span><br><span class="line">    <span class="attr">provider</span>: provider,</span><br><span class="line">    <span class="attr">factory</span>: factory,</span><br><span class="line">    <span class="attr">service</span>: service</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> $$providerRegistry = &#123;&#125;;</span><br><span class="line"><span class="keyword">var</span> $$serviceRegistry = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">provider</span>(<span class="params">serviceName, ProviderConstructor</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> serviceProvider = <span class="keyword">new</span> ProviderConstructor();</span><br><span class="line">    <span class="keyword">var</span> serviceProviderName = serviceName + <span class="string">&#39;Provider&#39;</span>;</span><br><span class="line">    $$providerRegistry[ serviceProviderName ] = serviceProvider;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> service = serviceProvider.$get();</span><br><span class="line">    $$serviceRegistry[ serviceName ] = service;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">factory</span>(<span class="params">serviceName, factoryFunction</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> provider(serviceName, <span class="function"><span class="keyword">function</span> <span class="title">ProviderConstructor</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.$get = factoryFunction;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">service</span>(<span class="params">serviceName, ServiceConstructor</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> factory(serviceName, <span class="function"><span class="keyword">function</span> <span class="title">factoryFunction</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ServiceConstructor();</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中 <code>provider()</code> 函數，做了這些事：</p>
<ol>
<li>以 <code>new</code> 運算子呼叫我們提供的 <code>ProviderConstructor()</code> 建構子，建立一個物件，這個物件也就是所謂的 Provider 服務，</li>
<li>將此 Provider 服務物件以 <code>serviceName + &#39;Provider&#39;</code> 的名稱，註冊為一個服務，這個服務將只能透過 <code>config()</code> 函數注入使用，</li>
<li>呼叫此物件的 <code>$get()</code> 方法，此方法回傳的物件才是真正的服務物件，</li>
<li>將服務物件以我們提供的 <code>serviceName</code> 為名稱，註冊為一個服務，這個服務將能夠在 <code>run()</code>, <code>factory()</code>, <code>service()</code>, <code>provider()</code>, <code>controller()</code>, <code>directive()</code> ... 等函數，以及任意建構子中注入使用。</li>
<li>對每個服務而言，上面的過程，在整個應用程式生命週期中，只會執行一次。</li>
</ol>
<p>而 <code>factory()</code> 函數，則是:</p>
<ol>
<li>建立一個 <code>ProviderConstructor()</code> 建構子，以該建構子做為參數呼叫上面的 <code>provider()</code> 函數，</li>
<li><code>provider()</code> 函數以 <code>new</code> 運算子呼叫該 <code>ProviderConstructor()</code> 建構子時，將建立一個具有 <code>$get()</code> 方法的物件，該 <code>$get()</code> 方法直接被設定為我們傳入的 <code>factoryFunction()</code> 函數，因此呼叫 <code>$get()</code> 方法，即等於呼叫 <code>factoryFunction()</code> 函數，</li>
<li>根據上面 <code>provider()</code> 函數的說明， <code>ProviderConstructor()</code> 建構子建立的物件，將被以 <code>serviceName + &#39;Provider&#39;</code> 的名稱，註冊為一個所謂的 Provider 服務，</li>
<li>根據上面 <code>provider()</code> 函數的說明，<code>$get()</code> 方法被呼叫，於是 <code>factoryFunction()</code> 函數回傳的任意值，將以我們提供的 <code>serviceName</code> 為名稱，註冊為一個服務，</li>
<li>對每個服務而言，上面的過程，在整個應用程式生命週期中，只會執行一次。</li>
</ol>
<p>而 <code>service()</code> 函數，則是:</p>
<ol>
<li>建立一個 <code>factoryFunction()</code> 函數，以該函數做為參數呼叫上面的 <code>factory()</code> 函數，</li>
<li>在剛剛的 <code>factoryFunction()</code> 函數中，以 <code>new</code> 運算子呼叫傳入的 <code>ServiceConstructor</code> 建構子，建立服務物件，並將其返回，</li>
<li>根據上面 <code>factory()</code> 函數的說明，<code>factory()</code> 函數又會轉呼叫 <code>provider()</code> 函數，</li>
<li><code>factoryFunction()</code> 函數返回的物件，也就是 <code>ServiceConstructor</code> 建構子建立的物件，最終被以 <code>serviceName</code> 為名稱，註冊為服務，</li>
<li>同時，會有一個僅包含 <code>$get()</code> 方法的物件，被以 <code>serviceName + &#39;Provider&#39;</code> 的名稱，註冊為所謂的 Provider 服務。</li>
<li>對每個服務而言，上面的過程，在整個應用程式生命週期中，只會執行一次。</li>
</ol>
<p>解釋起來似乎很麻煩，但是直接看程式碼，其實真的沒有什麼驚奇的地方。</p>
<p>重點有兩個：</p>
<ol>
<li>每個服務，實際上都會伴隨著一個 Provider 服務，但只有透過 <code>provider()</code> 函數來建立，該 Provider 服務才有機會附加其他額外方法，而 <code>factory()</code> 和 <code>service()</code> 函數建立的 Provider 服務，都只會有 <code>$get()</code> 方法，</li>
<li>它們全部都只有在註冊的時候，被執行唯一的一次，其結果就被儲存下來成為 singleton，再也沒有機會改變。</li>
<li>不管在哪個模組註冊，註冊的服務都是全域共用的。</li>
</ol>
<p>我過去一直以為 <code>factory()</code> 函數與 <code>service()</code> 函數的差異是，它每次被要求注入的時候都會執行一次，畢竟它的名稱叫做 &quot;factory&quot;，而且又只是個普通函數，所以我一直以為它就是 &quot;factory method&quot;，應該每次都回傳新的物件。但事實上它卻不是這樣被設計的，它跟 使用 <code>service()</code> 函數來建立服務完全一樣，我們傳入的 factory function 總是只被呼叫一次。</p>
<p>我從來沒有在任何一篇文章中看過有人強調這一點，最多只說它們全部都是 singleton。甚至大部分的文章在描述的時候，反而讓人誤以為每次都會呼叫 <code>$get()</code> 函數，譬如，在 <a href="http://blog.thoughtram.io/angular/2015/07/07/service-vs-factory-once-and-for-all.html#the-difference-between-services-and-factories" target="_blank" rel="external">Service vs Factory - Once and for all</a> 這篇文章中這樣描述：</p>
<aside class="cheatsheet"><h5>Service vs Factory - Once and for all</h5><p>It takes the name and the factory function that is passed and basically returns a provider with the same name, that has a $get method which is our factory function. So what is it with this provider thing? Well, whenever you ask the injector for a specific dependency, it basically asks the corresponding provider for an instance of that service, by calling the $get() method. That’s why $get() is required, when creating providers. In other words, if we inject MyService somewhere, what happens behind the scenes is:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyServiceProvider.$get(); <span class="comment">// return the instance of the service</span></span><br></pre></td></tr></table></figure>
</aside>
<p>大概的意思就是說，每次我們向 injector 要求時，就會呼叫 provider 的 <code>$get()</code> 函數。</p>
<p>錯錯錯！ <code>$get()</code> 函數永遠只會被呼叫一次！</p>
<p>正是因為只會被呼叫一次，並且其結果被儲存下來，所以不論是透過 <code>factory()</code>, <code>service()</code> 或 <code>provider()</code> 函數，實際上建立的服務都是 singleton，建立之後的行為完全一樣，<strong>它們之間真正的差別就是建立的形式不同</strong>。</p>
<p>AngularJS team 真的是把一件簡單的事情極度複雜化了，而且明明同樣都是服務，卻用了三種不同的方式來建立，又偏偏取了很不好的一組名稱，才會導致連他們自己人，都會在所謂的解惑文中，用不正確的方式描述其行為。</p>
<p>由下面的範例就可以看出來 (請打開 console 看 log)：</p>
<ol>
<li>雖然建立的是 <code>personFactory</code>，但是卻同時存在有 <code>personFactoryProvider</code>，</li>
<li>console.log 中，&quot;personFactory begin&quot; 與 &quot;personFactory end&quot; 各只出現一次，表示 factory 函數只被呼叫一次，</li>
<li><code>personFactory.rtti</code> 的值，在兩個 controller 中相同，表示 <code>personFactory</code> 的確是同一個物件，</li>
<li>由 <code>PersonController1</code> 透過 <code>personFactory.defaults()</code> 函數設定的值，在 <code>PersonController2</code> 可以看得到。</li>
</ol>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="YqaGJr" data-default-tab="result" data-user="amobiz" class="codepen">See the Pen <a href="https://codepen.io/amobiz/pen/YqaGJr/" target="_blank" rel="external">Angular: factory function only got called once</a> by amobiz (<a href="http://codepen.io/amobiz" target="_blank" rel="external">@amobiz</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p><p></p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>了解背後的運作原理之後，再來看看實際上要建立服務時，我們如何透過 <code>factory()</code>, <code>service()</code> 或 <code>provider()</code> 函數來建立。</p>
<p>基本定義方式如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">angular.module(<span class="string">&#39;app&#39;</span>, [])</span><br><span class="line">.provider(<span class="string">&#39;myProvider&#39;</span>, <span class="function"><span class="keyword">function</span> <span class="title">ProviderConstructor</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.$get = <span class="function"><span class="keyword">function</span> <span class="title">getter</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#39;whatever&#39;</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">this</span>.config = <span class="function"><span class="keyword">function</span> <span class="title">config</span>(<span class="params">options</span>) </span>&#123;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br><span class="line">.service(<span class="string">&#39;myService&#39;</span>, <span class="function"><span class="keyword">function</span> <span class="title">ServiceConstructor</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.method = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br><span class="line">.factory(<span class="string">&#39;myFactory&#39;</span>, <span class="function"><span class="keyword">function</span> <span class="title">factoryFunction</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#39;whatever&#39;</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>雖然透過 <code>provider()</code> 函數建立服務的彈性最大，也能提供設定功能，但是如同前面的範例的 <code>defaults()</code> 函數，設定功能有很多方式可以達成，因此並沒有非使用 <code>provider()</code> 函數不可的理由。而 <code>service()</code> 函數由於強制必須提供 constructor，所以服務本身只能是物件類型，彈性上多少受到一些限制。基於這些理由，多數的人才會建議：一律使用 <code>factory()</code> 函數來建立服務。</p>
<h4 id="使用-factory-函數來建立服務的常見用法"><a href="#使用-factory-函數來建立服務的常見用法" class="headerlink" title="使用 factory() 函數來建立服務的常見用法"></a>使用 <code>factory()</code> 函數來建立服務的常見用法</h4><p><strong>回傳常數</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">.factory(<span class="string">&#39;pi&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">3.14159</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">use.$inject = [<span class="string">&#39;pi&#39;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">use</span>(<span class="params">pi</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> area = pi * <span class="keyword">this</span>.radius * <span class="keyword">this</span>.radius;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>這個作法足以取代 <a href="https://docs.angularjs.org/api/auto/service/$provide#constant" target="_blank" rel="external"><code>constant()</code></a>, <a href="https://docs.angularjs.org/api/auto/service/$provide#value" target="_blank" rel="external"><code>.value()</code></a>。</p>
<p><strong>回傳服務物件</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">app.factory(<span class="string">&#39;personFactory&#39;</span>, personFactory);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, attributes</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">    <span class="keyword">this</span>.attributes = attributes;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.eat = <span class="function"><span class="keyword">function</span> (<span class="params">what</span>) </span>&#123;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">personFactory</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> defaults = &#123;</span><br><span class="line">        <span class="attr">gender</span>: <span class="string">&#39;unspecified&#39;</span></span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="attr">setDefaults</span>: <span class="function"><span class="keyword">function</span> (<span class="params">options</span>) </span>&#123;</span><br><span class="line">            <span class="built_in">Object</span>.assign(defaults, options);</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">create</span>: <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> Person(name, <span class="built_in">Object</span>.assign(&#123;&#125;, defaults));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">app.run(run)</span><br><span class="line">.controller(<span class="string">&#39;PersonController&#39;</span>, PersonController);</span><br><span class="line"></span><br><span class="line">run.$inject = [<span class="string">&#39;personFactory&#39;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">run</span>(<span class="params">personFactory</span>) </span>&#123;</span><br><span class="line">    personFactory.setDefault(&#123;</span><br><span class="line">        <span class="attr">country</span>: <span class="string">&#39;Taiwan&#39;</span></span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">PersonController.$inject = [<span class="string">&#39;personFactory&#39;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">PersonController</span>(<span class="params">personFactory</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> person = personFactory.create(<span class="string">&#39;John&#39;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到：</p>
<ul>
<li>可以提供 factory method，如上面的 <code>create()</code> 函數。最重要的是，它能夠接受參數。</li>
<li>可以提供 config 功能，如上面的 <code>setDefaults()</code> 函數，足以取代 provider。</li>
</ul>
<p><strong>回傳建構子</strong></p>
<p>老實說，我比較不建議這個作法。使用 DI，就是不希望依賴於實作，不希望由 client 自行建構依賴物件。使用 DI 應該直接取得立即可用的物件或服務，應該依賴於其界面，而不是其實作。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">.factory(<span class="string">&#39;Person&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Person.prototype.eat = <span class="function"><span class="keyword">function</span> (<span class="params">what</span>) </span>&#123;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> Person;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">use.$inject = [<span class="string">&#39;Person&#39;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">use</span>(<span class="params">Person</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> person = <span class="keyword">new</span> Person();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="不要使用-module-config"><a href="#不要使用-module-config" class="headerlink" title="不要使用 module.config"></a>不要使用 module.config</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#a583" target="_blank" rel="external">Forget about module.config</a></p>
<p>大多數時候，<code>module.run()</code> 函數即可勝任模組的初始化工作，沒有必要非在 <code>module.config()</code> 中進行初始化。許多時候，基於封裝原則，甚至於應該在元件中進行初始化。</p>
<p>唯一的例外，就是必須使用到 AngularJS 或第三方程式庫提供的 Provider 的時候，譬如 <code>$routeProvider</code>。這是因為 AngularJS 做了一個很不好的決定：強制 Provider 只能在 <code>module.config()</code> 中使用。你在開發自己的應用程式時，實在沒有必要跟著這麼做。</p>
<h3 id="謹慎使用-scope-事件"><a href="#謹慎使用-scope-事件" class="headerlink" title="謹慎使用 scope 事件"></a>謹慎使用 scope 事件</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#fb87" target="_blank" rel="external">Be careful with event publishing and listening</a></p>
<p>透過 <code>$rootScope</code> 或 <code>$scope</code> 發佈事件，應該是針對不特定受眾所為，通常是設計 API 時才需要考慮。多數時候，你只是在開發應用程式，所有的元件都在你的掌握之中，你可以透過元件之間的屬性，或 callback 函數來傳遞資料。如果是服務，則應該考慮使用 Promise。如果隨意濫用 <code>$rootScope</code> 或 <code>$scope</code> 發佈事件，小心導致程式難以除錯的風險。</p>
<h3 id="善加使用-exceptionHandler"><a href="#善加使用-exceptionHandler" class="headerlink" title="善加使用 $exceptionHandler"></a>善加使用 <code>$exceptionHandler</code></h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#7461" target="_blank" rel="external">Take advantage of $exceptionHandler</a></p>
<p>你的程式中未捕獲的 exception 都會丟給 <a href="https://docs.angularjs.org/api/ng/service/$exceptionHandler" target="_blank" rel="external"><code>$exceptionHandler</code></a> 這個服務處理，這個服務預設的行為是輸出 log。你可以定義自己的 <code>$exceptionHandler</code> 服務，這將覆蓋原本的服務，讓你有機會自行處理錯誤：也許是上傳錯誤到後端伺服器進行記錄等等。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">angular.module(<span class="string">&#39;exceptionOverride&#39;</span>, []).factory(<span class="string">&#39;$exceptionHandler&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">exception, cause</span>) </span>&#123;</span><br><span class="line">        exception.message += <span class="string">&#39; (caused by &quot;&#39;</span> + cause + <span class="string">&#39;&quot;)&#39;</span>;</span><br><span class="line">        <span class="keyword">throw</span> exception;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="找到合適的方法上傳-log-到後端伺服器"><a href="#找到合適的方法上傳-log-到後端伺服器" class="headerlink" title="找到合適的方法上傳 log 到後端伺服器"></a>找到合適的方法上傳 log 到後端伺服器</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#b375" target="_blank" rel="external">Find a good way to publish logs to the server side</a></p>
<p>SPA 應用程式是在使用者的瀏覽器中執行，如果沒有適當地記錄 log 訊息，根本無從得知在使用者的瀏覽器中到底發生了什麼事。</p>
<p>最底限至少應該在應用程式發生錯誤時，輸出錯誤訊息，請求使用者幫忙回報。若經過使用者同意，也可以考慮記錄整個執行過程，排除敏感資料，上傳到後端伺服器加以記錄分析。</p>
<p>上傳的時候應該盡力減少發送次數及流量，並且務必做好加密的動作。</p>
<p>另外，可以考慮使用商業化的產品，譬如 <a href="https://www.loggly.com/" target="_blank" rel="external">Loggy</a>、<a href="https://papertrailapp.com/" target="_blank" rel="external">Paper Trail</a> 等。</p>
<h3 id="使用-angular-ui-router"><a href="#使用-angular-ui-router" class="headerlink" title="使用 angular-ui-router"></a>使用 angular-ui-router</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#92ad" target="_blank" rel="external">Use angular-ui-router</a></p>
<p>如果你的服務 API 遵循 RESTful 原則，你可能會有許多 resources 具有 sub-resources，而 RESTful 要求每個 resource 應該要有唯一的 URL。然而 AngularJS 內建的 <code>ngRoute</code> 不支援巢狀路由，因此並不算 RESTful 友好。</p>
<p>稍具規模，支援 RESTful 的 AngularJS 1.X 應用程式，都應該考慮使用 <a href="https://github.com/angular-ui/ui-router" target="_blank" rel="external">ui-router</a>，甚至可以考慮搭配 <a href="https://github.com/christopherthielen/ui-router-extras" target="_blank" rel="external">ui-router-extras</a>，以提供更多進階功能。</p>
<p>為什麼不使用新的 AngularJS 2.0 的 <a href="https://github.com/angular/router" target="_blank" rel="external">ngComponentRouter</a>？</p>
<p>目前不推薦 ngComponentRouter 的主要原因，是因為：</p>
<ol>
<li><p>尚未正式發佈</p>
<p> ngComponentRouter 原本計畫要隨同在 AngularJS 1.4 發佈，但是現在已 1.5 卻還是沒有正式發佈，想必 AngularJS team 也還有其他考量。或許只是因為 AngularJS 2.0 尚未正式發佈，而針對 AngularJS 1.x 的規格也仍然還在變動。</p>
</li>
<li><p>目前的參考文件比較缺乏。</p>
<p> 最新的文件可以參考 <a href="https://docs.angularjs.org/guide/component-router" target="_blank" rel="external">Component Router</a>。其它目前在網路上可以找到的針對 AngularJS 1.x 的文章幾乎全部都已經過時，都是還是使用 controller 的方式。建議可以參考 StackOverflow 上 <a href="http://stackoverflow.com/a/33669885/726650" target="_blank" rel="external">Angular 1.5 and new Component Router</a> 的回答。或直接參考 ngComponentRouter 的 <a href="https://github.com/angular/router/tree/master/examples" target="_blank" rel="external">examples</a>。另外，我也寫了一個 sample 可以參考：</p>
</li>
</ol>
<p></p><p data-height="266" data-theme-id="0" data-slug-hash="bpvOKj" data-default-tab="result" data-user="amobiz" data-embed-version="2" class="codepen">See the Pen <a href="http://codepen.io/amobiz/pen/bpvOKj/" target="_blank" rel="external">Angular 1.5 component router playground</a> by amobiz (<a href="http://codepen.io/amobiz" target="_blank" rel="external">@amobiz</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p><p></p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<h3 id="小心使用-Promise-並且注意-AngularJS-對-Promise-錯誤的特殊處理"><a href="#小心使用-Promise-並且注意-AngularJS-對-Promise-錯誤的特殊處理" class="headerlink" title="小心使用 Promise 並且注意 AngularJS 對 Promise 錯誤的特殊處理"></a>小心使用 Promise 並且注意 AngularJS 對 Promise 錯誤的特殊處理</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#933e" target="_blank" rel="external">Be careful with promises and error handling</a></p>
<p>首先，不論是透過 AngularJS 自己的 <a href="https://docs.angularjs.org/api/ng/service/$q" target="_blank" rel="external"><code>$q</code></a> 提供的 Promise，或是標準的 <a href="https://promisesaplus.com/" target="_blank" rel="external">Promise/A+</a>，如果你沒有使用 <code>catch()</code> 捕捉錯誤，任何錯誤，包括 throw 的錯誤以及沒有 fulfilled 的錯誤，都將無聲無息地被吃掉。</p>
<p>再來，除非你是透過 <code>return $q.reject(err)</code> 回傳錯誤，否則任何其它被 throw 出來的錯誤，即使你已經使用 <code>catch()</code> 捕捉了，它們都還是會被丟到前面提到的 <code>$exceptionHandler</code> 服務。這就面臨了兩難的困境。意謂著，如果你使用 <code>catch()</code> 捕捉錯誤，同時也使用 <code>$exceptionHandler</code> 服務處理未捕捉的錯誤，結果是同一個錯誤你將處理兩次。</p>
<p>為了避免這個問題，可以借用 Java 的 <a href="http://www.slideshare.net/teddysoft/javacheckedunchecked-exceptions" target="_blank" rel="external">checked exception</a> 的概念：屬於你的設計中，明確會丟出來的錯誤，把它們當作是 checked exception，一律使用 <code>$q.reject(err)</code> 來處理，這樣它們就不會進入到 <code>$exceptionHandler</code> 服務；至於哪些你沒有預料到，或者是程式設計錯誤造成的錯誤，則把它們當作是 unchecked exception，讓它們由 <code>$exceptionHandler</code> 服務來把捕捉。</p>
<h3 id="避免使用延遲載入"><a href="#避免使用延遲載入" class="headerlink" title="避免使用延遲載入"></a>避免使用延遲載入</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#71b6" target="_blank" rel="external">Try to avoid lazy loading</a></p>
<p>AngularJS 1.x 的模組，只能在啟動階段定義，使用任何的 hack，即使是透過路由來做延遲載入，都無可避免的有其缺陷，更不用說這會無謂地增加程式的複雜度。</p>
<h3 id="了解-digest-循環的判斷方式"><a href="#了解-digest-循環的判斷方式" class="headerlink" title="了解 $digest 循環的判斷方式"></a>了解 $digest 循環的判斷方式</h3><p>出處：<a href="https://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-lessons-learned-from-paypal-checkout-c5320558d4ef#64a1" target="_blank" rel="external">Be careful with the digest cycle</a></p>
<p>在 AngularJS 1.x 中，所謂的雙向資料繫結 (two way data binding)，是靠著不斷地檢查 scope 中的變數是否有變動，而在變動時做出反應：更新畫面或根據使用者輸入更新 scope 資料。由於資料之間可能有關聯性，所以某些資料更新後，可能會導致其它資料也需要更新，也就是發生所謂的漣漪效應。因此，AngularJS 會持續地進行檢查、更新，直到資料不再發生變動為止。如果資料持續不斷發生變動，則 <a href="https://github.com/angular/angular.js/blob/fa79eaa816aa27c6d1b3c084b8372f9c17c8d5a3/src/ng/rootScope.js#L39" target="_blank" rel="external">10 次</a>  $digest loop 之後，AngularJS 就會丟出 exception。</p>
<p>AngularJS 如何檢查資料是否發生異動？它並不是靠傳說中的 <code>Object.observe()</code>(註)，而是藉由儲存舊值，透過比對舊值與現值，來判斷是否發生異動。然而，這裡存在著一個陷阱：由於資料可以經由函數回傳，如果函數將資料包裹在物件或陣列中回傳，雖然就『值』本身來看，並沒有發生異動，實際的包裹物件已經不是同一個了。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$scope.foo = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">bar</span>: <span class="string">&#39;baz&#39;</span> &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>JavaScript 並不像 Java 一樣，有所謂 <code>equals()</code> 和 <code>hashCode()</code> 方法，用來快速檢查兩個物件的『值』是否相等，也就是所謂的『等價』。因此，為了效率考量，AngularJS 並不會進行所謂 <code>deepEqual()</code> 檢查，而只是檢查物件是不是同一個 (位址相同)。由於每次要比對現值時，就會呼叫一次函數，而函數每次都會回傳一個新的包裹物件，因此每次都判定為資料發生異動，於是超過 10 次之後，就丟出錯誤了。</p>
<p>因此，上面的例子比較好的作法，應該像這樣：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> foo = &#123; <span class="attr">bar</span>: <span class="string">&#39;baz&#39;</span> &#125;;</span><br><span class="line"></span><br><span class="line">$scope.foo = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> foo;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>這樣每次被呼叫時，回傳的都是同一個物件，AngularJS 就不會誤判了。</p>
<p>註：<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe" target="_blank" rel="external"><code>Object.observe()</code></a> 提案已經於 2015 年 11 月<a href="https://esdiscuss.org/topic/an-update-on-object-observe" target="_blank" rel="external">撤銷</a>，因此未來也不可能使用 <code>Object.observe()</code> 來檢查資料異動。</p>
<h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><p>AngularJS 可以算是最早引入自訂 html 元件概念的 framework，只是早期的推廣，強調 <code>ng-controller</code> 的使用方便性，導致許許多多程式將業務邏輯與畫面呈現全部混在 controller 中。雖然 AngularJS 2.0 發佈在即，也更趨近於 Web Component 標準，舊有使用 AngularJS 1.x 的應用程式，只要嚴守元件化的原則，仍然可以寫出相當容易維護的程式，甚至於更有利於將來移植到 AngularJS 2.0。</p>
<p>以上整理，已盡力將參考資料完整列出，惟個人學識有限，如有謬誤或不足之處，歡迎補正，謝謝。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;a href=&quot;https://angularjs.org/&quot;&gt;&lt;br&gt;&lt;img src=&quot;https://angularjs.org/img/AngularJS-large.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文整理截至目前 AngularJS 1.5 為止， 個人以為的最佳實務做法。&lt;/p&gt;
&lt;p&gt;為什麼現在？基於以下三點理由：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;雖然 AngularJS 2.0 發佈在即，但是既有的 AngularJS 1.x 程式碼仍需要維護。&lt;/li&gt;
&lt;li&gt;由於 AngularJS 1.x 受到廣泛的歡迎，前後做了不少的改進，因此網路上到處可見其實已經過時的作法。&lt;/li&gt;
&lt;li&gt;基於向後相容的原則，有些功能雖然被保留下來，但未必是最佳作法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;『你可以這麼做，不代表你必須這麼做』，基於『JavaScript: The Good Parts』的哲理，本文適合具有 AngularJS 1.x 實務開發經驗，然而卻隨著 AngularJS 的發展，逐漸對於網路上充斥各種主觀、矛盾的說法、用法感到困惑的開發者。&lt;/p&gt;
&lt;p&gt;雖然遵循本文提出的原則，可以以趨近於元件化的風格開發 AngularJS 1.5 應用程式，但本文不涉及 AngularJS 1.x 移轉到 AngularJS 2.0 議題。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="AngularJS" scheme="https://amobiz.github.io/tags/AngularJS/"/>
    
      <category term="best practices" scheme="https://amobiz.github.io/tags/best-practices/"/>
    
  </entry>
  
  <entry>
    <title>反駁：NPM &amp; left-pad: Have We Forgotten How To Program?</title>
    <link href="https://amobiz.github.io/2016/04/07/re-npm-left-pad-have-we-forgotten-how-to-program/"/>
    <id>https://amobiz.github.io/2016/04/07/re-npm-left-pad-have-we-forgotten-how-to-program/</id>
    <published>2016-04-06T16:00:00.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1434871619871-1f315a50efba?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>JavaScript 社群 &lt; 百家爭鳴 &lt; 車廠＋車廂<br>黎明？黃昏？</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/@vaccinium">Dmitrii Vaccinium</a></div></div></div></div>
<p><a href="http://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code/" target="_blank" rel="external">前一陣子</a>，<a href="https://github.com/azer/left-pad" target="_blank" rel="external"><code>left-pad</code></a> 的作者，因為不滿 npmjs.com 屈從財團的壓力，未經作者同意就擅自移轉所有權，因此憤而將全部模組從 npm 下架，而導致眾多知名模組受到波及。此事件引起了廣泛的反思。</p>
<p>然而，漸漸出現一種聲音，開始呼籲大家不要使用 open source、要盡量自己寫程式碼，不要依賴別人的模組，就像這篇文章：『<a href="http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/" target="_blank" rel="external">NPM &amp; left-pad: Have We Forgotten How To Program?</a>』(<a href="http://qianduan.guru/2016/04/04/npm-left-pad-have-we-forgotten-how-to-program/" target="_blank" rel="external">中譯</a>)。更有甚者，連『懂得怎麼寫程式之前，別去玩 node.js 好嗎？』這樣的話都出來了。</p>
<a id="more"></a>
<h3 id="走火入魔？"><a href="#走火入魔？" class="headerlink" title="走火入魔？"></a>走火入魔？</h3><p>首先，該文作者提到，整個事件的源頭，<code>left-pad</code> 只有 11 個星星 (話說現在已經有 710 顆星星)，只有 11 行程式碼。整個產業卻因 11  行程式碼而崩潰實在不可思議。</p>
<p>我還是要說，造成崩潰的並不是這 11 行程式碼，這 11 行程式碼並沒有造成任何程式執行錯誤，這 11 行程式碼沒有品質問題；造成崩潰的是這 11 行程式碼，掌握在不遵守 open source 遊戲規則的 npmjs.com 手中；造成崩潰的是，整個產業的運作，竟是依賴在這樣一家公司上。(說的有點重，但是實在是無法接受傾斜於財團而欺壓獨立開發者的行為。)</p>
<p>舉 Internet 域名為例，對於那些域名已經被搶註冊的大企業，取回域名的最快手段，就是透過收購的方式，打官司也可行，但曠日費時，而且不保證一定勝訴。同樣的邏輯，open source 軟體名稱，憑什麼企業就有權強制他人不許用？</p>
<p>Npm 事件的問題，是整個 ecosystem 『把持』在少數特定企業而衍生的危機，解法應該是盡可能的分散化，不要依賴於『中央集權』。就像 Java 現在把持在 Oracle 手中，遲遲不肯實現 open source 的承諾而遭人唾棄一樣。</p>
<p>這跟 open source 如何演化，跟程式設計師會不會寫程式，根本毫無關係。</p>
<p>作者提到：</p>
<blockquote>
<p>There’s a package called isArray that has 880,000 downloads a day, and 18 million downloads in February of 2016. It has 72 dependent NPM packages. Here’s its entire 1 line of code:<br>1<br><code>return toString.call(arr) == &#39;[object Array]&#39;;</code></p>
</blockquote>
<p>作者在文中就提到，依賴於 <code>isArray()</code> 的模組，有 72 個。只有 72 個相依性，竟然有人可以稱之為『瘋狂的引用他人的模組』、『走火入魔』。</p>
<h4 id="模組下載數量怎麼算？"><a href="#模組下載數量怎麼算？" class="headerlink" title="模組下載數量怎麼算？"></a>模組下載數量怎麼算？</h4><p>那『18 million downloads in February of 2016』又怎麼說呢，大家不是在『瘋狂下載』？</p>
<p>大家真的知道 npm 的 download 數怎麼計算的嗎？</p>
<p>以我的 <a href="https://www.npmjs.com/package/globjoin" target="_blank" rel="external">globjoin</a> 為例，只不過僥倖被 <a href="https://github.com/stylelint/stylelint" target="_blank" rel="external">stylelint</a> 一個模組引用，現在每個月都有超過三萬次下載，請問，有人瘋狂地引用我的模組嗎？沒有，只有一個 stylelint 引用！三萬次下載你以為就有三萬個不重複使用者嗎？也不是，每次模組更新，就會重新下載，合併計算。</p>
<p>作者舉的 <a href="https://www.npmjs.com/package/isarray" target="_blank" rel="external"><code>isArray</code></a> 例子，大家可以去看看它的<a href="https://www.npmjs.com/browse/depended/isarray" target="_blank" rel="external">相依模組</a>，其中不乏像 <a href="https://www.npmjs.com/package/browserify" target="_blank" rel="external">browserify</a>、<a href="https://www.npmjs.com/package/buffer" target="_blank" rel="external">buffer</a> 這樣的重量級模組。我可以說，<code>isArray()</code> 的超高下載量，跟我的 globjoin 的現象相同 (當然等級不同)，只是被某些知名的模組引用了。並沒有人『瘋狂的引用他人的模組』，好嗎？！</p>
<p>至於另一個例子 <a href="https://www.npmjs.com/package/is-positive-integer" target="_blank" rel="external"><code>is-positive-integer</code></a>，『檢查一個整數是否是正數』這個模組的存在本身，的確符合作者的理論。但是由這個模組的實際下載量根本不高來看，更只能證明，顯然絕大多數的開發者並不會貿然使用毫無意義的模組。沒有人使用的模組，就算它依賴於兩萬個模組，影響也極有限。特地拿一個沒有人使用的模組來指責全體開發者，有何意義？</p>
<h4 id="一部分要歸咎於歷史因素"><a href="#一部分要歸咎於歷史因素" class="headerlink" title="一部分要歸咎於歷史因素"></a>一部分要歸咎於歷史因素</h4><p>至於像 <code>isArray</code> 這樣的函數的存在，正是因為過去並沒有原生的函數可以使用，而導致每個人都要自己寫一個系統本應該要有的功能。這一點在英文原文下面的討論，是一個攻防重點，大家不妨去看看。</p>
<p>回頭看看 <code>isArray()</code> 的實現：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> toString.call(arr) == <span class="string">&#39;[object Array]&#39;</span></span><br></pre></td></tr></table></figure>

<p>沒錯，只有短短的一行。但是捫心自問，不參考任何文件、資料，你隨時都寫的出來嗎？<br>如果你每次<strong>又要再寫一遍</strong>的時候，都還需要翻找文件，這樣真的會比較快嗎？</p>
<h4 id="再來談談封裝"><a href="#再來談談封裝" class="headerlink" title="再來談談封裝"></a>再來談談封裝</h4><p>如果程式中到處充滿 <code>return toString.call(arr) == &#39;[object Array]&#39;</code>，你就必須直接看到程式碼的細節，而無法像 Bob 大叔在『<a href="http://www.books.com.tw/products/0010579897" target="_blank" rel="external">Clean Code</a>』中所說的，程式碼應該要『讓名稱代表意圖』。於是，你還是會把它封裝成 <code>isArray()</code>。封裝之後，請問你會把它放在哪裡？</p>
<p><strong>某個模組裡！</strong></p>
<p>這麼一來，跟直接引用別人的模組，又有何不同，只不過是自己寫的就會比較好看？就不存在模組相依性問題？</p>
<p>不要忘了，別人還寫好了測試程式。</p>
<p>沒有測試程式的程式碼，就是『遺留程式碼』。</p>
<p>你為了不要『相依』別人的模組，自幹了自己的模組，然後不寫測試？好吧，你會寫測試，那麼，這就不是『一行程式碼』的問題了。從頭到尾，你只是在重複別人已經處理好的問題。</p>
<h3 id="『Have-We-Forgotten-How-To-Program-』"><a href="#『Have-We-Forgotten-How-To-Program-』" class="headerlink" title="『Have We Forgotten How To Program?』"></a>『Have We Forgotten How To Program?』</h3><p>作者提到，一個簡單的『blank project template』就要引用 28,000 個檔案，這是不是太過複雜了？甚至於直接結論：</p>
<blockquote>
<p>I get the impression that the NPM ecosystem participants have created a fetish for micro-packages.</p>
</blockquote>
<p>『開發者有一種不良的、對微型模塊的熱衷。』</p>
<blockquote>
<p>It feels to me as if the entire job of an NPM-participating developer is writing the smallest amount of code possible to string existing library calls together in order to create something new that functions uniquely for their personal or business need.</p>
</blockquote>
<p>只想『拼拼湊湊、串起來交差』。</p>
<p>先不論這 28,000 個檔案，究竟實際被專案引用的有多少，而用來輔助專案開發的比例又有多少。如果一個專案實際上就是有這些需求，那麼這些檔案，不論改用何種方式撰寫，它們的複雜性並不會因此降低，並不是把 100 個函數，全部寫在一起，變成為一個函數，它就變得不複雜了；事實上剛好相反。</p>
<p>過去開發者不斷地重新發明輪子，而軟體專家則不斷地疾呼要 reuse。終於現在 JavaScript 社群蓬勃發展，大家支持 open source，信任 open source 反而變成『不良』的習慣？</p>
<h3 id="『Functions-Are-Not-Packages』"><a href="#『Functions-Are-Not-Packages』" class="headerlink" title="『Functions Are Not Packages』"></a>『Functions Are Not Packages』</h3><p>模組並不是這樣定義的。根據維基百科對『<a href="https://en.wikipedia.org/wiki/Modularity" target="_blank" rel="external">Modularity</a>』的定義：</p>
<blockquote>
<p>In software design, modularity refers to a logical partitioning of the &quot;software design&quot; that allows complex software to be manageable for the purpose of implementation and maintenance. The logic of partitioning may be based on related functions, implementation considerations, data links, or other criteria.</p>
</blockquote>
<p>『在軟體設計中，模組化是指軟體設計的邏輯分割，以允許複雜的軟體，能夠以可管理的方式實作及維護。邏輯分割可能基於相關性、實作考量、資料連結、以及其它要素。』</p>
<p>要我說，任何具有獨立性、內聚性，可以管理的東西，都可以將它做成模組，哪怕只是提供一些常數。</p>
<p>『<a href="https://en.wikipedia.org/wiki/Unix_philosophy" target="_blank" rel="external">Do one thing and do it well</a>』，『程式應該只關注一個目標，並儘可能把它做好。讓程式能夠互相協同工作。』稟持著 Unix 的設計哲學，node.js 社群也信奉這個原則，讓每個元件，做到最好，讓每個元件，能夠隨意組合使用。</p>
<h3 id="『Third-Party-Problems』"><a href="#『Third-Party-Problems』" class="headerlink" title="『Third Party Problems』"></a>『Third Party Problems』</h3><blockquote>
<p>There’s absolutely no guarantee that what someone else has written is correct, or even works well. Even if correct, is it the most optimal solution possible? At least when you write the code yourself, you can easily modify it to fix bugs and improve its efficiency. Not that there should be many bugs in 1 line functions.</p>
</blockquote>
<p>沒有人強迫你用他們的模組，你要使用前，本應做好評估。甚至，如果你是 TDD 派，只要你的程式碼通過測試，就是該模組的功能符合你的需求的證明。更何況如果你對該模組有疑慮，大可以針對該模組撰寫測試來進行評估。『為什麼要幫別人寫測試？』你問。這一段程式你原本不是要自幹？難道自幹就不用寫測試？</p>
<p>至於，如何保證引用的模組，是『最有效率的解決方案』？好問題，你如何證明你自幹的程式碼，是『最有效率的解決方案』？</p>
<p>『至少我自己寫的程式碼，要怎麼改就可以怎麼改。』---- 你知道 open source 是什麼意思嗎？</p>
<blockquote>
<p>Second, even if the package’s logic is correct, I can’t help but be amazed by the fact that developers are taking on dependencies for single line functions that they should be able to write with their eyes closed. In my opinion, if you cannot write a left-pad, is-positive-integer, or isArray function in 5 minutes flat (including the time you spend Googling), then you don’t actually know how to code.</p>
</blockquote>
<p>我前面已經回答過了，忘記的話請自行回捲。</p>
<h3 id="『Strive-For-Few-Dependencies』"><a href="#『Strive-For-Few-Dependencies』" class="headerlink" title="『Strive For Few Dependencies』"></a>『Strive For Few Dependencies』</h3><blockquote>
<p>The more dependencies you take on, the more points of failure you have.</p>
</blockquote>
<p>你自己寫的程式碼也是一樣，如果你不寫測試的話。</p>
<blockquote>
<p>have you vetted any of the programmers who have written these functions that you depend on daily?</p>
</blockquote>
<p>你每天吃進肚子裡的東西，穿在身上的衣服，出門開的車，住的房子，你有去審核評估過製作者嗎？</p>
<p>還是那句話，別人都已經攤開 source 給你看了，為什麼還偏偏要自己寫？你要不要相信這些程式碼，由測試決定！要是怕 npm 又出問題，不會把用到的模組都複製一份到 local repo，或是自己架個 <a href="https://github.com/verdaccio/verdaccio" target="_blank" rel="external">server</a> 嗎？</p>
<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>作者其實只有一個重點：只有自己會寫程式，別人都是笨蛋都不會寫程式，所以不要相信別人寫的程式。即使別人寫的程式有嚴謹的測試，自己的卻沒有。一個開源社群的茁壯非常不容易，落井下石卻很簡單。將整個生態圈的蓬勃發展，形容為失控，而呼籲大家不要使用，根本就是因噎廢食。</p>
<p>社群已經出現反思的聲音，表示有人看到問題了，接下來自然就會有人提出解決辦法，這就是社群的運作法則。</p>
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1434871619871-1f315a50efba?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;JavaScript 社群 &amp;lt; 百家爭鳴 &amp;lt; 車廠＋車廂&lt;br&gt;黎明？黃昏？&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/@vaccinium&quot;&gt;Dmitrii Vaccinium&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code/&quot;&gt;前一陣子&lt;/a&gt;，&lt;a href=&quot;https://github.com/azer/left-pad&quot;&gt;&lt;code&gt;left-pad&lt;/code&gt;&lt;/a&gt; 的作者，因為不滿 npmjs.com 屈從財團的壓力，未經作者同意就擅自移轉所有權，因此憤而將全部模組從 npm 下架，而導致眾多知名模組受到波及。此事件引起了廣泛的反思。&lt;/p&gt;
&lt;p&gt;然而，漸漸出現一種聲音，開始呼籲大家不要使用 open source、要盡量自己寫程式碼，不要依賴別人的模組，就像這篇文章：『&lt;a href=&quot;http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/&quot;&gt;NPM &amp;amp; left-pad: Have We Forgotten How To Program?&lt;/a&gt;』(&lt;a href=&quot;http://qianduan.guru/2016/04/04/npm-left-pad-have-we-forgotten-how-to-program/&quot;&gt;中譯&lt;/a&gt;)。更有甚者，連『懂得怎麼寫程式之前，別去玩 node.js 好嗎？』這樣的話都出來了。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="Open Source" scheme="https://amobiz.github.io/tags/Open-Source/"/>
    
      <category term="NPM" scheme="https://amobiz.github.io/tags/NPM/"/>
    
      <category term="Programming" scheme="https://amobiz.github.io/tags/Programming/"/>
    
      <category term="modularity" scheme="https://amobiz.github.io/tags/modularity/"/>
    
  </entry>
  
  <entry>
    <title>如何判斷 npmjs.com 上的套件，是否支援 node 或 browser</title>
    <link href="https://amobiz.github.io/2016/03/09/npm-package-node-or-browser/"/>
    <id>https://amobiz.github.io/2016/03/09/npm-package-node-or-browser/</id>
    <published>2016-03-08T17:32:38.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1454587399083-b11b22f48fb6?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;w=1080&amp;fit=max&amp;s=8a0cb2a646f0d3a30461337998b6c642" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>工欲善其事必先利其器</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/photos/GOQ32dlahDk">Vítor Santos</a></div></div></div></div>
<p>在社群上有網友問到如何判斷 <a href="https://www.npmjs.com/" target="_blank" rel="external">npmjs.com</a> (或任意 open source 平台)上的套件，是否支援 node 或 browser，以下我將自己的經驗整理回答如下：</p>
<a id="more"></a>
<ol>
<li><p>首先當然就是先看 readme。通常 for browser 專用的套件，都會直接說明支援哪些瀏覽器，或在哪些瀏覽器測試過。另一個觀察的方式就是看它是否還可以使用 <a href="http://bower.io/" target="_blank" rel="external">bower</a> 安裝。</p>
</li>
<li><p>再來，可以了解一下套件提供的功能，自行判斷一下這些功能的實作需要哪些平台功能的支援。由此大概判斷是 for node 或 for browser。</p>
</li>
<li><p>另外，可以看套件本身支援哪種模組格式：</p>
<ul>
<li><p>採用 <a href="http://requirejs.org/docs/whyamd.html" target="_blank" rel="external">AMD</a> 的話，通常作者只預期可以在 browser 上使用 (但實際上也許可以供 node 使用)，</p>
</li>
<li><p>採用 <a href="https://github.com/umdjs/umd" target="_blank" rel="external">UMD</a> 的話，通常作者預期可以同時在 browser 和 node 上使用，</p>
</li>
<li><p>採用 <a href="http://wiki.commonjs.org/wiki/Modules/1.1" target="_blank" rel="external">CommonJS</a> 的話，通常作者只預期可以在 node 上使用 (但實際上也許可以供 browser 使用)，</p>
</li>
<li><p>採用 <a href="http://www.2ality.com/2014/09/es6-modules-final.html" target="_blank" rel="external">ES2015 module</a> 的話，就很難僅憑格式判斷。</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> 的流行，越來越多人只使用 CommonJS 或 ES2015 格式，所以只能做為參考。</p>
</li>
</ul>
</li>
<li><p>看一下相依的模組，看看有沒有熟悉的模組，這些模組是否只支援特定平台。</p>
</li>
<li><p>掃一下 source code，看看有沒有平台相關的關鍵字，譬如 <code>window</code>, <code>location</code> (browser) 或 <code>process</code>, <code>readFile</code> (node) 之類。</p>
</li>
<li><p>如果上面的方式都無法判斷的話，最快的方式就是直接寫測試程式，在目標平台上執行看看。</p>
<p> 因為至少有下面兩項因素會導致套件無法在 browser 上使用：</p>
<ul>
<li><p>套件本身或依賴的套件用到了 node 的專屬 API</p>
<p>不過，類似 <a href="https://github.com/substack/node-browserify#compatibility" target="_blank" rel="external">browserify</a> 這樣的工具可以提供簡單的 node API 供 browser 下使用。</p>
</li>
<li><p>用到了一般瀏覽器尚未支援的語言特性</p>
<p>不過，一樣可以用 <a href="https://babeljs.io/docs/usage/polyfill/" target="_blank" rel="external">babel</a> 等工具來克服。</p>
<p>相反地，如果是要在 node 上使用，怕套件本身或依賴的套件用到了 browser <a href="http://stackoverflow.com/a/7614380/726650" target="_blank" rel="external">host object</a> 的話，真的只有直接測試比較快。</p>
</li>
</ul>
</li>
<li><p>最後的絕招，如果在所有的文件上都找不到的話，就直接向作者詢問吧。</p>
<p> 真的要先確認文件上找不到再發問，建議直接發 issue (標示為 Question)，因為其他使用者也可能會想知道答案。大部分的作者都會很熱心回答，不過有時候還是需要耐心等候。</p>
</li>
</ol>
<p>最後，也歡迎大家到我的 <a href="https://www.npmjs.com/~amobiz" target="_blank" rel="external">npm</a> 和 <a href="https://github.com/amobiz" target="_blank" rel="external">GitHub</a> 針對專案提問或建議，能發 PR 當然是最好了。 &lt;3</p>
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1454587399083-b11b22f48fb6?ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;fm=jpg&amp;amp;crop=entropy&amp;amp;w=1080&amp;amp;fit=max&amp;amp;s=8a0cb2a646f0d3a30461337998b6c642&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;工欲善其事必先利其器&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/photos/GOQ32dlahDk&quot;&gt;Vítor Santos&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在社群上有網友問到如何判斷 &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npmjs.com&lt;/a&gt; (或任意 open source 平台)上的套件，是否支援 node 或 browser，以下我將自己的經驗整理回答如下：&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="tutorial" scheme="https://amobiz.github.io/tags/tutorial/"/>
    
      <category term="CommonJS" scheme="https://amobiz.github.io/tags/CommonJS/"/>
    
      <category term="npm" scheme="https://amobiz.github.io/tags/npm/"/>
    
      <category term="AMD" scheme="https://amobiz.github.io/tags/AMD/"/>
    
      <category term="UMD" scheme="https://amobiz.github.io/tags/UMD/"/>
    
      <category term="Open Source" scheme="https://amobiz.github.io/tags/Open-Source/"/>
    
      <category term="node" scheme="https://amobiz.github.io/tags/node/"/>
    
      <category term="browser" scheme="https://amobiz.github.io/tags/browser/"/>
    
  </entry>
  
  <entry>
    <title>AMP - Accelerated Mobile Pages 技術預覽</title>
    <link href="https://amobiz.github.io/2016/02/28/amp-intro/"/>
    <id>https://amobiz.github.io/2016/02/28/amp-intro/</id>
    <published>2016-02-27T20:06:38.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1454997423871-b5215756e54d?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1375" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>行動網頁</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/maliha">Maliha Mannan</a></div></div></div></div>
<p>Google AMP 是用來加速顯示靜態網頁內容的技術。</p>
<p>主要由三個部份組成：AMP HTML (content), AMP JS (runtime), Google AMP Cache。</p>
<p>其中 AMP HTML 及 AMP JS 牽涉到內容的技術部份，可以一併來檢視。可以這麼說，為了達成瞬間顯示的效能，這兩項技術禁用一些容易阻塞瀏覽器的元素：</p>
<ol>
<li>Style 必須內聯定義，以加速版面的計算。</li>
<li>除了 AMP 元件必要的 JavaScript 之外，禁用其他 JavaScript。以避免諸如 <code>document.write()</code>, 或動態改變 class, style 等造成 reflow 的狀況。</li>
<li>所有的外部資源均需要使用訂製的 AMP 元件來引用，以便由 AMP 管理資源的載入順序。主要是為了要讓網頁最前面可視區域能最優先顯示。</li>
<li>所有的外部資源均需要設定尺寸大小，以便在資源實際載入前，先行計算 layout。讓畫面不會在資源載入後又重新計算。</li>
</ol>
<a id="more"></a>
<p>第四點乍看之下似乎限制了 RWD 的可能性，但實際上所有的元素仍然可以使用 media query，根據不同媒體設定可視尺寸。<br>不過這同時也導致一個疑惑，既然元素實際顯示的大小，可以使用 media query 改變，若設定的大小與 media query 指定的不同，不是又會造成 reflow 嗎？</p>
<p>關於這部份的技術細節部份，留待下一篇文章，介紹如何開發符合 AMP 規格的頁面時，再詳細介紹。</p>
<p>至於 Google AMP Cache，則是利用 Google 無所不在的 CDN，讓內容可以就近取得，加速網頁的下載。</p>
<h3 id="與-Apple-News-及-Facebook-Instant-Articles-的比較"><a href="#與-Apple-News-及-Facebook-Instant-Articles-的比較" class="headerlink" title="與 Apple News 及 Facebook Instant Articles 的比較"></a>與 Apple News 及 Facebook Instant Articles 的比較</h3><p>已經有許多文章對此進行了詳細的比較，總結來說，有以下的差異：</p>
<h4 id="體驗不同"><a href="#體驗不同" class="headerlink" title="體驗不同"></a>體驗不同</h4><p>Apple News 及 Facebook Instant Articles 雖然也強調速度，但同時更強調他們的技術可以提昇互動與沈浸體驗 (interactive and immersive)；而 Google AMP 則並未著重在內容體驗上，而是主打呈現速度。</p>
<h4 id="載具不同"><a href="#載具不同" class="headerlink" title="載具不同"></a>載具不同</h4><p>Apple News 及 Facebook Instant Articles 都必須使用特定的 App 來存取；而 Google AMP 則是獨立的頁面，可以使用任何瀏覽程式閱讀。</p>
<h4 id="驅動方式不同"><a href="#驅動方式不同" class="headerlink" title="驅動方式不同"></a>驅動方式不同</h4><p>Apple News 及 Facebook Instant Articles 基本上都是透過類似 RSS 的技術來遞送內容，而 Google AMP 則不預設任何方式來發現內容 (基本上 Google 當然是希望讀者透過自家的搜尋引擎)，並透過標準 HTTP/HTTP2 來傳送。</p>
<h4 id="限制不同"><a href="#限制不同" class="headerlink" title="限制不同"></a>限制不同</h4><p>Apple News 及 Facebook Instant Articles 基本上都是透過平台本身的技術來加速與強化內容，因此在格式上未做太多限制；而 Google AMP 由於不允許使用自訂 JavaScript，因此即便是廣告都必須符合 AMP 的規範，透過特定的 AMP 元件來引用。</p>
<p>簡單地說，Apple News 及 Facebook Instant Articles 算是 CMS 平台，而 Google AMP 則是格式。</p>
<p>有網友說，不像 Facebook 採用的是標準的 HTML 5，『AMP 並不是標準的 HTML, 應該算是 HTML 的延伸並有自己定義的 AMP 屬性』。事實上，AMP 採用的是 <a href="http://webcomponents.org/" target="_blank" rel="external">Web Components</a> 技術，而 Web Components 又依賴於 <a href="https://www.w3.org/TR/shadow-dom/" target="_blank" rel="external">Shadow DOM</a>，雖然這些標準仍然處於草案階段，但是實際上已是相當成熟的技術，並且已有 polyfill 可以支援現有的主流瀏覽器。相反地，Facebook Instant Articles 必需以 Facebook App 閱覽，而不是瀏覽器，是否採用 HTML 5 的標準，根本已無關緊要。</p>
<p>另外，所謂 『Facebook Instant Articles 會自動幫內容出版商做格式轉換，這點 Google AMP 相較於 Facebook Instant Articles 略遜一籌。』</p>
<p>我想這一點恐怕是見仁見智。</p>
<p>首先，就誘因而言，若出版商不需做任何處理，就可以讓內容呈現在 Facebook Instant Articles 上，出版商的接受度當然是會提高。但關鍵是，Facebook Instant Articles 並未公佈技術細節，出版商並沒有最終內容呈現的控制權，你無法控制內容最終呈現在讀者面前的樣貌，甚至於部份內容 (因為即時顯示的需要) 被移除了，你完全無權置喙。</p>
<p>相對地，Google AMP 是 Open Source，所有的技術公開，除了 WordPress 已經<a href="http://techcrunch.com/2016/02/24/wordpress-sites-now-support-googles-amp-to-make-mobile-pages-load-much-faster/" target="_blank" rel="external">支援 AMP</a>，毫無疑問，很快地就會有大量的工具出爐，協助出版商轉換內容。無論是否透過工具自動轉換，出版商對於最終內容的呈現，擁有完整的控制權。</p>
<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>自從首度發布，可以看到 AMP components 的數量不斷成長，也就是說，AMP 頁面可以承載的內容，也越來越豐富了。可以這麼說，AMP 是一個不斷發展，充滿生命力的技術。當然，這還是要歸功於 Open Source 的力量。</p>
<h3 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h3><ul>
<li><a href="https://www.ampproject.org/" target="_blank" rel="external">Accelerated Mobile Pages Project</a></li>
<li><a href="https://www.smashingmagazine.com/2016/02/everything-about-google-accelerated-mobile-pages/" target="_blank" rel="external">Turn Your AMP Up To 11: Everything You Need To Know About Google’s Accelerated Mobile Pages</a></li>
<li><a href="https://instantarticles.fb.com/" target="_blank" rel="external">Instant Articles</a></li>
<li><a href="https://s0.wp.com/wp-content/themes/vip/facebook-instantarticles/library/docs/FB_IA_FAQS.pdf" target="_blank" rel="external">Instant Articles Frequently Asked Questions</a></li>
<li><a href="http://techcrunch.com/2016/02/24/wordpress-sites-now-support-googles-amp-to-make-mobile-pages-load-much-faster/" target="_blank" rel="external">WordPress Sites Now Support Google’s AMP To Make Mobile Pages Load Much Faster</a></li>
<li><a href="https://softnshare.wordpress.com/2016/02/21/%E6%9C%80%E8%BF%91%E5%B9%BE%E5%89%87%E8%B7%9F%E6%95%B8%E4%BD%8D%E5%87%BA%E7%89%88%E6%A0%BC%E5%BC%8F%E6%9C%89%E9%97%9C%E7%9A%84%E6%96%B0%E8%81%9E/" target="_blank" rel="external">最近幾則跟數位出版格式有關的新聞</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1454997423871-b5215756e54d?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1375&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;行動網頁&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/maliha&quot;&gt;Maliha Mannan&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Google AMP 是用來加速顯示靜態網頁內容的技術。&lt;/p&gt;
&lt;p&gt;主要由三個部份組成：AMP HTML (content), AMP JS (runtime), Google AMP Cache。&lt;/p&gt;
&lt;p&gt;其中 AMP HTML 及 AMP JS 牽涉到內容的技術部份，可以一併來檢視。可以這麼說，為了達成瞬間顯示的效能，這兩項技術禁用一些容易阻塞瀏覽器的元素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Style 必須內聯定義，以加速版面的計算。&lt;/li&gt;
&lt;li&gt;除了 AMP 元件必要的 JavaScript 之外，禁用其他 JavaScript。以避免諸如 &lt;code&gt;document.write()&lt;/code&gt;, 或動態改變 class, style 等造成 reflow 的狀況。&lt;/li&gt;
&lt;li&gt;所有的外部資源均需要使用訂製的 AMP 元件來引用，以便由 AMP 管理資源的載入順序。主要是為了要讓網頁最前面可視區域能最優先顯示。&lt;/li&gt;
&lt;li&gt;所有的外部資源均需要設定尺寸大小，以便在資源實際載入前，先行計算 layout。讓畫面不會在資源載入後又重新計算。&lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
      <category term="Web Design" scheme="https://amobiz.github.io/categories/Web-Design/"/>
    
    
      <category term="AMP" scheme="https://amobiz.github.io/tags/AMP/"/>
    
      <category term="Google" scheme="https://amobiz.github.io/tags/Google/"/>
    
  </entry>
  
  <entry>
    <title>[ESLint 學習筆記] Applying ESLint - Using Atom &amp; linter-eslint</title>
    <link href="https://amobiz.github.io/2015/12/21/applying-eslint-using-atom-linter-eslint/"/>
    <id>https://amobiz.github.io/2015/12/21/applying-eslint-using-atom-linter-eslint/</id>
    <published>2015-12-21T04:48:30.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1453946610176-6be21147c400?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>筆記、筆記、再筆記</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/calum_mac">Calum MacAulay</a></div></div></div></div>
<p>使用 Atom 編輯器，安裝上 linter-eslint 外掛，再加上一個現成的 ESLint  範本，就可以無痛投入到 ESLint 的懷抱了。</p>
<a id="more"></a>
<h3 id="轉移到-ESLint"><a href="#轉移到-ESLint" class="headerlink" title="轉移到 ESLint"></a>轉移到 ESLint</h3><p>老實說，很慶幸我恰巧選擇了使用 <a href="https://atom.io/" target="_blank" rel="external">Atom</a> 編輯器，及 <a href="https://atom.io/packages/linter-eslint" target="_blank" rel="external">linter-eslint</a> package 外掛來進行轉換工作，整個過程輕鬆愉快，一個晚上，就把所有的專案都移轉到 <a href="http://eslint.org/" target="_blank" rel="external">ESLint</a> 下了。(其實不一定要 Atom，任何可以支援 linter-eslint 的編輯器都可以。)</p>
<p>我的作法如下：</p>
<ol>
<li><p>安裝 <a href="https://atom.io/" target="_blank" rel="external">Atom</a><br>安裝後，執行起來。</p>
</li>
<li><p>安裝 <a href="https://atom.io/packages/linter-eslint" target="_blank" rel="external">linter-eslint</a></p>
<ol>
<li>點選 Edit / Preferences 選單，</li>
<li>點選 Install 標籤，</li>
<li>在 Search packages 輸入框中輸入 <code>linter-eslint</code>，</li>
<li>按下右邊的 Package 按鈕，</li>
<li>找到之後，按下 Install 按鈕。它會自動安裝相依的 linter 模組，</li>
<li>重新啟動 Atom。</li>
</ol>
</li>
<li><p>找一個現成的規則檔做為起點<br>最好是包含完整規則的範本，譬如<a href="https://gist.github.com/Corhero/856c9612ce61d22bf100" target="_blank" rel="external">這個</a>，直接放到專案根目錄下，就可以開始移轉了。</p>
</li>
<li><p>修正錯誤<br>打開專案目錄，開啟程式碼，根據發生的錯誤，依自己的喜好，或所屬公司、組織的標準， 決定處理方式：</p>
<ul>
<li>關閉規則</li>
<li>調整規則</li>
<li>修正程式碼</li>
</ul>
</li>
<li><p>修正所有的專案</p>
<ol>
<li>針對每個專案，複製上個步驟所修正的最新規則檔，重複步驟 4。</li>
<li>將最後完成的專案的規則檔，複製到所有修正過的專案下，重複步驟 4，確認這個規則檔適用於所有的專案。若不適用於所有的專案，則可以考慮在個別專案 source 目錄下，另增一個只適用於個別專案的規則檔，或是只針對特定檔案以 directives 的形式設定。</li>
</ol>
</li>
<li><p>把最終的規則檔，放到範本專案中，讓未來的新專案可以一體適用。</p>
</li>
</ol>
<h3 id="借助-linter-eslint-查詢錯誤規則"><a href="#借助-linter-eslint-查詢錯誤規則" class="headerlink" title="借助 linter-eslint 查詢錯誤規則"></a>借助 linter-eslint 查詢錯誤規則</h3><p>linter-eslint 其實是 <a href="https://atom.io/packages/linter" target="_blank" rel="external">linter</a> 的 plugin，而 linter 的界面做的相當好：</p>
<p><img src="linter-eslint.png" alt="圖：(1) 可以在程式碼中直接看到錯誤的地方與發生錯誤的規則。 (2) 可以在下方的錯誤列表中直接複製規則名稱。"></p>
<ol>
<li><p>在編輯器中直接強調錯誤的程式碼，以及違反的規則名稱<br>到底是那一段程式碼發生錯誤，以及違反了那一條規則，一目了然。</p>
</li>
<li><p>在下方的錯誤列表中，可以使用滑鼠圈選文字的方式，複製錯誤規則名稱<br>如果懶的自己打規則名稱，或者怕打錯，還可以直接複製，然後到網站上查詢。</p>
</li>
</ol>
<p>這一點是我覺得 ESLint 這麼好用的最重要原因。所有的規則，一一對應，沒有模糊不清的地方。你只要根據規則名稱，到 <a href="http://eslint.org/docs/rules/" target="_blank" rel="external">Rules</a> 搜尋一下，就可以找到對應的說明，然後就可以判斷自己要不要遵守這條規則，或是根據自己的喜好或習慣，開關或調整規則的選項。</p>
<h3 id="ESLint-超簡單入門"><a href="#ESLint-超簡單入門" class="headerlink" title="ESLint 超簡單入門"></a>ESLint 超簡單入門</h3><p>ESLint 完全可以自行決定是否套用特定規則，以及套用到何種程度。</p>
<h4 id="1-配置規則可以使用下列各種方式："><a href="#1-配置規則可以使用下列各種方式：" class="headerlink" title="1. 配置規則可以使用下列各種方式："></a>1. 配置規則可以使用下列各種方式：</h4><ol>
<li><p>直接以 directives 的形式寫在檔案中。只對該檔案生效，並且覆蓋其他方式的配置。同時</p>
</li>
<li><p>以 <a href="http://eslint.org/docs/user-guide/configuring#configuration-file-formats" target="_blank" rel="external"><code>.eslintrc.*</code> 檔案</a>的方式獨立配置。對該目錄及所有子目錄的檔案<a href="http://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy" target="_blank" rel="external">生效</a>；每個子目錄都可以有自己的 <code>.eslintrc.*</code> 檔案配置，並且覆蓋其父目錄的配置。注意，繼承規則時，會一路往磁碟的根目錄查詢規則，任何目錄若不想繼承其父目錄的規則，可以使用 <code>&quot;root&quot;: true</code> 加以阻絕。最後</p>
</li>
<li><p>如果是以 npm 管理模組，可以直接在 <code>pacakge.json</code> 中的 &quot;eslintConfig&quot; 區段直接配置。(以重複使用的角度，我個人不建議使用這種方式。)</p>
</li>
</ol>
<h4 id="2-支援-ES2015-及-JSX-語法規則"><a href="#2-支援-ES2015-及-JSX-語法規則" class="headerlink" title="2. 支援 ES2015 及 JSX 語法規則"></a>2. 支援 ES2015 及 JSX 語法規則</h4><p>可以在規則檔中以 <a href="http://eslint.org/docs/user-guide/configuring#specifying-language-options" target="_blank" rel="external">&quot;ecmaFeatures&quot;</a> 區塊，指定要套用那些語法規則。(文件中沒有提到如何以 directives 的方式在檔案中指定。不過我想最好還是整個專案採用一致的標準會比較好。)</p>
<h4 id="3-指定可執行環境"><a href="#3-指定可執行環境" class="headerlink" title="3. 指定可執行環境"></a>3. 指定可執行環境</h4><p>以 <code>/* eslint-env node, mocha */</code> 或在配置檔中以 <a href="http://eslint.org/docs/user-guide/configuring#specifying-environments" target="_blank" rel="external">&quot;env&quot;</a> 區塊指定模組或應用程式的可執行環境，譬如 <code>browser</code>, <code>es6</code>, <code>node</code>, <code>amd</code>, <code>mocha</code> 及 <code>jasmine</code> 等。ESLint 將假設這些環境的 global 變數的存在，而不會報告錯誤：『使用未定義的變數』。譬如：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;env&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;browser&quot;</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="string">&quot;node&quot;</span>: <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="4-自行指定既存的-global-變數"><a href="#4-自行指定既存的-global-變數" class="headerlink" title="4. 自行指定既存的 global 變數"></a>4. 自行指定既存的 global 變數</h4><p>如果某些程式庫仍使用 global 變數的方式匯出功能，則必須以 <code>/* global angular, bootstrap */</code> (注意沒有 <code>s</code>，這是為了與 JSLint/JSHit 相容。其實我是覺得可以 <code>globals</code> 和 <code>global</code> 兩種都支援)。或在配置檔中以 <a href="http://eslint.org/docs/user-guide/configuring#specifying-globals" target="_blank" rel="external">&quot;globals&quot;</a> 區塊加以指定，以免報告錯誤。譬如：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;globals&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;angular&quot;</span>: <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&quot;bootstrap&quot;</span>: <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中 <code>true</code> 代表該 global 變數可以被覆寫。</p>
<h4 id="5-支援外掛"><a href="#5-支援外掛" class="headerlink" title="5. 支援外掛"></a>5. 支援外掛</h4><p>外掛必須先以 npm 安裝，然後以 <a href="http://eslint.org/docs/user-guide/configuring#configuring-plugins" target="_blank" rel="external">&quot;plugins&quot;</a> 區塊指定要使用的外掛名稱，外掛名稱如果以 <code>eslint-plugin-</code> 開頭，最好省略不寫 <code>eslint-plugin-</code> 前置名稱 (配置外掛選項時會用到)。譬如：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;plugins&quot;</span>: [</span><br><span class="line">        <span class="string">&quot;plugin1&quot;</span>,</span><br><span class="line">        <span class="string">&quot;eslint-plugin-plugin2&quot;</span></span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="6-可以關閉或調整規則"><a href="#6-可以關閉或調整規則" class="headerlink" title="6. 可以關閉或調整規則"></a>6. 可以關閉或調整規則</h4><p>規則的 directives 格式完整形式如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/<em> eslint rule-name: [severity, options ...], &quot;plugin-name/rule-name&quot;: [severity, options ...] </em>/</span><br></pre></td></tr></table></figure>

<p>若以規則檔指定，則以 <a href="http://eslint.org/docs/user-guide/configuring#configuring-rules" target="_blank" rel="external">&quot;rules&quot;</a> 區指定規則。規則的 JSON 格式完整形式如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&quot;rule-name&quot;: [severity, options ...],</span><br><span class="line">&quot;plugin-name/rule-name&quot;: [severity, options ...]</span><br></pre></td></tr></table></figure>

<p>其中 <code>severity</code> 的值可以是下列三個：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>關閉</td>
</tr>
<tr>
<td>1</td>
<td>警告</td>
</tr>
<tr>
<td>2</td>
<td>錯誤</td>
</tr>
</tbody>
</table>
<p>而 <code>options</code> 的部份，則每一條規則不同，需要查<a href="http://eslint.org/docs/rules/" target="_blank" rel="external">文件</a>。若不需要指定 <code>options</code> 部份，則可以省略 <code>[]</code> 括號。</p>
<p>如果使用的外掛需要調整設定，則必須以 <code>&quot;plugin-name/rule-name&quot;</code> 的形式指定。注意，這時候<strong>不能</strong>指定 <code>eslint-plugin-</code> 前置名稱。</p>
<p>譬如：</p>
<p><strong>directives</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/<em> eslint &quot;plugin1/rule1&quot;: 2, &quot;plugin2/rule1&quot;: [1, &quot;always&quot;] </em>/</span><br></pre></td></tr></table></figure>

<p><strong>.eslintrc.json</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;rules&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;eqeqeq&quot;</span>: <span class="number">0</span>,</span><br><span class="line">        <span class="string">&quot;quotes&quot;</span>: [<span class="number">2</span>, <span class="string">&quot;double&quot;</span>],</span><br><span class="line">        <span class="string">&quot;yoda&quot;</span>: [<span class="number">1</span>, <span class="string">&quot;never&quot;</span>, &#123; <span class="string">&quot;exceptRange&quot;</span>: <span class="literal">true</span> &#125;]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="7-可以使用擴展的方式重複使用設定檔"><a href="#7-可以使用擴展的方式重複使用設定檔" class="headerlink" title="7. 可以使用擴展的方式重複使用設定檔"></a>7. 可以使用擴展的方式重複使用設定檔</h4><p>為了確保設定檔只有一份，而不是散落在各個專案中，可以利用 <a href="http://eslint.org/docs/user-guide/configuring#extending-configuration-files" target="_blank" rel="external">&quot;extends&quot;</a> 區塊，指定規則檔，然後再加以補正。譬如說，可以有一個模組，實際上不含任何功能，唯一的目的就是提供 <code>.eslintrc.*</code> 檔案供其他專案引用：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;extends&quot;</span>: [</span><br><span class="line">        <span class="string">&quot;./node_modules/coding-standard/.eslintrc.json&quot;</span>,</span><br><span class="line">        <span class="string">&quot;./node_modules/coding-standard/.eslintrc-jsx.json&quot;</span></span><br><span class="line">    ],</span><br><span class="line">    <span class="string">&quot;rules&quot;</span>: &#123;</span><br><span class="line">        <span class="comment">// Override any settings from the &quot;parent&quot; configuration</span></span><br><span class="line">        <span class="string">&quot;eqeqeq&quot;</span>: <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由上面例子也可以看到，ESLint 允許在 JSON 檔案中撰寫註解 (雖然標準的 JSON 不允許，譬如 <code>package.json</code>)。</p>
<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>利用上述的方式套用範本，只需要針對衝突的規則加以處理，調整到最適合自己的方式，一點都不難。還不快投入 ESLint 的懷抱！</p>
<p>想參考我的 Open Source 專案 (符合 ECMAScript 5，未使用 ES2015 的語法) 的 <a href="https://github.com/amobiz/configurable-gulp-recipes/blob/master/.eslintrc.json" target="_blank" rel="external"><code>.eslintrc.json</code></a> 做為起點的朋友，請自行<a href="https://github.com/amobiz/configurable-gulp-recipes/blob/master/.eslintrc.json" target="_blank" rel="external">取用</a>。</p>
<p>歡迎大家的回饋與心得分享。</p>
<h3 id="參考文章："><a href="#參考文章：" class="headerlink" title="參考文章："></a>參考文章：</h3><ul>
<li><a href="http://eslint.org/docs/user-guide/configuring" target="_blank" rel="external">ESLint Configuring</a></li>
<li><a href="http://eslint.org/docs/rules/" target="_blank" rel="external">ESLint Rules</a></li>
<li><a href="http://www.sitepoint.com/comparison-javascript-linting-tools/" title="A Comparison of JavaScript Linting Tools" target="_blank" rel="external">Linting Tools Comparison</a></li>
</ul>
<h3 id="相關文章："><a href="#相關文章：" class="headerlink" title="相關文章："></a>相關文章：</h3><!-- cross references -->
<div><ul>
<li><a href="/2015/12/20/problems-of-jshint/" title="The Problems of JSHint (JSHint 的問題)">The Problems of JSHint (JSHint 的問題)</a></li>
</ul>
</div>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1453946610176-6be21147c400?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;筆記、筆記、再筆記&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/calum_mac&quot;&gt;Calum MacAulay&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;使用 Atom 編輯器，安裝上 linter-eslint 外掛，再加上一個現成的 ESLint  範本，就可以無痛投入到 ESLint 的懷抱了。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="tutorial" scheme="https://amobiz.github.io/tags/tutorial/"/>
    
      <category term="lint" scheme="https://amobiz.github.io/tags/lint/"/>
    
      <category term="Atom" scheme="https://amobiz.github.io/tags/Atom/"/>
    
      <category term="ESLint" scheme="https://amobiz.github.io/tags/ESLint/"/>
    
      <category term="linter-eslint" scheme="https://amobiz.github.io/tags/linter-eslint/"/>
    
      <category term="linter" scheme="https://amobiz.github.io/tags/linter/"/>
    
  </entry>
  
  <entry>
    <title>The Problems of JSHint (JSHint 的問題)</title>
    <link href="https://amobiz.github.io/2015/12/20/problems-of-jshint/"/>
    <id>https://amobiz.github.io/2015/12/20/problems-of-jshint/</id>
    <published>2015-12-19T22:30:30.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1442115597578-2d0fb2413734?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>問題 &lt; 一個頭兩個大 &lt; 小孩讀聖經</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/samanthasophia">Samantha Sophia</a></div></div></div></div>
<p>最早的 JavaScript <a href="https://en.wikipedia.org/wiki/Lint_%28software%29" target="_blank" rel="external">linter</a> 是 <a href="http://www.jslint.com/" target="_blank" rel="external">JSLint</a>，但是由於作者 <a href="https://en.wikipedia.org/wiki/Douglas_Crockford" target="_blank" rel="external">Douglas Crockford</a> 設定了太多個人主觀認定必須要遵守的強制規則，而且很少有妥協的餘地，所以後來強調可以配置規則的 <a href="http://jshint.com/docs/" target="_blank" rel="external">JSHint</a> 漸漸崛起，我自己從一開始就是使用 JSHint，並且也用了蠻長一段時間。然而...</p>
<a id="more"></a>
<h3 id="JSHint-的問題"><a href="#JSHint-的問題" class="headerlink" title="JSHint 的問題"></a>JSHint 的問題</h3><p>過去使用 JSHint 時，有非常多的困擾：</p>
<h4 id="1-難以確認發生錯誤的規則"><a href="#1-難以確認發生錯誤的規則" class="headerlink" title="1. 難以確認發生錯誤的規則"></a>1. 難以確認發生錯誤的規則</h4><p>預設情況下，根本無法從它的錯誤訊息中，得知是違反那一條<a href="http://jshint.com/docs/options/" target="_blank" rel="external">規則</a>。每次遇到錯誤，只能利用它的錯誤訊息來搜尋文件，每次幾乎都是看完所有的文件，還是找不到正確的規則。然後你才會不小心<a href="http://jshint.com/docs" target="_blank" rel="external">發現 (想起來)</a>，它可以在執行的時候指定 <code>--verbose</code> 參數，然後就會顯示對應的規則代碼 (warning code)，譬如 <code>(W034)</code>。這難道不應該是預設行為才對嗎？</p>
<p>最糟糕的是，JSHint 的 API 並不支援 <code>verbose</code> 選項，這是寫在 <a href="https://github.com/jshint/jshint/blob/master/src/cli.js" target="_blank" rel="external"><code>cli</code></a> 模組裡的，也就是說，如果你是使用自動化建制工具，如 <a href="http://gulpjs.com/" target="_blank" rel="external">Gulp</a> 或 <a href="http://gruntjs.com/" target="_blank" rel="external">Grunt</a>，那麼你永遠看不到規則代碼！</p>
<h4 id="2-沒有完整的規則代碼列表"><a href="#2-沒有完整的規則代碼列表" class="headerlink" title="2. 沒有完整的規則代碼列表"></a>2. 沒有完整的規則代碼列表</h4><p>好不容易總算找到規則代碼了，到它的<a href="http://jshint.com/docs" target="_blank" rel="external">文件</a>頁面，在右上角輸入規則代碼，按下 &quot;Jump to docs&quot; 搜尋，結果只會跳到 <a href="http://jshint.com/docs/options/" target="_blank" rel="external">JSHint Options</a> 頁面，好吧，那麼應該在頁面上搜尋得到吧，按下 Ctrl+F 搜尋，還是找不到 <code>W034</code>。</p>
<p>翻遍文件，你絕對找不到任何說明，除了文件<a href="https://www.google.com.tw/webhp?sourceid=chrome-instant&amp;ion=1&amp;espv=2&amp;ie=UTF-8#q=site%3Ajshint.com%20W034" target="_blank" rel="external">首頁的介紹文章</a>中，以 <code>W034</code> 介紹如何開關警告錯誤訊息時提到。</p>
<p>那麼，<code>W034</code> 到底是什麼呢？還好，有 <a href="https://jslinterrors.com/" target="_blank" rel="external">JSLint Error Explanations</a> 這樣的網站，可以在這裡查詢錯誤代碼，譬如：搜尋 <a href="https://jslinterrors.com/?q=W034" target="_blank" rel="external">W034</a>，可以找到這個錯誤是 <a href="https://jslinterrors.com/unnecessary-use-strict" target="_blank" rel="external">Unnecessary &#39;use strict&#39;</a>，在這裡可以看到極為詳細的說明。</p>
<h4 id="3-規則代碼無法望文生義"><a href="#3-規則代碼無法望文生義" class="headerlink" title="3. 規則代碼無法望文生義"></a>3. 規則代碼無法望文生義</h4><p>當你調整好規則，繼續快樂地 coding。幾天之後，你再次看到 <code>W034</code>，卻根本記不得這條規則是什麼。像這種寫在 source code 檔案中的 directives： <code>/* jshint -W034 */</code>，除非你在旁邊寫註解來說明這條規則，否則你光是看到 <code>W034</code>，根本稿不清楚這是什麼規則。但是這樣一來，每個檔案都必須重複同樣的內容，根本違反 <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" target="_blank" rel="external">DRY</a> 原則。</p>
<p>那麼，寫在 <code>.jshintrc</code> 檔案如何？很不幸的是，<code>.jshintrc</code> 檔案是 <a href="https://en.wikipedia.org/wiki/JSON" target="_blank" rel="external">JSON</a> 格式，而標準 JSON 是不能有註解的。更不用提 JSHint 文件上根本就沒有提到像 <code>W034</code> 這樣的規則，要如何寫在 <code>.jshintrc</code> 檔案中了。</p>
<p>在 Google 直接搜尋 <code>W034</code>，可以找到在這個 <a href="http://gruntjs.com/" target="_blank" rel="external">Grunt</a> 的 <a href="https://github.com/gruntjs/grunt-contrib-jshint/issues/59" target="_blank" rel="external">Issue</a> 中，有人回答了 Grunt 的作法：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">jshint: &#123;</span><br><span class="line">  <span class="attr">options</span>: &#123;</span><br><span class="line">    <span class="string">&#39;-W034&#39;</span>: <span class="literal">true</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>同樣的方法也可以套用在 <code>.jshintrc</code> 檔案中。</p>
<h4 id="4-配置選項與規則代碼兩套規則"><a href="#4-配置選項與規則代碼兩套規則" class="headerlink" title="4. 配置選項與規則代碼兩套規則"></a>4. 配置選項與規則代碼兩套規則</h4><p>前面提到的<a href="http://jshint.com/docs" target="_blank" rel="external">文件</a>頁面，裡面提到的 directives 使用的 <a href="http://jshint.com/docs/options/" target="_blank" rel="external">options</a> 都是像 <code>unused</code> 及 <code>eqnull</code> 這樣的選項。這些選項，比較像是大分類，而執行 JSHint 時所報告的錯誤，實際可能是由這些選項、或是預設的選項所控制，但是卻無從得知這些規則代碼與配置選項之間的對應關係。所以，即使你很仔細地設定了每一條配置選項， JSHint 還是有可能會報告錯誤。除非你已經是 JavaScript 的專家，在面對 JSHint 報告的發生錯誤的規則代碼時，你很難知道究竟是那一條規則所造成的，於是，你只能以規則代碼去查詢 <a href="https://jslinterrors.com/" target="_blank" rel="external">JSLint Error Explanations</a> 網站。最後，恐怕還是只能在 source code 上寫下 <code>/* jshint -W034 */</code> 。</p>
<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>其實 JSHint 自己也<a href="http://jshint.com/docs" target="_blank" rel="external">承認</a>這些問題的存在：</p>
<blockquote>
<p>Trying to figure out how JSHint options work can be confusing and frustrating (and we&#39;re working on fixing that!)</p>
</blockquote>
<p>在 JSHint 變得更酷之前，就先允許我們小小地[叛逃][applying-eslint-using-atom-linter-eslint]吧。</p>
<h3 id="參考文章："><a href="#參考文章：" class="headerlink" title="參考文章："></a>參考文章：</h3><ul>
<li><a href="http://jshint.com/docs" target="_blank" rel="external">JSHint Documentation</a></li>
<li><a href="http://jshint.com/docs/options/" target="_blank" rel="external">JSHint Options</a></li>
<li><a href="http://xianjing.github.io/blog/2013/10/16/jshint-doc/" title="JSHint在线文档翻译（一）：基本介绍" target="_blank" rel="external">JSHint在线文档翻译（一）：基本介绍</a></li>
<li><a href="http://xianjing.github.io/blog/2013/10/21/jshint-options/" title="JSHint在线文档翻译（二）: 选项" target="_blank" rel="external">JSHint在线文档翻译（二）: 选项</a></li>
</ul>
<h3 id="相關文章："><a href="#相關文章：" class="headerlink" title="相關文章："></a>相關文章：</h3><!-- cross references -->
<div><ul>
<li><a href="/2015/12/21/applying-eslint-using-atom-linter-eslint/" title="[ESLint 學習筆記] Applying ESLint - Using Atom &amp; linter-eslint">(ESLint 學習筆記) Applying ESLint - Using Atom &amp; linter-eslint</a></li>
</ul>
</div>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1442115597578-2d0fb2413734?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;問題 &amp;lt; 一個頭兩個大 &amp;lt; 小孩讀聖經&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/samanthasophia&quot;&gt;Samantha Sophia&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;最早的 JavaScript &lt;a href=&quot;https://en.wikipedia.org/wiki/Lint_%28software%29&quot;&gt;linter&lt;/a&gt; 是 &lt;a href=&quot;http://www.jslint.com/&quot;&gt;JSLint&lt;/a&gt;，但是由於作者 &lt;a href=&quot;https://en.wikipedia.org/wiki/Douglas_Crockford&quot;&gt;Douglas Crockford&lt;/a&gt; 設定了太多個人主觀認定必須要遵守的強制規則，而且很少有妥協的餘地，所以後來強調可以配置規則的 &lt;a href=&quot;http://jshint.com/docs/&quot;&gt;JSHint&lt;/a&gt; 漸漸崛起，我自己從一開始就是使用 JSHint，並且也用了蠻長一段時間。然而...&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="pitfalls" scheme="https://amobiz.github.io/tags/pitfalls/"/>
    
      <category term="JSHint" scheme="https://amobiz.github.io/tags/JSHint/"/>
    
      <category term="lint" scheme="https://amobiz.github.io/tags/lint/"/>
    
  </entry>
  
  <entry>
    <title>Logdown 學習筆記</title>
    <link href="https://amobiz.github.io/2015/11/15/logdown-notes/"/>
    <id>https://amobiz.github.io/2015/11/15/logdown-notes/</id>
    <published>2015-11-14T18:05:00.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1457717047216-96440b68d44c?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Logdown 諧音 Look down</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/jrbreland">John Breland 3</a></div></div></div></div>
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>長期關注本部落格的朋友，可能有發現到，除了原本在 Blogger 的 <a href="http://andmobiz.blogspot.tw/" title="格物致知" target="_blank" rel="external">格物致知</a> 之外，還有一個同步更新的網站 <a href="http://fragmentsoftime.logdown.com/" title="Fragments Of Time" target="_blank" rel="external">Fragments of Time</a> 位於 <a href="http://logdown.com/" target="_blank" rel="external">Logdown</a>。</p>
<p>這是因為我在之前的文章『<a href="/2015/11/09/medium-vs-facebook-notes/" title="Medium 與 Facebook Notes 簡短試用">Medium 與 Facebook Notes 簡短試用</a>』中提到的，厭倦了使用 HTML 撰寫部落格，所以在 Logdown <a href="http://www.inside.com.tw/2013/07/17/logdown-interview-a" title="台灣製造，新一代部落格平台 Logdown 團隊專訪" target="_blank" rel="external">發佈</a>之後，曾經有一段時間，我是在 Logdown 先用 Markdown 語法撰寫文章，然後再將 HTML 複製到 Blogger 中。</p>
<p>現在決定改用 <a href="https://pages.github.com/" target="_blank" rel="external">GitHub Pages</a> / <a href="https://hexo.io/zh-tw/" target="_blank" rel="external">hexo</a>，上面的步驟就省了，現在我可以直接用 Markdown 寫文章，然後下個指令，就自動轉成 HTML 並且發佈。當初除了必須手動複製這個原因之外，其實在使用 Logdown 時，還遇到一些不方便的地方，並且做了一些筆記。我不確定現在這些問題是否依舊沒有改善，整理出來，給不幸遇到的朋友參考看看。</p>
<a id="more"></a>
<h4 id="Q-無法在同一頁中，建立交互連結"><a href="#Q-無法在同一頁中，建立交互連結" class="headerlink" title="Q: 無法在同一頁中，建立交互連結"></a>Q: 無法在同一頁中，建立交互連結</h4><p>A: 有兩個問題:</p>
<ol>
<li><p>Logdown 不會自動為 header 元素設定 id，所以必須自行加上 anchor 元素。如:</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">### &lt;a id=&quot;mylink&quot; href=&quot;#&quot;&gt;My Link&lt;/a&gt;</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>Logdown 不支援 <code>[link text][link reference]</code> 的連結語法，所以只能使用 <code>[My Link](#mylink)</code> 的語法建立連結。如：</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Here is <a href="span class=&quot;link&quot;&gt;#mylink&lt;/span"><span class="string">My Link</span></a>.</span><br></pre></td></tr></table></figure>

</li>
</ol>
<p>參考:</p>
<p><a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#links" target="_blank" rel="external">Markdown Cheatsheet</a></p>
<h4 id="Q-在-Logdown-的-inline-code-中，「-」顯示不出來。-2014-08-04"><a href="#Q-在-Logdown-的-inline-code-中，「-」顯示不出來。-2014-08-04" class="headerlink" title="Q: 在 Logdown 的 inline code 中，「$」顯示不出來。(2014/08/04)"></a>Q: 在 Logdown 的 inline code 中，「<code>$</code>」顯示不出來。(2014/08/04)</h4><p>A: 由於 Logdown 支援 LaTeX，其中還支援 inline math 語法，就是「<code>$ expression $</code>」，但是這樣卻反而造成顯示普通程式時的困擾，如 jQuery 的代表符號「<code>$</code>」，如果產出的 HTML 要用「<code>&lt;code&gt;</code>」標籤強調，用 markdown 語法必須寫成「<code>$</code>」，但是在 Logdown 中，這樣寫的時候「<code>$</code>」這個符號是顯示不出來的。目前解決方法是直接使用 HTML 標籤：「<code>&lt;code&gt;$&lt;/code&gt;</code>」。</p>
<p>參考:</p>
<p><a href="http://nan.logdown.com/post/2013/08/03/write-complex-latex-equations-in-logdown-by-mathjax-support" title="在 logdown 裡面打複雜方程式" target="_blank" rel="external">在 logdown 裡面打複雜方程式</a></p>
<h3 id="參考文章："><a href="#參考文章：" class="headerlink" title="參考文章："></a>參考文章：</h3><ul>
<li><a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#links" target="_blank" rel="external">Markdown Cheatsheet</a></li>
<li><a href="http://nan.logdown.com/post/2013/08/03/write-complex-latex-equations-in-logdown-by-mathjax-support" title="在 logdown 裡面打複雜方程式" target="_blank" rel="external">在 logdown 裡面打複雜方程式</a></li>
<li><a href="http://www.inside.com.tw/2013/07/17/logdown-interview-a" title="台灣製造，新一代部落格平台 Logdown 團隊專訪" target="_blank" rel="external">台灣製造，新一代部落格平台 Logdown 團隊專訪</a></li>
</ul>
<h3 id="相關文章："><a href="#相關文章：" class="headerlink" title="相關文章："></a>相關文章：</h3><!-- cross references -->
<ul>
<li><a href="/2015/11/09/medium-vs-facebook-notes/" title="Medium 與 Facebook Notes 簡短試用">Medium 與 Facebook Notes 簡短試用</a>
</li>
</ul>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1457717047216-96440b68d44c?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Logdown 諧音 Look down&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/jrbreland&quot;&gt;John Breland 3&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;長期關注本部落格的朋友，可能有發現到，除了原本在 Blogger 的 &lt;a href=&quot;http://andmobiz.blogspot.tw/&quot; title=&quot;格物致知&quot;&gt;格物致知&lt;/a&gt; 之外，還有一個同步更新的網站 &lt;a href=&quot;http://fragmentsoftime.logdown.com/&quot; title=&quot;Fragments Of Time&quot;&gt;Fragments of Time&lt;/a&gt; 位於 &lt;a href=&quot;http://logdown.com/&quot;&gt;Logdown&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;這是因為我在之前的文章『&lt;a href=&quot;/2015/11/09/medium-vs-facebook-notes/&quot; title=&quot;Medium 與 Facebook Notes 簡短試用&quot;&gt;Medium 與 Facebook Notes 簡短試用&lt;/a&gt;』中提到的，厭倦了使用 HTML 撰寫部落格，所以在 Logdown &lt;a href=&quot;http://www.inside.com.tw/2013/07/17/logdown-interview-a&quot; title=&quot;台灣製造，新一代部落格平台 Logdown 團隊專訪&quot;&gt;發佈&lt;/a&gt;之後，曾經有一段時間，我是在 Logdown 先用 Markdown 語法撰寫文章，然後再將 HTML 複製到 Blogger 中。&lt;/p&gt;
&lt;p&gt;現在決定改用 &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt; / &lt;a href=&quot;https://hexo.io/zh-tw/&quot;&gt;hexo&lt;/a&gt;，上面的步驟就省了，現在我可以直接用 Markdown 寫文章，然後下個指令，就自動轉成 HTML 並且發佈。當初除了必須手動複製這個原因之外，其實在使用 Logdown 時，還遇到一些不方便的地方，並且做了一些筆記。我不確定現在這些問題是否依舊沒有改善，整理出來，給不幸遇到的朋友參考看看。&lt;/p&gt;
    
    </summary>
    
      <category term="Software" scheme="https://amobiz.github.io/categories/Software/"/>
    
    
      <category term="Markdown" scheme="https://amobiz.github.io/tags/Markdown/"/>
    
      <category term="pitfalls" scheme="https://amobiz.github.io/tags/pitfalls/"/>
    
      <category term="Logdown" scheme="https://amobiz.github.io/tags/Logdown/"/>
    
  </entry>
  
  <entry>
    <title>Gulp 學習筆記 - Glob 篇</title>
    <link href="https://amobiz.github.io/2015/11/14/gulp-glob/"/>
    <id>https://amobiz.github.io/2015/11/14/gulp-glob/</id>
    <published>2015-11-13T21:43:00.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1445551712021-28753c605094?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>glob &lt; pattern &lt; 在眾多相似物中尋找</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/c_ow">Oliver Wendel</a></div></div></div></div>
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p><a href="http://en.wikipedia.org/wiki/Glob_(programming)" target="_blank" rel="external">Glob</a> 類似於 <a href="/2014/08/04/regular-expression-javascript-study-notes-3-informal-bnf-grammar/" title="regular expression">regular expression</a>，但是僅用來匹配檔案路徑名稱。名稱由來是 UNIX 一個負責解析匹配檔案、叫做 global command 的模組。</p>
<p>本文針對 gulp 使用的 <a href="https://github.com/isaacs/node-glob" title="node-glob" target="_blank" rel="external">node-glob</a> 所支援的語法及選項進行說明，詳細的使用方法可參考 <a href="https://github.com/isaacs/node-glob" title="node-glob" target="_blank" rel="external">node-glob</a> 的說明。</p>
<a id="more"></a>
<h3 id="gulp-src-基本使用方式"><a href="#gulp-src-基本使用方式" class="headerlink" title="gulp.src() 基本使用方式"></a><code>gulp.src()</code> 基本使用方式</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">&#39;gulp&#39;</span>);</span><br><span class="line"><span class="keyword">var</span> uglify = <span class="built_in">require</span>(<span class="string">&#39;gulp-uglify&#39;</span>);</span><br><span class="line"></span><br><span class="line">gulp.src(<span class="string">&#39;<em>*/</em>.js&#39;</span>)</span><br><span class="line">    .pipe(uglify())</span><br><span class="line">    .pipe(gulp.dest(<span class="string">&#39;dist&#39;</span>));</span><br></pre></td></tr></table></figure>

<p><code>gulp.src()</code> 還支援陣列形式的多組 glob 參數：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gulp.src([<span class="string">&#39;<strong>/*.js&#39;</strong></span>, <span class="string">&#39;/*.ts&#39;</span>])</span><br><span class="line">    .pipe(uglify())</span><br><span class="line">    .pipe(gulp.dest(<span class="string">&#39;dist&#39;</span>));</span><br></pre></td></tr></table></figure>

<p>不過這個例子，可以使用後面介紹的『多選結構』加以簡化，而不必使用陣列：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gulp.src(<span class="string">&#39;<em>*/</em>.&#123;js,ts&#125;&#39;</span>)</span><br><span class="line">    .pipe(uglify())</span><br><span class="line">    .pipe(gulp.dest(<span class="string">&#39;dist&#39;</span>));</span><br></pre></td></tr></table></figure>

<h3 id="或者，直接使用-node-glob-來匹配檔案"><a href="#或者，直接使用-node-glob-來匹配檔案" class="headerlink" title="或者，直接使用 node-glob 來匹配檔案"></a>或者，直接使用 <code>node-glob</code> 來匹配檔案</h3><p>安裝</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save glob</span><br></pre></td></tr></table></figure>

<p>注意，雖然 GitHub 上的名稱為 <code>node-glob</code>，但是 npm 上卻是註冊為 <code>glob</code>，因此安裝時名稱必須為 <code>glob</code>。</p>
<p>呼叫 glob() 函數</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> glob = <span class="built_in">require</span>(<span class="string">&#39;glob&#39;</span>);</span><br><span class="line"><span class="keyword">var</span> files = glob(<span class="string">&#39;<em>*/</em>&#39;</span>, &#123; <span class="attr">dot</span>: <span class="literal">true</span> &#125;);</span><br></pre></td></tr></table></figure>

<p>檔案路徑匹配結果以陣列回傳，如果沒有匹配任何路徑或檔案，將回傳空陣列。</p>
<h3 id="Glob-元字元"><a href="#Glob-元字元" class="headerlink" title="Glob 元字元"></a>Glob 元字元</h3><p>前面兩個例子中，呼叫 <code>gulp.src()</code> 及 <code>glob()</code> 函數的第一個字串參數，就是所謂的 glob，其中 <code>*</code> 及 <code>/</code> 字元，就是所謂的 Glob 元字元。就是這些元字元，讓我們可以以簡單的模式，即可對應各種不同的檔案名稱。</p>
<h4 id="多選結構，『-』及『-』元字元："><a href="#多選結構，『-』及『-』元字元：" class="headerlink" title="多選結構，『{』及『}』元字元："></a>多選結構，『{』及『}』元字元：</h4><p>首先，在開始解析 glob 之前，其中的『{}』部份，會先被擴展開來，每個以『,』隔開的部份，都會造成整個 glob 被複製一次，成為個別的 glob。系統再分別針對這些個別的 glob，一一去比對檔案系統，然後將吻合的檔案，綜合起來，去除重複的檔案後傳回。</p>
<ol>
<li>若 glob 中包含兩個以上的『{}』部份，可想而知，被擴展的 glob 將以等比級數比例增加。譬如，<code>a{b,c}{d,e}?</code>，將擴展為：<code>abd?</code>, <code>abe?</code>, <code>acd?</code>, <code>ace?</code>。</li>
<li>『{}』中的部份，可以包含路徑分隔符號『/』，譬如，<code>a{/b/c,bc}</code> 將被擴展為 <code>a/b/c</code> 及 <code>abc</code>。</li>
</ol>
<h4 id="匹配路徑分隔符號，『-』元字元："><a href="#匹配路徑分隔符號，『-』元字元：" class="headerlink" title="匹配路徑分隔符號，『/』元字元："></a>匹配路徑分隔符號，『/』元字元：</h4><p>『/』元字元用來標示路徑分隔符號。除了『/』元字元及後面介紹的『**』元字元之外，其他元字元所能夠匹配的檔案路徑字元，皆不包含路徑分隔符號。</p>
<h4 id="任意字元，『-』元字元："><a href="#任意字元，『-』元字元：" class="headerlink" title="任意字元，『*』元字元："></a>任意字元，『*』元字元：</h4><p>在 glob 中若出現『*』元字元，則在該元字元的位置，可以匹配任意數量的任意檔案路徑字元，包含 0 個字元，也就是可以不匹配任何字元</p>
<p>譬如，<code>a*.js</code> 可以匹配 <code>a.js</code>, <code>a4.js</code>, <code>about.js</code>，但是不包含 <code>app/about.js</code>，因為不匹配『/』路徑分隔符號 (如前所述，匹配的字元不包含『/』路徑分隔符號，後不再贅述)。</p>
<h4 id="任意單一字元，『-』元字元："><a href="#任意單一字元，『-』元字元：" class="headerlink" title="任意單一字元，『?』元字元："></a>任意單一字元，『?』元字元：</h4><p>在 glob 中若出現『?』元字元，則在該元字元的位置，必須且只能匹配一個任意路徑字元。</p>
<p>譬如，<code>a?.js</code> 可以匹配 <code>a4.js</code> 及 <code>ax.js</code>，但是不能匹配 <code>a.js</code>,  <code>about.js</code>。</p>
<h4 id="字元組，『-』及『-』元字元："><a href="#字元組，『-』及『-』元字元：" class="headerlink" title="字元組，『[』及『]』元字元："></a>字元組，『[』及『]』元字元：</h4><p>與 regular expression 類似，字元組的意義為，匹配若干字元之一，且僅匹配一個路徑字元。同樣與 regular expression 類似地，若字元組的第一個字元為『!』或『^』，則為『排除型字元組』，代表該位置不得匹配字元組中列出的字元。</p>
<p>譬如，<code>a[bc].js</code> 可以匹配 <code>ab.js</code> 及 <code>ac.js</code>，但是不能匹配任何其他檔案路徑。<br>而，<code>a[!bc].js</code> 或 <code>a[^bc].js</code>，兩種寫法意義相同，可以匹配 <code>a4.js</code> 及 <code>ax.js</code> 及任何其他 <code>a</code> 開頭 <code>.js</code> 結尾的兩個字元檔名的檔案，但是就是不能匹配 <code>ab.js</code> 及 <code>ac.js</code>。</p>
<h4 id="模式匹配，『-pattern-pattern-pattern-』："><a href="#模式匹配，『-pattern-pattern-pattern-』：" class="headerlink" title="模式匹配，『@(pattern|pattern|pattern)』："></a>模式匹配，『@(pattern|pattern|pattern)』：</h4><p>匹配的路徑，必須吻合任一 pattern，實際的意義由開頭的字元決定：</p>
<ul>
<li>『!』：不得與列出的任一 pattern 吻合 (0)</li>
<li>『?』：可選的吻合，即可以不吻合，或者有一次吻合 (0~1)</li>
<li>『*』：可選的吻合，即可以不吻合，或者有一次以上吻合 (0~n)</li>
<li>『+』：必須吻合，至少一次，或一次以上 (1~n)</li>
<li>『@』：必須吻合，一次，且僅能一次吻合 (1)</li>
</ul>
<p>注意，『(』與『)』皆是合法的檔案名稱，所以若少了前面的『!』,『?』,『*』,『+』及『@』等元字元，則『(』與『)』不會被當作是模式匹配元字元。</p>
<h4 id="任意路徑，『-』元字元："><a href="#任意路徑，『-』元字元：" class="headerlink" title="任意路徑，『**』元字元："></a>任意路徑，『**』元字元：</h4><p>又稱為 <code>globstar</code> 元字元，只有當單獨出現在路徑中時才有效，也就是必須以 <code>**/</code> 或 <code>/**/</code>  的形式出現才有效。一旦發揮作用，可以匹配包含任意子目錄的任意路徑。也就是說，全部任意深度的子目錄都將被匹配。但是不包含 symlinked 目錄，即不包含目錄捷徑。</p>
<h3 id="選項"><a href="#選項" class="headerlink" title="選項"></a>選項</h3><p><code>gulp.src()</code> 及 <code>glob()</code> 函數的第二個參數是 <code>options</code>，可以用來改變 glob 匹配的行為。</p>
<h4 id="dot-選項與-字元"><a href="#dot-選項與-字元" class="headerlink" title="dot 選項與 . 字元"></a><code>dot</code> 選項與 <code>.</code> 字元</h4><p><code>.</code> 字元並非元字元。在 Linux 下，目錄或檔案名稱若以 <code>.</code> 字元開頭，則為隱藏檔案。因此，glob 預設不會匹配以 <code>.</code> 字元開頭的檔案，除非明確在 glob 中明確寫出 <code>.</code> 字元。即使在 Windows 底下，也保留相同的行為。</p>
<p>譬如：<code>a*</code> 不會匹配 <code>.access</code> 檔案或目錄，<code>**/b*</code> 不會匹配 <code>abc/.batch</code> 檔案或目錄，除非分別明確寫為 <code>.a*</code> 及 <code>**/.b*</code>。</p>
<p>如果程式經常需要匹配隱藏檔案，可以在呼叫 <code>glob()</code> 函數時，在第二個參數提供 <code>{ dot: true }</code> 選項，讓 <code>glob()</code> 函數像一般字元一樣，對待 <code>.</code> 字元。</p>
<h4 id="base-選項"><a href="#base-選項" class="headerlink" title="base 選項"></a><code>base</code> 選項</h4><p>在 gulp 中，要特別注意的是 base 的決定與作用。</p>
<p>base 的作用，是匹配檔案的路徑參考原點。簡單地說，就是『匹配檔案的路徑，要從哪裡算起』。</p>
<p>所有匹配的檔案，其路徑必須以相對於 base 所指定的路徑為起點，以相對路徑表示。也可以這樣解讀：base 的作用是，最終匹配的檔案路徑及名稱，將排除 base 路徑部分。所以，base 的值，將會影響檔案輸出時是否包含目錄、若包含目錄，又是哪些目錄路徑。</p>
<p>那麼，base 是如何決定的呢? Gulp 關於 base 屬性的預設值的說明提到：</p>
<blockquote>
<p>Default: everything before a glob starts</p>
</blockquote>
<p>這恐怕有點誤導，事實上整個 pattern 都是 glob，而 gulp 的說明恐怕把 glob 狹義解釋為是指普通文字以外的部分，譬如 <code>*</code>, <code>?</code> 等通配字元。</p>
<p>正確地說，base 的預設值，是指未動用到通配字元而能夠匹配的路徑的部分。只有兩種可能情況符合：</p>
<p>1.glob 中不含路徑分隔符號，也就是不匹配任何目錄。所以 base 就是 <code>.</code>。<br>2.glob 中含有路徑分隔符號，但是路徑是直接寫出，不含任何通配字元。一旦路徑中含有通配字元，就會被排除在 base 路徑之外。</p>
<p>譬如：</p>
<table>
<thead>
<tr>
<th>glob</th>
<th>exists</th>
<th>base</th>
<th>match</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>app/js/</b>&#42;.js</td>
<td><b>app/js/</b>app.js</td>
<td><b>app/js/</b></td>
<td>app.js</td>
</tr>
<tr>
<td><b>app/views/</b><span style="color:red">&#42;&#42;/</span>&#42;.html</td>
<td><b>app/views/</b><span style="color:red">options/</span>options.html</td>
<td><b>app/views/</b></td>
<td><span style="color:red">options/</span>options.html</td>
</tr>
<tr>
<td><b>app/i18n/</b><span style="color:red">zh&#42;/</span>&#42;.json</td>
<td><b>app/i18n/</b><span style="color:red">zh_TW/</span>message.json</td>
<td><b>app/i18n/</b></td>
<td><span style="color:red">zh_TW/</span>message.json</td>
</tr>
</tbody>
</table>
<p>上面是以 base 的觀點來說明。反過來說，對於匹配的檔案來說，一旦匹配成功，若匹配的檔案含有路徑，而該路徑是透過通配字元匹配的，則匹配的路徑將包含在匹配檔案的匹配路徑中。</p>
<p>所以：</p>
<ol>
<li><p>在指定 glob 時若不含目錄通配字元，則找到的檔案，將忽視其路徑部分，因此，由 <code>gulp.src(&#39;src/*.js&#39;)</code> 對應到 <code>gulp.dest()</code> 時，檔案將不含目錄，直接寫到 <code>gulp.dest()</code> 指定的目錄。</p>
</li>
<li><p>若指定了 <code>?</code>, <code>*</code> 或 <code>**</code> 目錄通用字元，由於目錄通用字元將被視為檔案的一部分，所以輸出到 <code>gulp.dest()</code> 時，檔案將被寫入到目錄通用字元所匹配的路徑下。</p>
</li>
</ol>
<p>譬如：</p>
<p>假設存在 <code>app/views/options/options.html</code>，且一律指定 <code>dest(&#39;dist&#39;)</code>：</p>
<table>
<thead>
<tr>
<th>gulp.src(glob)</th>
<th>base (粗體部分) / match path (紅色部分)</th>
<th>gulp.dest(&#39;dist&#39;)</th>
</tr>
</thead>
<tbody>
<tr>
<td>src(&#39;<b>app/</b>&#42;/&#42;.html&#39;)</td>
<td><b>app/</b><span style="color:red">views/</span> (假設 app/views 目錄下無 .html 檔案。)</td>
<td>N/A (假設 app/views 目錄下無 .html 檔案。)</td>
</tr>
<tr>
<td>src(&#39;<b>app/</b>&#42;&#42;/&#42;.html&#39;)</td>
<td><b>app/</b><span style="color:red">views/options/</span>options.html</td>
<td>dist/<span style="color:red">views/options/</span>options.html</td>
</tr>
<tr>
<td>src(&#39;<b>app/views/</b>&#42;/&#42;.html&#39;)</td>
<td><b>app/views/</b><span style="color:red">options/</span>options.html</td>
<td>dist/<span style="color:red">options/</span>options.html</td>
</tr>
<tr>
<td>src(&#39;<b>app/views/</b>&#42;&#42;/&#42;.html&#39;)</td>
<td><b>app/views/</b><span style="color:red">options/</span>options.html</td>
<td>dist/<span style="color:red">options/</span>options.html</td>
</tr>
<tr>
<td>src(&#39;<b>app/views/options/</b>&#42;.html&#39;)</td>
<td><b>app/views/options/</b>options.html</td>
<td>dist/options.html</td>
</tr>
<tr>
<td>src(&#39;&#42;/views/&#42;&#42;/&#42;.html&#39;)</td>
<td><span style="color:red">app/views/options/</span>options.html</td>
<td>dist/<span style="color:red">app/views/options/</span>options.html</td>
</tr>
</tbody>
</table>
<p>3.透過指定 &quot;base&quot; property 來改變參照路徑；或者，在 <code>gulp.dest()</code> 指定輸出路徑。</p>
<p>下面的範例中，對 <code>gulp.src()</code> 指定了兩個不同的 glob：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">gulp.src([ <span class="string">&#39;bower.json&#39;</span>, <span class="string">&#39;app/manifest.json&#39;</span> ])</span><br><span class="line">      .pipe(plugins.configSync(&#123;</span><br><span class="line">           <span class="attr">fields</span>: [ <span class="string">&#39;version&#39;</span> ]</span><br><span class="line">      &#125;))</span><br><span class="line">      <span class="comment">// 注意這裡若指定為 gulp.dest(&#39;/&#39;) 則真的會輸出到磁碟的根目錄。</span></span><br><span class="line">      .pipe(gulp.dest(<span class="string">&#39;.&#39;</span>));</span><br></pre></td></tr></table></figure>

<p>但是由於兩個檔案的根目錄不同，同時目錄也都未使用通配字元，所以最後匹配的檔案都不含路徑，因此，使用 <code>gulp.dest(&#39;.&#39;)</code> 輸出時，將輸出到專案目錄 (假設專案叫做 project) 下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">project</span><br><span class="line">    +-- bower.json</span><br><span class="line">    +-- manifest.json</span><br></pre></td></tr></table></figure>

<p>這裡當然也可以藉由指定通配字元 <code>&quot;*/manifest.json&quot;</code> 從而避免 base 的指定。但是，這樣的寫法，很可能會殃及無辜檔案。同時，最重要的是，反而不容易看出我們真正的意圖：就是只要 app/manifest.json 這個檔案。</p>
<p>因此，這裡可以藉由指定 <code>base</code> property 來改變參照路徑，強迫匹配的檔案以目前目錄為相對路徑表示，從而，得以保留檔案的目錄資訊。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">gulp.src([ <span class="string">&#39;bower.json&#39;</span>, <span class="string">&#39;app/manifest.json&#39;</span> ], &#123; <span class="attr">base</span>: <span class="string">&#39;.&#39;</span> &#125;)</span><br><span class="line">      .pipe(plugins.configSync(&#123;</span><br><span class="line">           <span class="attr">fields</span>: [ <span class="string">&#39;version&#39;</span> ]</span><br><span class="line">      &#125;))</span><br><span class="line">      .pipe(gulp.dest(<span class="string">&#39;.&#39;</span>));</span><br></pre></td></tr></table></figure>

<p>輸出如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">project</span><br><span class="line">    +-- app</span><br><span class="line">            +-- manifest.json</span><br><span class="line">    +-- bower.json</span><br></pre></td></tr></table></figure>

<p>當匹配的檔案都在共同的子目錄之下時，若輸出目錄仍然希望含有特定目錄，則可以在 <code>gulp.dest()</code> 指定輸出路徑：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gulp.src(<span class="string">&#39;app/scripts/<em>*/</em>.js&#39;</span>)</span><br><span class="line">    .pipe(uglify())</span><br><span class="line">    .pipe(gulp.dest(<span class="string">&#39;dist/scripts&#39;</span>));</span><br></pre></td></tr></table></figure>

<p>4.透過 <a href="https://github.com/armed/gulp-flatten" target="_blank" rel="external"><code>gulp-flatten</code></a> 可消除匹配的路徑。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> flatten = <span class="built_in">require</span>(<span class="string">&#39;gulp-flatten&#39;</span>);</span><br><span class="line">gulp.src(<span class="string">&#39;bower_components/<em>*/</em>.min.js&#39;</span>)</span><br><span class="line">    .pipe(flatten())</span><br><span class="line">    .pipe(gulp.dest(<span class="string">&#39;build/js&#39;</span>));</span><br></pre></td></tr></table></figure>

<h3 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h3><ul>
<li><a href="http://en.wikipedia.org/wiki/Glob_(programming)" target="_blank" rel="external">Glob</a></li>
<li><a href="https://github.com/gulpjs/gulp/blob/master/docs/recipes/maintain-directory-structure-while-globbing.md" title="Maintain Directory Structure while Globbing" target="_blank" rel="external">Maintain Directory Structure while Globbing</a></li>
<li><a href="https://github.com/gulpjs/gulp/blob/master/docs/API.md#optionsbase" title="gulp.src() options.base" target="_blank" rel="external">gulp.src(): options.base</a></li>
<li><a href="https://github.com/isaacs/node-glob" title="node-glob" target="_blank" rel="external">node-glob</a></li>
<li><a href="https://github.com/armed/gulp-flatten" title="gulp-flatten" target="_blank" rel="external">gulp-flatten</a></li>
</ul>
<h3 id="相關文章"><a href="#相關文章" class="headerlink" title="相關文章"></a>相關文章</h3><!-- cross references -->
<div><ul>
<li><a href="/2015/11/12/gulp-internals/" title="Gulp 學習筆記 - Gulp Internals (3.x)">Gulp 學習筆記 - Gulp Internals (3.x)</a></li>
</ul>
</div>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1445551712021-28753c605094?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;glob &amp;lt; pattern &amp;lt; 在眾多相似物中尋找&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/c_ow&quot;&gt;Oliver Wendel&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Glob_(programming)&quot;&gt;Glob&lt;/a&gt; 類似於 &lt;a href=&quot;/2014/08/04/regular-expression-javascript-study-notes-3-informal-bnf-grammar/&quot; title=&quot;regular expression&quot;&gt;regular expression&lt;/a&gt;，但是僅用來匹配檔案路徑名稱。名稱由來是 UNIX 一個負責解析匹配檔案、叫做 global command 的模組。&lt;/p&gt;
&lt;p&gt;本文針對 gulp 使用的 &lt;a href=&quot;https://github.com/isaacs/node-glob&quot; title=&quot;node-glob&quot;&gt;node-glob&lt;/a&gt; 所支援的語法及選項進行說明，詳細的使用方法可參考 &lt;a href=&quot;https://github.com/isaacs/node-glob&quot; title=&quot;node-glob&quot;&gt;node-glob&lt;/a&gt; 的說明。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="notes" scheme="https://amobiz.github.io/tags/notes/"/>
    
      <category term="Gulp" scheme="https://amobiz.github.io/tags/Gulp/"/>
    
      <category term="glob" scheme="https://amobiz.github.io/tags/glob/"/>
    
  </entry>
  
  <entry>
    <title>Gulp 學習筆記 - Gulp Internals (3.x)</title>
    <link href="https://amobiz.github.io/2015/11/12/gulp-internals/"/>
    <id>https://amobiz.github.io/2015/11/12/gulp-internals/</id>
    <published>2015-11-12T12:38:00.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1458217222755-d1c4985b0852?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Gulp</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/mrs_m">Maria Vernigora</a></div></div></div></div>
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>最近正在開發一個幫助重複使用 gulp task 的工具：<a href="https://gulp-cookery.github.io/gulp-chef/" target="_blank" rel="external">gulp-chef</a>，所以分別研究了 Gulp 3.9 和 Gulp 4.0 的原始碼，以下是 Gulp 3.9 的快速筆記。</p>
<a id="more"></a>
<h3 id="解析-Gulp-原始碼"><a href="#解析-Gulp-原始碼" class="headerlink" title="解析 Gulp 原始碼"></a>解析 Gulp 原始碼</h3><p>進入點：</p>
<p><a href="https://github.com/gulpjs/gulp/blob/master/index.js" target="_blank" rel="external">gulp/index.js</a></p>
<p><code>Gulp</code> 繼承 <code>Orchestrator</code>:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">var util = require(&apos;util&apos;);</span><br><span class="line">var Orchestrator = require(&apos;orchestrator&apos;);</span><br><span class="line">var gutil = require(&apos;gulp-util&apos;);</span><br><span class="line">var deprecated = require(&apos;deprecated&apos;);</span><br><span class="line">var vfs = require(&apos;vinyl-fs&apos;);</span><br><span class="line"></span><br><span class="line">function Gulp() &#123;</span><br><span class="line">    Orchestrator.call(this);</span><br><span class="line">&#125;</span><br><span class="line">util.inherits(Gulp, Orchestrator);</span><br></pre></td></tr></table></figure>

<p><code>Orchestrator</code> 的定義：</p>
<p><a href="https://github.com/orchestrator/orchestrator" target="_blank" rel="external">orchestrator/orchestrator</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">Orchestrator.prototype.add = function (name, dep, fn) &#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    this.tasks[name] = &#123;</span><br><span class="line">        fn: fn,</span><br><span class="line">        dep: dep,</span><br><span class="line">        name: name</span><br><span class="line">    &#125;;</span><br><span class="line">    return this;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">Orchestrator.prototype.task = function (name, dep, fn) &#123;</span><br><span class="line">    if (dep || fn) &#123;</span><br><span class="line">        // alias for add, return nothing rather than this</span><br><span class="line">        this.add(name, dep, fn);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        return this.tasks[name];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>可以看到：</p>
<ol>
<li><code>gulp.task()</code> 透過 <code>gulp.add()</code> 函數，將定義的 task 包裝為物件，儲存在 <code>gulp.tasks[]</code> 陣列中。</li>
<li>透過 <code>gulp.task()</code> 函數，其實可以取得特定名稱的 task 物件的完整定義。</li>
</ol>
<p>下面這一行乍看之下似乎是多餘的，因為 <code>Gulp</code> 繼承 <code>Orchestrator</code> 時，就已經同時取得 <code>.add()</code> 及 <code>.task()</code> 函數，但是 <code>Orchestrator.prototype.task()</code> 實際上將工作委託給 <code>Orchestrator.prototype.add()</code> 函數，然而<code>task()</code> 並未改變任何行為，因此，其實只要讓 <code>task()</code> 也指向 <code>add()</code> 即可：</p>
<p><code>gulp.task()</code> 改直接引用自 <code>Orchestrator.prototype.add</code> 成為別名:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Gulp.prototype.task = Gulp.prototype.add;</span><br></pre></td></tr></table></figure>

<p><code>gulp.src()</code>, <code>gulp.dest()</code> 直接引用自 <code>vinyl-fs</code>:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Gulp.prototype.src = vfs.src;</span><br><span class="line">Gulp.prototype.dest = vfs.dest;</span><br></pre></td></tr></table></figure>

<p><code>gulp.watch()</code> 將監看對象陣列攤平，丟給 <code>vinyl-fs</code> 的 <code>.watch()</code> 函數：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Gulp.prototype.watch = function(glob, opt, fn) &#123;</span><br><span class="line">    if (typeof opt === &apos;function&apos; || Array.isArray(opt)) &#123;</span><br><span class="line">        fn = opt;</span><br><span class="line">        opt = null;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    // Array of tasks given</span><br><span class="line">    if (Array.isArray(fn)) &#123;</span><br><span class="line">        return vfs.watch(glob, opt, function() &#123;</span><br><span class="line">            this.start.apply(this, fn);</span><br><span class="line">        &#125;.bind(this));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    return vfs.watch(glob, opt, fn);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><code>gulp.src()</code> 透過 <code>glob-stream</code> 將 glob 轉換為 <code>vinyl</code> (File)(vinyl 是黑膠唱片的意思，表示承載內容的物品)：</p>
<p><a href="https://github.com/wearefractal/vinyl-fs/blob/master/lib/src/index.js" target="_blank" rel="external">vinyl-fs/lib/src/index.js</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">var assign = require(&apos;object-assign&apos;);</span><br><span class="line">var through = require(&apos;through2&apos;);</span><br><span class="line">var gs = require(&apos;glob-stream&apos;);</span><br><span class="line">var File = require(&apos;vinyl&apos;);</span><br><span class="line">var duplexify = require(&apos;duplexify&apos;);</span><br><span class="line">var merge = require(&apos;merge-stream&apos;);</span><br><span class="line">var sourcemaps = require(&apos;gulp-sourcemaps&apos;);</span><br><span class="line">var filterSince = require(&apos;../filterSince&apos;);</span><br><span class="line">var isValidGlob = require(&apos;is-valid-glob&apos;);</span><br><span class="line"></span><br><span class="line">var getContents = require(&apos;./getContents&apos;);</span><br><span class="line">var resolveSymlinks = require(&apos;./resolveSymlinks&apos;);</span><br><span class="line"></span><br><span class="line">function createFile(globFile, enc, cb) &#123;</span><br><span class="line">    cb(null, new File(globFile));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function src(glob, opt) &#123;</span><br><span class="line">    var options = assign(&#123;</span><br><span class="line">        read: true,</span><br><span class="line">        buffer: true,</span><br><span class="line">        sourcemaps: false,</span><br><span class="line">        passthrough: false</span><br><span class="line">    &#125;, opt);</span><br><span class="line"></span><br><span class="line">    var inputPass;</span><br><span class="line"></span><br><span class="line">    if (!isValidGlob(glob)) &#123;</span><br><span class="line">        throw new Error(&apos;Invalid glob argument: &apos; + glob);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    var globStream = gs.create(glob, options);</span><br><span class="line"></span><br><span class="line">    var outputStream = globStream</span><br><span class="line">        .pipe(resolveSymlinks())</span><br><span class="line">        .pipe(through.obj(createFile));</span><br><span class="line"></span><br><span class="line">    if (options.since != null) &#123;</span><br><span class="line">        outputStream = outputStream</span><br><span class="line">        .pipe(filterSince(options.since));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    if (options.read !== false) &#123;</span><br><span class="line">        outputStream = outputStream</span><br><span class="line">            .pipe(getContents(options));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    if (options.passthrough === true) &#123;</span><br><span class="line">        inputPass = through.obj();</span><br><span class="line">        outputStream = duplexify.obj(inputPass, merge(outputStream, inputPass));</span><br><span class="line">    &#125;</span><br><span class="line">    if (options.sourcemaps === true) &#123;</span><br><span class="line">        outputStream = outputStream</span><br><span class="line">            .pipe(sourcemaps.init(&#123;loadMaps: true&#125;));</span><br><span class="line">    &#125;</span><br><span class="line">    globStream.on(&apos;error&apos;, outputStream.emit.bind(outputStream, &apos;error&apos;));</span><br><span class="line">    return outputStream;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">module.exports = src;</span><br></pre></td></tr></table></figure>

<p><a href="https://github.com/wearefractal/glob-stream/blob/master/index.js" target="_blank" rel="external">glob-stream/index.js</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">// create globbing stuff</span><br><span class="line">var globber = new glob.Glob(ourGlob, ourOpt);</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">globber.on(&apos;match&apos;, function(filename) &#123;</span><br><span class="line">    found = true;</span><br><span class="line"></span><br><span class="line">    stream.write(&#123;</span><br><span class="line">        cwd: opt.cwd,</span><br><span class="line">        base: basePath,</span><br><span class="line">        path: path.resolve(opt.cwd, filename)</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><code>vinyl</code> 雖然有專屬的 module （class），也有許多 method 可用:</p>
<p><a href="https://github.com/wearefractal/vinyl" target="_blank" rel="external">wearefractal/vinyl</a></p>
<p>但是對於 <code>Gulp</code> 來說，它只需要四個欄位：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    base: string,</span><br><span class="line">    cwd: string,</span><br><span class="line">    path: string, // filename</span><br><span class="line">    contents: any // file content</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在 <code>Gulp</code> 中，由 <code>gulp.src()</code> 開始，一直到 <code>gulp.dest()</code> 為止，傳遞的都是只有上面四個欄位的 <code>vinyl</code>。</p>
<p>所以，像 <a href="http://browserify.org/" target="_blank" rel="external"><code>Browserify</code></a> 這樣的工具若要能夠供 <code>Gulp</code> 使用，必須透過 <a href="https://github.com/hughsk/vinyl-source-stream" target="_blank" rel="external"><code>vinyl-source-stream</code></a> 這樣的工具，將輸出的文字內容，轉換為 vinyl 的格式，同時賦予檔案名稱：</p>
<h3 id="同場加映，解析-vinyl-source-stream-原始碼"><a href="#同場加映，解析-vinyl-source-stream-原始碼" class="headerlink" title="同場加映，解析 vinyl-source-stream 原始碼"></a>同場加映，解析 vinyl-source-stream 原始碼</h3><p><a href="https://github.com/hughsk/vinyl-source-stream" target="_blank" rel="external">hughsk/vinyl-source-stream</a> / <a href="https://github.com/hughsk/vinyl-source-stream/blob/master/index.js" target="_blank" rel="external">index.js</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">var through2 = require(&apos;through2&apos;)</span><br><span class="line">var File = require(&apos;vinyl&apos;)</span><br><span class="line">var path = require(&apos;path&apos;)</span><br><span class="line"></span><br><span class="line">module.exports = function (filename, baseDir) &#123;</span><br><span class="line">    var ins = through2();    // .......................................................(3)</span><br><span class="line">    var out = false</span><br><span class="line"></span><br><span class="line">    var opts = &#123;</span><br><span class="line">        contents: ins        // ...............................................(3)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    // NOTE: 注意其 opts.path 的決定方式</span><br><span class="line">    if (filename) opts.path = path.resolve(baseDir || process.cwd(), filename)</span><br><span class="line">    if (baseDir) opts.base = baseDir</span><br><span class="line"></span><br><span class="line">    var file = new File(opts);    //  ...............................................(2)</span><br><span class="line"></span><br><span class="line">    // 回傳的是 through2 物件，注意 objectMode: true。........................................(1)</span><br><span class="line">    return through2(&#123;</span><br><span class="line">        objectMode: true</span><br><span class="line">    &#125;, function(chunk, enc, next) &#123;</span><br><span class="line">        if (!out) &#123;</span><br><span class="line">            this.push(file) // NOTE: 這裡推入 vinyl 物件。.......................(2)</span><br><span class="line">            out = true</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        ins.push(chunk)     // ................................................(4)</span><br><span class="line">        next()</span><br><span class="line">    &#125;, function() &#123;</span><br><span class="line">        ins.push(null)        // ................................................(5)</span><br><span class="line">        this.push(null)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到：</p>
<ol>
<li>回傳的是 <a href="https://github.com/rvagg/through2" target="_blank" rel="external"><code>through2</code></a> 物件 (<code>through2</code> 是 node 常用的 stream 包裝函數，可以避免使用繼承的方式撰寫 stream 物件)。</li>
<li><code>through2</code> 接受的第一個函數，稱為 <code>transformFunction</code>，是當 stream 在傳輸資料時，負責進行內容轉換工作。<br> 使用 <code>through2</code> 時，透過 <code>push()</code> 函數將實際上要輸出的內容，加到輸出 stream 中。可以看到這裡要傳輸的是 <code>vinyl</code> 物件，所以回傳的 <code>through2</code> 物件，在定義的時候，必須指定 <code>objectMode: true</code>。</li>
<li>前面提到 <code>vinyl</code> 物件最重要的四大屬性，其中 <code>contents</code> 是用來傳輸 / 存放檔案內容的。在這裡，<code>contents</code> 實際上又是一個 <code>through2</code> 物件，注意這裡沒有定義 <code>objectMode</code>，所以這是一個 <code>byte stream</code>。</li>
<li>在每當 up stream 有資料傳送進來時，便呼叫 <code>push()</code> 函數，將資料推送到 buffer 中。</li>
<li><code>through2</code> 接受的第二個函數，稱為 <code>flushFunction</code>，是當 stream 資料傳輸完畢時，負責進行最後的收尾清理工作。在這裡都 <code>push(null)</code> 表示資料結尾。</li>
</ol>
<h3 id="相關文章"><a href="#相關文章" class="headerlink" title="相關文章"></a>相關文章</h3><!-- cross references -->
<div><ul>
<li><a href="/2015/11/14/gulp-glob/" title="Gulp 學習筆記 - Glob 篇">Gulp 學習筆記 - Glob 篇</a></li>
</ul>
</div>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1458217222755-d1c4985b0852?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Gulp&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/mrs_m&quot;&gt;Maria Vernigora&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;最近正在開發一個幫助重複使用 gulp task 的工具：&lt;a href=&quot;https://gulp-cookery.github.io/gulp-chef/&quot;&gt;gulp-chef&lt;/a&gt;，所以分別研究了 Gulp 3.9 和 Gulp 4.0 的原始碼，以下是 Gulp 3.9 的快速筆記。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="notes" scheme="https://amobiz.github.io/tags/notes/"/>
    
      <category term="Gulp" scheme="https://amobiz.github.io/tags/Gulp/"/>
    
  </entry>
  
  <entry>
    <title>Medium 與 Facebook Notes 簡短試用</title>
    <link href="https://amobiz.github.io/2015/11/09/medium-vs-facebook-notes/"/>
    <id>https://amobiz.github.io/2015/11/09/medium-vs-facebook-notes/</id>
    <published>2015-11-09T14:05:00.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1442458370899-ae20e367c5d8?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>看不中意 &lt; 努力看清楚 &lt; 老眼昏花</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/alexharvey">Alex Harvey</a></div></div></div></div>
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>由於實在是受不了使用 HTML 寫部落格的瑣碎，前一陣子決定將部落格由 Blogger 移轉出來之後，除了 <a href="https://pages.github.com/" target="_blank" rel="external">GitHub Pages</a> / <a href="https://hexo.io/zh-tw/" target="_blank" rel="external">hexo</a> 這個可以使用 Markdown 的組合之外，其實還評估了幾個平台，尤其其中 Facebook Notes 剛剛<a href="https://free.com.tw/facebook-notes/" title="臉書「網誌」功能全新改版，Notes 專為長篇圖文分享而生" target="_blank" rel="external">全新改版</a>，吸引了不少的目光，所以就順勢評估了一下 <a href="https://medium.com/" target="_blank" rel="external">Medium</a> 和 <a href="https://www.facebook.com/help/115983655152193" target="_blank" rel="external">Facebook Notes</a>。</p>
<a id="more"></a>
<h3 id="Medium"><a href="#Medium" class="headerlink" title="Medium"></a>Medium</h3><h4 id="優點"><a href="#優點" class="headerlink" title="優點"></a>優點</h4><p>提供簡單的格式化工具，可以輕鬆寫出排版漂亮的文章。</p>
<h4 id="問題"><a href="#問題" class="headerlink" title="問題"></a>問題</h4><ol>
<li>Code block 根本是災難。</li>
</ol>
<p>操作方式：選擇程式碼，然後按 Ctrl+Alt+6 (PC), Cmd-6 (Mac)</p>
<ul>
<li>常常將鄰近的區塊含入。</li>
<li>無法支援 inline code span，選取段落裡的關鍵字要標示為程式碼時，會變成整個段落被標示為程式碼。</li>
</ul>
<ol>
<li>無法插入網路圖片(必須上傳到 Medium)</li>
<li>不支援 markdown</li>
</ol>
<h3 id="Facebook-Notes"><a href="#Facebook-Notes" class="headerlink" title="Facebook Notes"></a>Facebook Notes</h3><h4 id="優點-1"><a href="#優點-1" class="headerlink" title="優點"></a>優點</h4><p>根本是抄襲 medium。</p>
<h4 id="問題-1"><a href="#問題-1" class="headerlink" title="問題"></a>問題</h4><ol>
<li>一樣不支援 code block</li>
<li>一樣無法插入網路圖片(必須上傳到 Facebook)</li>
<li>一樣不支援 markdown</li>
<li>文章不能太長，最多只能有 300 個 item (應該是指段落):</li>
</ol>
<blockquote>
<p> Your note is too long. Please limit the number of paragraphs, list items, headers, and embedded media to 300 items.</p>
</blockquote>
<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>Medium 和 Facebook Notes 都是適合一般軟性文章的平台；但是由於無法做到必要的格式化，顯然不適合以技術性內容為主的作者 (不是只有我持這樣的<a href="https://medium.com/@sastanin/code-medium-492ed410bed5" title="Code Medium: Is Medium suitable for writing about programming?" target="_blank" rel="external">看法</a>)。</p>
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1442458370899-ae20e367c5d8?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;看不中意 &amp;lt; 努力看清楚 &amp;lt; 老眼昏花&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/alexharvey&quot;&gt;Alex Harvey&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;由於實在是受不了使用 HTML 寫部落格的瑣碎，前一陣子決定將部落格由 Blogger 移轉出來之後，除了 &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt; / &lt;a href=&quot;https://hexo.io/zh-tw/&quot;&gt;hexo&lt;/a&gt; 這個可以使用 Markdown 的組合之外，其實還評估了幾個平台，尤其其中 Facebook Notes 剛剛&lt;a href=&quot;https://free.com.tw/facebook-notes/&quot; title=&quot;臉書「網誌」功能全新改版，Notes 專為長篇圖文分享而生&quot;&gt;全新改版&lt;/a&gt;，吸引了不少的目光，所以就順勢評估了一下 &lt;a href=&quot;https://medium.com/&quot;&gt;Medium&lt;/a&gt; 和 &lt;a href=&quot;https://www.facebook.com/help/115983655152193&quot;&gt;Facebook Notes&lt;/a&gt;。&lt;/p&gt;
    
    </summary>
    
      <category term="Software" scheme="https://amobiz.github.io/categories/Software/"/>
    
    
      <category term="HTML" scheme="https://amobiz.github.io/tags/HTML/"/>
    
      <category term="Markdown" scheme="https://amobiz.github.io/tags/Markdown/"/>
    
      <category term="Blogger" scheme="https://amobiz.github.io/tags/Blogger/"/>
    
      <category term="Medium" scheme="https://amobiz.github.io/tags/Medium/"/>
    
      <category term="Facebook Notes" scheme="https://amobiz.github.io/tags/Facebook-Notes/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript Promise: resolver vs. handler</title>
    <link href="https://amobiz.github.io/2015/10/02/javascript-promise-resolver-vs-handler/"/>
    <id>https://amobiz.github.io/2015/10/02/javascript-promise-resolver-vs-handler/</id>
    <published>2015-10-02T09:55:08.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1439920120577-eb3a83c16dd7?crop=entropy&amp;fit=crop&amp;fm=jpg&amp;h=975&amp;ixjsv=2.1.0&amp;ixlib=rb-0.3.5&amp;q=80&amp;w=1900" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Promise &lt; 承諾</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/liane">Liane Metzler</a></div></div></div></div>
<h3 id="鑽牛角尖時間：Promise-resolver-vs-handler"><a href="#鑽牛角尖時間：Promise-resolver-vs-handler" class="headerlink" title="鑽牛角尖時間：Promise: resolver vs handler"></a>鑽牛角尖時間：Promise: resolver vs handler</h3><p>不曉得讀者有沒有同樣的困擾：<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" rel="external"><code>Promise</code></a> 的 resolver 與 handler 有著不同的 signature (形式) 及啟動方式，看起來非常地不協調：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// resolver 由 Promise() constructor 啟動</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(resolver)</span><br><span class="line">    <span class="comment">// handler 由 then() 函數啟動</span></span><br><span class="line">    .then(handler, onerror);</span><br><span class="line"></span><br><span class="line"><span class="comment">// resolver 具有 function (resolve, reject) 的形式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolver</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    fs.readFile(<span class="string">&#39;readme.md&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            reject(err);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            resolve(data);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// fulfillment handler 具有 function (result) 的形式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handler</span>(<span class="params">markdown</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> marked(markdown);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// rejection handler 具有 function (reason) 的形式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onerror</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">    log(error);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<a id="more"></a>
<p>這樣的設計，著眼點是：對於已經使用 promise 模式的函數，早就已經回傳 <code>Promise</code> 物件了，使用上不涉及到 <code>Promise</code> 物件的建立，只要直接取用其回傳的 <code>Promise</code> 物件，呼叫其 <code>#then()</code> 函數即可，像這樣：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$.get(url)</span><br><span class="line">    .then(<span class="function"><span class="keyword">function</span> (<span class="params">text</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure>

<p>至於那些需要自行處理 <code>Promise</code> 物件的函數，是尚未使用 promise 模式的函數。這些函數，若要使用 promise 模式<sup>[<a href="#promisify">註1</a>]</sup>，必須先設法建立一個 <code>Promise</code> 物件，然後 <code>Promise</code> 物件在稍後呼叫指定的 resolver 函數，而 resolver 函數異步處理完畢後，再透過 <code>resolve()</code> 與 <code>reject()</code> 函數來完成整個處理過程。既然建立 <code>Promise</code> 物件和呼叫 resolver 函數這兩個動作似乎具有時間上的耦合，因此，這個責任就被設計，並落在 <code>Promise</code> 物件的 constructor 上：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(resolver);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolver</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    fs.readFile(<span class="string">&#39;readme.md&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            reject(err);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            resolve(data);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>但是，真的不能處理得更好，更一致嗎？</p>
<h4 id="嘗試-1：讓-resolver-的-signature-與-handler-一致"><a href="#嘗試-1：讓-resolver-的-signature-與-handler-一致" class="headerlink" title="嘗試 1：讓 resolver 的 signature 與 handler 一致"></a>嘗試 1：讓 resolver 的 signature 與 handler 一致</h4><p>首先，必須先建立一個 <code>Promise</code> 物件。由於 <code>Promise</code> 提供有 <code>Promise.resolve()</code> 函數，可以建立一個已經 fulfilled 的 <code>Promise</code> 物件，顯然可以利用它來幫忙建立初始的 <code>Promise</code> 物件。有了初始的 <code>Promise</code> 物件，就可以對它呼叫 <code>#then()</code> 函數，然後以一致的 signature 撰寫 resolver 與 handler 了。</p>
<p>然而，因為要讓 resolver 與 handler 使用一致的 signature，這麼一來，resolver 就無法取得 <code>resolve()</code> 與 <code>reject()</code> 函數，所以 resolver 反而必須自行建立 <code>Promise</code> 物件：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.resolve(<span class="literal">true</span>)</span><br><span class="line">  .then(resolver)</span><br><span class="line">  .then(handler)</span><br><span class="line">  .catch(onerror);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolver</span>(<span class="params">initValue</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">        fs.readFile(<span class="string">&#39;readme.md&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (err) &#123;</span><br><span class="line">                reject(err);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                resolve(data);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handler</span>(<span class="params">markdown</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> marked(markdown);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onerror</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">    log(error);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>嗯...不好。看起來只是把該做的事情，搬到不同的地方做而已，並沒有讓事情更簡化。</p>
<h4 id="嘗試-2：讓-handler-與-resolver-採用一般化的，一致的-signature"><a href="#嘗試-2：讓-handler-與-resolver-採用一般化的，一致的-signature" class="headerlink" title="嘗試 2：讓 handler 與 resolver 採用一般化的，一致的 signature"></a>嘗試 2：讓 handler 與 resolver 採用一般化的，一致的 signature</h4><p>如果採用兩者的聯集形式： <code>function (value, resolve, reject)</code> 呢？</p>
<p>為此，我為 <code>Promise</code> 添加了兩個函數，分別是 <code>Promise.first()</code> 與 <code>Promise#next()</code>，負責幫忙建立 <code>Promise</code> 物件，並且轉送 <code>#then()</code>  函數傳遞的值：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.first = <span class="function"><span class="keyword">function</span> (<span class="params">handler</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> handler(<span class="literal">undefined</span>, resolve, reject);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Promise</span>.prototype.next = <span class="function"><span class="keyword">function</span> (<span class="params">handler</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.then(<span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> handler(value, resolve, reject);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">Promise</span></span><br><span class="line">  .first(resolver)</span><br><span class="line">  .then(handler)</span><br><span class="line">  .next(writer)</span><br><span class="line">  .catch(onerror);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolver</span>(<span class="params">initValue, resolve, reject</span>) </span>&#123;</span><br><span class="line">    fs.readFile(<span class="string">&#39;readme.md&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            reject(err);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            resolve(data);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handler</span>(<span class="params">markdown</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> marked(markdown);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">writer</span>(<span class="params">data, resolve, reject</span>) </span>&#123;</span><br><span class="line">    fs.writeFile(<span class="string">&#39;readme.md.bak&#39;</span>, data, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            reject(err);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            resolve(data);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onerror</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">    log(error);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>看起來還蠻順眼的，讀者以為呢？</p>
<h3 id="參考文章："><a href="#參考文章：" class="headerlink" title="參考文章："></a>參考文章：</h3><ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" rel="external">Promise</a></li>
<li><a href="https://blog.domenic.me/youre-missing-the-point-of-promises/" title="You&#39;re Missing the Point of Promises" target="_blank" rel="external">You&#39;re Missing the Point of Promises</a></li>
<li><span id="promisify"></span>有許多 Promise 程式庫，譬如 <a href="https://github.com/petkaantonov/bluebird" target="_blank" rel="external">bluebird</a>，提供 <a href="https://github.com/petkaantonov/bluebird/blob/master/API.md#promisification" target="_blank" rel="external">方法</a> 幫助將 callback 形式的函數轉化為 Promise 形式。</li>
</ul>
<h3 id="相關文章："><a href="#相關文章：" class="headerlink" title="相關文章："></a>相關文章：</h3><!-- cross references -->
<div><ul>
<li><a href="/2015/09/28/javascript-truthy-falsy/" title="JavaScript: Truthy? Falsy?">JavaScript: Truthy? Falsy?</a></li>
<li><a href="/2015/09/30/javascript-programming-pattern-resolver/" title="JavaScript Programming Pattern: Resolver">JavaScript Programming Pattern: Resolver</a></li>
<li><a href="/2015/10/01/javascript-resolver-vs-promise/" title="JavaScript: Resolver vs. Promise">JavaScript: Resolver vs. Promise</a></li>
</ul>
</div>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1439920120577-eb3a83c16dd7?crop=entropy&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=975&amp;amp;ixjsv=2.1.0&amp;amp;ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;w=1900&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Promise &amp;lt; 承諾&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/liane&quot;&gt;Liane Metzler&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;鑽牛角尖時間：Promise-resolver-vs-handler&quot;&gt;&lt;a href=&quot;#鑽牛角尖時間：Promise-resolver-vs-handler&quot; class=&quot;headerlink&quot; title=&quot;鑽牛角尖時間：Promise: resolver vs handler&quot;&gt;&lt;/a&gt;鑽牛角尖時間：Promise: resolver vs handler&lt;/h3&gt;&lt;p&gt;不曉得讀者有沒有同樣的困擾：&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;&lt;code&gt;Promise&lt;/code&gt;&lt;/a&gt; 的 resolver 與 handler 有著不同的 signature (形式) 及啟動方式，看起來非常地不協調：&lt;/p&gt;
&lt;figure class=&quot;highlight js&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// resolver 由 Promise() constructor 啟動&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;built_in&quot;&gt;Promise&lt;/span&gt;(resolver)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// handler 由 then() 函數啟動&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    .then(handler, onerror);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// resolver 具有 function (resolve, reject) 的形式&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;resolver&lt;/span&gt;(&lt;span class=&quot;params&quot;&gt;resolve, reject&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    fs.readFile(&lt;span class=&quot;string&quot;&gt;&amp;#39;readme.md&amp;#39;&lt;/span&gt;, &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt; (&lt;span class=&quot;params&quot;&gt;err, data&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            reject(err);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            resolve(data);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// fulfillment handler 具有 function (result) 的形式&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;handler&lt;/span&gt;(&lt;span class=&quot;params&quot;&gt;markdown&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; marked(markdown);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// rejection handler 具有 function (reason) 的形式&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;onerror&lt;/span&gt;(&lt;span class=&quot;params&quot;&gt;error&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    log(error);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="Design Pattern" scheme="https://amobiz.github.io/tags/Design-Pattern/"/>
    
      <category term="Promise" scheme="https://amobiz.github.io/tags/Promise/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript: Resolver vs. Promise</title>
    <link href="https://amobiz.github.io/2015/10/01/javascript-resolver-vs-promise/"/>
    <id>https://amobiz.github.io/2015/10/01/javascript-resolver-vs-promise/</id>
    <published>2015-10-01T06:05:18.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1429308755210-25a272addeb3?fit=crop&amp;fm=jpg&amp;h=800&amp;q=80&amp;w=1200" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Design Pattern &lt; 重複的模式</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/demidearest">Demi DeHerrera</a></div></div></div></div>
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>讀者應該已經發現，<a href="/2015/09/30/javascript-programming-pattern-resolver/" title="上一篇">上一篇</a> 文中提到的 resolver 模式，與 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" rel="external">Promise</a> 似乎有著極為類似的形式。的確，Promise 與 resolver 模式有許多相似之處，尤其是 resolver 的變體形式，與 Promise 一樣，都有著 <code>resolve()</code> 函數。然而，resolver 主要是用來對付 falsy 值問題，而 promise 的主要目的是處理 asnyc (異步) 問題。</p>
<a id="more"></a>
<p><strong>resolver 模式</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">eval</span>(<span class="params">expression</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> tokens, resolve;</span><br><span class="line">    resolve = resolver();</span><br><span class="line">    tokens = parse(expression);</span><br><span class="line">    <span class="keyword">if</span> (evaluate(tokens, resolve)) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(expression + <span class="string">&#39;=&#39;</span> + resolve.get());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">evaluate</span>(<span class="params">tokens, resolve</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 進行計算...</span></span><br><span class="line">    <span class="keyword">if</span> (ok) &#123;</span><br><span class="line">        <span class="comment">// 由 resolve() 函數幫忙回傳 true 以表示一個有效值</span></span><br><span class="line">        <span class="keyword">return</span> resolve(value);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 無回傳 (等於回傳 undefined) 以表示一個無效值</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolver</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> resolved;</span><br><span class="line">    <span class="keyword">var</span> resolve = <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>&#123;</span><br><span class="line">        resolved = value;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    resolve.get = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> resolved;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">return</span> resolve;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">parse</span>(<span class="params">expression</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>promise 模式</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(resolver);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolver</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    fs.read(file, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            reject(err);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            resolve(data);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>同時，最能彰顯兩者強大功能的地方，同樣是串連多個函數時：</p>
<p><strong>resolver 模式</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> result = (expired() || cached() || read() || defaults())();</span><br></pre></td></tr></table></figure>

<p><strong>promise 模式</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(read)</span><br><span class="line">    .then(process)</span><br><span class="line">    .then(write);</span><br></pre></td></tr></table></figure>

<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>在 Design Pattern 的世界中，有許多的 pattern 其實具有相近甚至相同的形式，而其嘗試解決的問題，卻是如此不同。能夠注意到諸多 pattern 在形式上的相似，已然是融會貫通的具體表徵，但更重要的是，對於問題本質的洞察。</p>
<h3 id="參考文章："><a href="#參考文章：" class="headerlink" title="參考文章："></a>參考文章：</h3><ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" rel="external">Promise</a></li>
<li><a href="https://blog.domenic.me/youre-missing-the-point-of-promises/" title="You&#39;re Missing the Point of Promises" target="_blank" rel="external">You&#39;re Missing the Point of Promises</a></li>
</ul>
<h3 id="相關文章："><a href="#相關文章：" class="headerlink" title="相關文章："></a>相關文章：</h3><!-- cross references -->
<div><ul>
<li><a href="/2015/09/28/javascript-truthy-falsy/" title="JavaScript: Truthy? Falsy?">JavaScript: Truthy? Falsy?</a></li>
<li><a href="/2015/09/30/javascript-programming-pattern-resolver/" title="JavaScript Programming Pattern: Resolver">JavaScript Programming Pattern: Resolver</a></li>
<li><a href="/2015/10/02/javascript-promise-resolver-vs-handler/" title="JavaScript Promise: resolver vs. handler">JavaScript Promise: resolver vs. handler</a></li>
</ul>
</div>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1429308755210-25a272addeb3?fit=crop&amp;amp;fm=jpg&amp;amp;h=800&amp;amp;q=80&amp;amp;w=1200&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Design Pattern &amp;lt; 重複的模式&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/demidearest&quot;&gt;Demi DeHerrera&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;讀者應該已經發現，&lt;a href=&quot;/2015/09/30/javascript-programming-pattern-resolver/&quot; title=&quot;上一篇&quot;&gt;上一篇&lt;/a&gt; 文中提到的 resolver 模式，與 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;Promise&lt;/a&gt; 似乎有著極為類似的形式。的確，Promise 與 resolver 模式有許多相似之處，尤其是 resolver 的變體形式，與 Promise 一樣，都有著 &lt;code&gt;resolve()&lt;/code&gt; 函數。然而，resolver 主要是用來對付 falsy 值問題，而 promise 的主要目的是處理 asnyc (異步) 問題。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="Design Pattern" scheme="https://amobiz.github.io/tags/Design-Pattern/"/>
    
      <category term="Promise" scheme="https://amobiz.github.io/tags/Promise/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript Programming Pattern: Resolver</title>
    <link href="https://amobiz.github.io/2015/09/30/javascript-programming-pattern-resolver/"/>
    <id>https://amobiz.github.io/2015/09/30/javascript-programming-pattern-resolver/</id>
    <published>2015-09-30T06:21:48.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1428542170253-0d2f063e92c2?fit=crop&amp;fm=jpg&amp;h=800&amp;q=80&amp;w=1200" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Resolver &lt; 過關斬將 &lt; 飛魚拍打水面飛行</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/johncobb">John Cobb</a></div></div></div></div>
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>在 JavaScript 中，有許多數值，在邏輯判斷中，其結果與 <code>false</code> 等價。由於其數值實際上並非 <code>false</code>，因此，特別稱此類數值為 <strong>falsy</strong> value。</p>
<p>在 <a href="/2015/09/28/javascript-truthy-falsy/" title="上一篇文章">上一篇文章</a> 中，介紹了一般在程式中處理 falsy 值的方法。對於函數回傳值是 falsy 的狀況，上述的原則也都同樣適用，譬如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 回傳的空字串是 falsy 值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getText</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#39;&#39;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> text = getText();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 錯誤方式：空字串是 falsy 值，不會進入 if 敘述區塊。</span></span><br><span class="line"><span class="keyword">if</span> (text) &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不佳方式：分別檢查空字串與非空字串的情形。</span></span><br><span class="line"><span class="keyword">if</span> (text || text === <span class="string">&#39;&#39;</span>) &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 上一篇文章建議的方式。</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> text === <span class="string">&#39;string&#39;</span>) &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>但是，對於函數回傳值是 falsy 的狀況，其實有更好的表達方式，就是本文要介紹的 resolver 模式。</p>
<a id="more"></a>
<h3 id="問題"><a href="#問題" class="headerlink" title="問題"></a>問題</h3><p>函數回傳的有效值中，可能包含 falsy 值。</p>
<h3 id="解決方案"><a href="#解決方案" class="headerlink" title="解決方案"></a>解決方案</h3><p>當函數要回傳一個有效值時，回傳一個函數物件，透過該函數物件本身所呈現的 truthy 值，來表示具有有效值，同時，透過該函數可以取得該有效值；否則回傳 <code>undefined</code> (或任意 falsy 值)，以表示無法得出有效值。</p>
<h3 id="範例"><a href="#範例" class="headerlink" title="範例"></a>範例</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">eval</span>(<span class="params">expression</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> tokens, resolved;</span><br><span class="line">    tokens = parse(expression);</span><br><span class="line">    resolved = evaluate(tokens);</span><br><span class="line">    <span class="comment">// 函數是 truthy 值，若通過 if 敘述，則表示得出正確結果，即使計算結果可能是 0 (一個 falsy 值)。</span></span><br><span class="line">    <span class="keyword">if</span> (resolved) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(expression + <span class="string">&#39;=&#39;</span> + resolved());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">evaluate</span>(<span class="params">tokens</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 進行計算...</span></span><br><span class="line">    <span class="keyword">if</span> (ok) &#123;</span><br><span class="line">        <span class="comment">// 回傳一個函數物件以表示一個有效值</span></span><br><span class="line">        <span class="keyword">return</span> resolve(value);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 無回傳 (等於回傳 undefined) 以表示一個無效值</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolve</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">eval</span>(<span class="string">&#39;1+1-2&#39;</span>);</span><br></pre></td></tr></table></figure>

<p>如果有多個互斥的函數，僅有其中一個可以得出正確結果時，可以將這些函數以 <code>||</code> 串接在一起，使它們依序執行，直到其中任一個函數回傳 truthy 值，或全部失敗為止：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> result;</span><br><span class="line"><span class="keyword">var</span> resolved = expired() || cached() || read();</span><br><span class="line"><span class="keyword">if</span> (resolved) &#123;</span><br><span class="line">    result = resolved();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果，想要簡化對於結果的處理方式，可以在串接的函數最後面，安置一個 guardian 函數，負責回傳預設的結果，以保證最終可以獲得結果：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> result = (expired() || cached() || read() || defaults())();</span><br></pre></td></tr></table></figure>

<h3 id="變體"><a href="#變體" class="headerlink" title="變體"></a>變體</h3><p>另一種變體是，<code>resolver</code> 函數物件由外部傳入，而不是由函數自行建構，並且，改回傳 <code>true</code> 以表示具有有效值：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">eval</span>(<span class="params">expression</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> tokens, resolve;</span><br><span class="line">    resolve = resolver();</span><br><span class="line">    tokens = parse(expression);</span><br><span class="line">    <span class="keyword">if</span> (evaluate(tokens, resolve)) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(expression + <span class="string">&#39;=&#39;</span> + resolve.get());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">evaluate</span>(<span class="params">tokens, resolve</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 進行計算...</span></span><br><span class="line">    <span class="keyword">if</span> (ok) &#123;</span><br><span class="line">        resolve(value);</span><br><span class="line">        <span class="comment">// 回傳 true 以表示一個有效值</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 無回傳 (等於回傳 undefined) 以表示一個無效值</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolver</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> resolved;</span><br><span class="line">    <span class="keyword">var</span> resolve = <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>&#123;</span><br><span class="line">        resolved = value;</span><br><span class="line">    &#125;;</span><br><span class="line">    resolve.get = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> resolved;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">return</span> resolve;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">eval</span>(<span class="string">&#39;1+1-2&#39;</span>);</span><br></pre></td></tr></table></figure>

<p>這樣的表現方式，乍看似乎有點多餘，但考慮到現在可以將表達式以任意邏輯運算串接起來：</p>
<script src="//gist.github.com/amobiz/2435713ddec72e7ce87e.js"></script>
<p>注意到上面的程式風格，符合 <a href="https://en.wikipedia.org/wiki/Robert_Cecil_Martin" target="_blank" rel="external">Robert C. Martin</a> 在 <a href="http://www.pearsonhighered.com/educator/product/Clean-Code-A-Handbook-of-Agile-Software-Craftsmanship/9780132350884.page" target="_blank" rel="external">Clean Code</a> 中的建議：程式由上而下，逐一呈現細節。程式的邏輯與意圖一目了然。</p>
<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>前一陣子 <a href="/2015/09/06/open-source-json-normalizer/" title="釋出">釋出</a> 的 <a href="https://github.com/amobiz/json-normalizer" target="_blank" rel="external">json-normalizer</a>，大量使用到本文介紹的 resolver 模式，想看更多 <a href="https://github.com/amobiz/json-normalizer/blob/master/src/normalize.js" target="_blank" rel="external">實戰範例</a> 的朋友，不妨前往瞧個究竟。</p>
<p>歡迎大家的回饋與心得分享。</p>
<h3 id="參考文章："><a href="#參考文章：" class="headerlink" title="參考文章："></a>參考文章：</h3><ul>
<li><a href="https://en.wikipedia.org/wiki/Robert_Cecil_Martin" target="_blank" rel="external">Robert C. Martin</a></li>
<li><a href="http://www.pearsonhighered.com/educator/product/Clean-Code-A-Handbook-of-Agile-Software-Craftsmanship/9780132350884.page" target="_blank" rel="external">Clean Code</a></li>
</ul>
<h3 id="相關文章："><a href="#相關文章：" class="headerlink" title="相關文章："></a>相關文章：</h3><!-- cross references -->
<ul>
<li><a href="/2015/09/28/javascript-truthy-falsy/" title="JavaScript: Truthy? Falsy?">JavaScript: Truthy? Falsy?</a></li>
<li><a href="/2015/10/01/javascript-resolver-vs-promise/" title="JavaScript: Resolver vs. Promise">JavaScript: Resolver vs. Promise</a></li>
<li><a href="/2015/10/02/javascript-promise-resolver-vs-handler/" title="JavaScript Promise: resolver vs. handler">JavaScript Promise: resolver vs. handler</a>
</li>
</ul>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1428542170253-0d2f063e92c2?fit=crop&amp;amp;fm=jpg&amp;amp;h=800&amp;amp;q=80&amp;amp;w=1200&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Resolver &amp;lt; 過關斬將 &amp;lt; 飛魚拍打水面飛行&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/johncobb&quot;&gt;John Cobb&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;在 JavaScript 中，有許多數值，在邏輯判斷中，其結果與 &lt;code&gt;false&lt;/code&gt; 等價。由於其數值實際上並非 &lt;code&gt;false&lt;/code&gt;，因此，特別稱此類數值為 &lt;strong&gt;falsy&lt;/strong&gt; value。&lt;/p&gt;
&lt;p&gt;在 &lt;a href=&quot;/2015/09/28/javascript-truthy-falsy/&quot; title=&quot;上一篇文章&quot;&gt;上一篇文章&lt;/a&gt; 中，介紹了一般在程式中處理 falsy 值的方法。對於函數回傳值是 falsy 的狀況，上述的原則也都同樣適用，譬如：&lt;/p&gt;
&lt;figure class=&quot;highlight js&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// 回傳的空字串是 falsy 值&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;getText&lt;/span&gt;(&lt;span class=&quot;params&quot;&gt;&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; text = getText();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// 錯誤方式：空字串是 falsy 值，不會進入 if 敘述區塊。&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (text) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// 不佳方式：分別檢查空字串與非空字串的情形。&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (text || text === &lt;span class=&quot;string&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// 上一篇文章建議的方式。&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;keyword&quot;&gt;typeof&lt;/span&gt; text === &lt;span class=&quot;string&quot;&gt;&amp;#39;string&amp;#39;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;但是，對於函數回傳值是 falsy 的狀況，其實有更好的表達方式，就是本文要介紹的 resolver 模式。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="Design Pattern" scheme="https://amobiz.github.io/tags/Design-Pattern/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript: Truthy? Falsy?</title>
    <link href="https://amobiz.github.io/2015/09/28/javascript-truthy-falsy/"/>
    <id>https://amobiz.github.io/2015/09/28/javascript-truthy-falsy/</id>
    <published>2015-09-28T05:21:18.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/photo-1431051047106-f1e17d81042f?fit=crop&amp;fm=jpg&amp;h=725&amp;q=80&amp;w=1075" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Truthy? Falsy? &lt; 迷惘</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/foxwalk">Genta Mochizawa</a></div></div></div></div>
<h3 id="簡介"><a href="#簡介" class="headerlink" title="簡介"></a>簡介</h3><p>在 JavaScript 中，有許多數值，在邏輯判斷中，其結果與 <code>false</code> 等價。由於其數值實際上並非 <code>false</code>，因此，特別稱此類數值為 <strong>falsy</strong> value。</p>
<!-- JavaScript - The Good Parts, P.106 -->
<p><strong>falsy values</strong></p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Type</th>
<th>Implementation</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>Number</td>
<td>Constant</td>
</tr>
<tr>
<td>NaN</td>
<td>Number</td>
<td>Global Variable</td>
</tr>
<tr>
<td>&#39;&#39;</td>
<td>String</td>
<td>Constant</td>
</tr>
<tr>
<td>false</td>
<td>Boolean</td>
<td>Constant</td>
</tr>
<tr>
<td>null</td>
<td>Object</td>
<td>Constant</td>
</tr>
<tr>
<td>undefined</td>
<td>Undefined</td>
<td>Global Variable</td>
</tr>
</tbody>
</table>
<a id="more"></a>
<h4 id="基本邏輯判斷方法"><a href="#基本邏輯判斷方法" class="headerlink" title="基本邏輯判斷方法"></a>基本邏輯判斷方法</h4><p>下面使用 <code>if</code> 敘述直接進行 truthy / falsy 判斷，上表中所有的 falsy 值皆無法通過 <code>if</code> 敘述的判斷，而其餘的值皆會通過：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (value) &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>等同於：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">value ? <span class="literal">true</span> : <span class="literal">false</span></span><br></pre></td></tr></table></figure>

<p>下面這個例子比較特別，注意 <code>===</code> 運算子<strong>不會</strong>進行型別轉換，所以唯一會通過邏輯判斷的情況，是 value 的值恰好為 <code>true</code> 的情況，其餘的所有值，結果都是 <code>false</code>。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">value === <span class="literal">true</span></span><br></pre></td></tr></table></figure>

<p>下面這個例子，由於 <code>==</code> 運算子<strong>會</strong>進行型別轉換，上表中所有的 falsy 值，都將先轉換為 <code>false</code>，然後才與 <code>true</code> 進行比較，結果當然為 <code>false</code>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">value == <span class="literal">true</span></span><br></pre></td></tr></table></figure>

<aside class="cheatsheet warning"><h5>警告</h5><p>不建議使用 <code>==</code> 運算子，詳見 <a href="http://shop.oreilly.com/product/9780596517748.do" target="_blank" rel="external">JavaScript - The Good Parts</a></p>
</aside>
<p>Falsy 值測試範例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">var falsy = [0, NaN, &apos;&apos;, false, null, undefined];</span><br><span class="line">var tests = &#123;</span><br><span class="line">    &apos;if statement&apos;: function (value) &#123;</span><br><span class="line">        if (value) &#123;</span><br><span class="line">            return &apos;truthy&apos;;</span><br><span class="line">        &#125;</span><br><span class="line">        return &apos;falsy&apos;;</span><br><span class="line">    &#125;,</span><br><span class="line">    &apos;?: operator&apos;: function (value) &#123;</span><br><span class="line">        return value ? &apos;truthy&apos; : &apos;falsy&apos;;</span><br><span class="line">    &#125;,</span><br><span class="line">    &apos;=== operator&apos;: function (value) &#123;</span><br><span class="line">        return (value === true) ? &apos;truthy&apos; : &apos;falsy&apos;;</span><br><span class="line">    &#125;,</span><br><span class="line">    &apos;== operator&apos;: function (value) &#123;</span><br><span class="line">        return (value == true) ? &apos;truthy&apos; : &apos;falsy&apos;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">falsy.forEach(function (value) &#123;</span><br><span class="line">    Object.keys(tests).forEach(function (test) &#123;</span><br><span class="line">        console.log(test + &apos;: &apos; + (value === &apos;&apos; ? &quot;&apos;&apos;&quot; : value) + &apos;: &apos; + tests<a href="value">test</a>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h4 id="判斷-string-值的方法"><a href="#判斷-string-值的方法" class="headerlink" title="判斷 string 值的方法"></a>判斷 string 值的方法</h4><p>要判斷字串是否等於特定值，毫無疑問，可以使用 <code>===</code> 運算子直接進行判斷：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (title === <span class="string">&#39;untitled&#39;</span>) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然而，需要特別注意的是，<code>&#39;&#39;</code> (空字串) 也是 falsy。</p>
<p>如果程式不接受空字串，也就是說，可以把空字串直接當作是未定義值，那麼可以直接進行邏輯判斷：</p>
<p>下面的例子，假設 <code>title</code> 是字串，且唯有當 <code>title</code> 字串長度大於 <code>0</code> 時，才進行處理：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (title) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>或者，當 <code>title</code> 未給定值，或者是空字串時，則給予預設值：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setTitle(title || DEFAULT_VALUE);</span><br></pre></td></tr></table></figure>

<p>但是，若空字串也是有效值，則反而需要特別的處理。問題是，該如何判斷字串有給值 (包含空字串也算有值)？</p>
<p>以下皆是不佳甚至錯誤的處理方式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// title 可能為 null</span></span><br><span class="line">setTitle(title === <span class="literal">undefined</span> ? DEFAULT_VALUE : title);</span><br><span class="line"><span class="comment">// title 可能為 undefined</span></span><br><span class="line">setTitle(title === <span class="literal">null</span> ? DEFAULT_VALUE : title);</span><br><span class="line"><span class="comment">// 檢查兩次</span></span><br><span class="line">setTitle((title === <span class="literal">null</span> || title === <span class="literal">undefined</span>) ? DEFAULT_VALUE : title);</span><br><span class="line"><span class="comment">// title 可能為 null，此時 typeof title === &#39;object&#39;</span></span><br><span class="line">setTitle(<span class="keyword">typeof</span> title === <span class="string">&#39;undefined&#39;</span> ? DEFAULT_VALUE : title);</span><br><span class="line"><span class="comment">// 檢查兩次，若不了解 falsy，可能會以為第二個判斷是多餘的</span></span><br><span class="line"><span class="keyword">if</span> (title || title === <span class="string">&#39;&#39;</span>) &#123;</span><br><span class="line">    setTitle(title);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    setTitle(DEFAULT_VALUE);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>比較好的方法，是<strong>利用 <code>typeof</code> 輔助判斷</strong>，直接檢查該值的型態是否為 <code>string</code>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typeof</span> value === <span class="string">&#39;string&#39;</span>                   <span class="comment">// 有給值，至少是空字串</span></span><br><span class="line"><span class="keyword">typeof</span> value === <span class="string">&#39;string&#39;</span> &amp;&amp; value.length   <span class="comment">// 有給值，而且是長度大於 0 的字串</span></span><br></pre></td></tr></table></figure>

<p>譬如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setTitle(<span class="keyword">typeof</span> title === <span class="string">&#39;string&#39;</span> ? title : DEFAULT_VALUE);</span><br></pre></td></tr></table></figure>

<h4 id="判斷-number-值的方法"><a href="#判斷-number-值的方法" class="headerlink" title="判斷 number 值的方法"></a>判斷 number 值的方法</h4><p>由於 <code>0</code> 也是 falsy，因此有著與字串類似的問題：</p>
<p>不允許 <code>0</code> 的情況，可以直接進行邏輯判斷：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (value) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>允許 <code>0</code> 的情況，則建議<strong>利用 <code>typeof</code> 輔助判斷</strong>，直接檢查該值的型態是否為 <code>number</code>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setValue(<span class="keyword">typeof</span> value === <span class="string">&#39;number&#39;</span> ? value : DEFAULT_VALUE);</span><br></pre></td></tr></table></figure>

<h4 id="判斷物件屬性是否存在的方法"><a href="#判斷物件屬性是否存在的方法" class="headerlink" title="判斷物件屬性是否存在的方法"></a>判斷物件屬性是否存在的方法</h4><p>判斷物件的屬性值，方法與上述的一般數值相同，當然也包括對於字串和數值的 falsy 值的特別處理。</p>
<p>如果要判斷物件是否具有某個特定的屬性的話，則又有以下不同的方式。假設要判斷 <code>object</code> 物件中是否具有 <code>property</code> 這個屬性，以下都是可行的方式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">object.property === <span class="literal">undefined</span></span><br><span class="line">object[<span class="string">&#39;property&#39;</span>] === <span class="literal">undefined</span></span><br><span class="line"><span class="keyword">typeof</span> object.property === <span class="string">&#39;undefined&#39;</span></span><br><span class="line"><span class="keyword">typeof</span> object[<span class="string">&#39;property&#39;</span>] === <span class="string">&#39;undefined&#39;</span></span><br></pre></td></tr></table></figure>

<p>然而，最好的方式，是使用 <code>in</code> 運算子：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="string">&#39;property&#39;</span> <span class="keyword">in</span> object) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果需要忽略繼承的屬性的話，可以使用 <code>.hasOwnProperty()</code> 函數：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (object.hasOwnProperty(<span class="string">&#39;property&#39;</span>)) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="undefined-與-null，使用時機？"><a href="#undefined-與-null，使用時機？" class="headerlink" title="undefined 與 null，使用時機？"></a><code>undefined</code> 與 <code>null</code>，使用時機？</h4><p>除了 falsy 值容易造成困擾，<code>undefined</code> 與 <code>null</code> 也是另一個容易讓人不知所措的特性。</p>
<p>上面的例子已經可以看到，對 <code>undefined</code> 與 <code>null</code> 直接進行邏輯判斷時，兩者皆表現為 falsy：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var value1;                 // undefined</span><br><span class="line">value1 ? &apos;truthy&apos; : &apos;falsy&apos; // &apos;falsy&apos;</span><br><span class="line"></span><br><span class="line">var value2 = null;          // null</span><br><span class="line">value2 ? &apos;truthy&apos; : &apos;falsy&apos; // &apos;falsy&apos;</span><br></pre></td></tr></table></figure>

<p>而要加以區別的話，可以如下處理：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 判斷是否為 undefined</span></span><br><span class="line">value === <span class="literal">undefined</span></span><br><span class="line"><span class="keyword">typeof</span> value === <span class="string">&#39;undefined&#39;</span></span><br><span class="line"><span class="comment">// 判斷是否為 null</span></span><br><span class="line">value === <span class="literal">null</span></span><br></pre></td></tr></table></figure>

<aside class="cheatsheet"><h5>注意</h5><p>無法使用 <code>typeof</code> 運算子判斷數值是否為 <code>null</code>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typeof</span> value === <span class="string">&#39;object&#39;</span></span><br></pre></td></tr></table></figure>
</aside>
<aside class="cheatsheet"><h5>注意</h5><p>在 JavaScript 中，<code>undefined</code> 不是關鍵字，而是以 global 變數的方式實作。在無法完全控制的環境下，可以像這樣確保 <code>undefined</code> 不受影響：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="literal">undefined</span> = <span class="string">&#39;not undefined&#39;</span>;</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params">value, undefined</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (value === <span class="literal">undefined</span>) &#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)(value);</span><br></pre></td></tr></table></figure>
</aside>
<p>關於兩者的使用時機：</p>
<h5 id="undefined-如同其字面上的意義：『未定義』，應該使用在可以省略，或是可以略過定義的場合"><a href="#undefined-如同其字面上的意義：『未定義』，應該使用在可以省略，或是可以略過定義的場合" class="headerlink" title="undefined 如同其字面上的意義：『未定義』，應該使用在可以省略，或是可以略過定義的場合"></a><code>undefined</code> 如同其字面上的意義：『未定義』，應該使用在可以省略，或是可以略過定義的場合</h5><p>通常使用時機如下：</p>
<ol>
<li>物件的可選屬性，</li>
<li>函數的可選參數，以及</li>
<li>函數的區域變數，在特定流程下也許不會用到的變數。</li>
</ol>
<p>由於通常我們只關心其是否定義，並且 falsy 的狀況剛好可以當作未定義來處理，因此可以直接進行邏輯判斷，即使實際上該值可能為 <code>null</code> 也無妨：</p>
<p><strong>範例</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">post</span>(<span class="params">url, data, options</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> headers;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// options 是函數的可選參數</span></span><br><span class="line">    options = options || &#123;&#125;;</span><br><span class="line">    <span class="comment">// options.headers 是 options 物件的可選屬性</span></span><br><span class="line">    <span class="keyword">if</span> (options.headers) &#123;</span><br><span class="line">        headers = <span class="built_in">Object</span>.assign(&#123;&#125;, options.headers);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">Object</span>.isObject(data)) &#123;</span><br><span class="line">        data = <span class="built_in">JSON</span>.stringify(data);</span><br><span class="line">        headers = <span class="built_in">Object</span>.assign(headers || &#123;&#125;, &#123; <span class="string">&quot;data-type&quot;</span>: <span class="string">&quot;application/json&quot;</span> &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// headers 是函數的區域變數，在特定流程下也許不會用到的變數。</span></span><br><span class="line">    <span class="keyword">if</span> (headers) &#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// options.success 是 options 物件的可選屬性</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> options.success === <span class="string">&#39;function&#39;</span>) &#123;</span><br><span class="line">        options.success(<span class="literal">null</span>, result);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="null-則應該用於明確表達『不具有值』的情況。"><a href="#null-則應該用於明確表達『不具有值』的情況。" class="headerlink" title="null 則應該用於明確表達『不具有值』的情況。"></a><code>null</code> 則應該用於明確表達『不具有值』的情況。</h5><p>譬如資料庫在儲存欄位的時候，必須明確表示該欄位是否具有值。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">employee.save(&#123;</span><br><span class="line">    <span class="attr">name</span>: <span class="string">&#39;Jack&#39;</span>,</span><br><span class="line">    <span class="attr">age</span>: <span class="number">15</span>,</span><br><span class="line">    <span class="attr">license</span>: <span class="literal">null</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>再舉例，node.js 的 callback 函數，依慣例其形式為 <code>callback(err, result)</code>。</p>
<p>其中 <code>err</code> 是用來標示錯誤的發生，當其值不為 falsy 時，表示發生錯誤，而 <code>err</code> 通常就是錯誤訊息或存放錯誤訊息的物件。</p>
<p>相反地，當 <code>err</code> 為 falsy 時 (依慣例通常是 <code>null</code>，以表示：沒有 error)，這時候，<code>result</code> 用來表示回傳的結果。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">read</span>(<span class="params">file, callback</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> data;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// do read file</span></span><br><span class="line">        data = ...;</span><br><span class="line">        <span class="comment">// 明確地使用 null 表示沒有錯誤發生</span></span><br><span class="line">        callback(<span class="literal">null</span>, data);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (ex) &#123;</span><br><span class="line">        <span class="comment">// 明確地使用 ex 做為實際參數，傳遞給 callback 做為 err 形式參數，以表示發生錯誤</span></span><br><span class="line">        callback(ex);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">read(<span class="string">&#39;readme.md&#39;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (err) &#123;</span><br><span class="line">        <span class="keyword">throw</span> err;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">console</span>.log(data);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="總結：一般原則"><a href="#總結：一般原則" class="headerlink" title="總結：一般原則"></a>總結：一般原則</h3><ol>
<li><p>若需要檢查是否給值，而不論其 falsy 狀態，則直接使用 <code>typeof</code> 對目標型別進行確認：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> title === <span class="string">&#39;string&#39;</span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.title = title;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> options.zIndex === <span class="string">&#39;number&#39;</span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.zIndex = options.zIndex;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> callback === <span class="string">&#39;function&#39;</span>) &#123;</span><br><span class="line">    callback(<span class="literal">null</span>, result);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>若 falsy 狀態恰巧是期望的行為，則直接進行邏輯判斷：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">options = options || &#123;&#125;;</span><br><span class="line"><span class="keyword">this</span>.title = title || DEFAULT_VALUE;</span><br><span class="line"><span class="keyword">if</span> (message) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ol>
<h3 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h3><p>由於 JavaScript 對於 falsy 值的認定過於寬鬆，甚至比 C/C++ 還寬鬆，導致在處理有效值的時候，反而需要進行額外的處理。這當然是 bad parts，而且還是無法避開的部份。希望經過本文的詳細探討之後，能幫助初學者掌握到其中的關鍵。</p>
<p>歡迎大家的回饋與心得分享。</p>
<h3 id="參考文章："><a href="#參考文章：" class="headerlink" title="參考文章："></a>參考文章：</h3><ul>
<li><a href="http://shop.oreilly.com/product/9780596517748.do" target="_blank" rel="external">JavaScript: The Good Parts</a></li>
</ul>
<h3 id="相關文章："><a href="#相關文章：" class="headerlink" title="相關文章："></a>相關文章：</h3><!-- cross references -->
<div><ul>
<li><a href="/2015/09/30/javascript-programming-pattern-resolver/" title="JavaScript Programming Pattern: Resolver">JavaScript Programming Pattern: Resolver</a></li>
<li><a href="/2015/10/01/javascript-resolver-vs-promise/" title="JavaScript: Resolver vs. Promise">JavaScript: Resolver vs. Promise</a></li>
<li><a href="/2015/10/02/javascript-promise-resolver-vs-handler/" title="JavaScript Promise: resolver vs. handler">JavaScript Promise: resolver vs. handler</a></li>
</ul>
</div>
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1431051047106-f1e17d81042f?fit=crop&amp;amp;fm=jpg&amp;amp;h=725&amp;amp;q=80&amp;amp;w=1075&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Truthy? Falsy? &amp;lt; 迷惘&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/foxwalk&quot;&gt;Genta Mochizawa&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;簡介&quot;&gt;&lt;a href=&quot;#簡介&quot; class=&quot;headerlink&quot; title=&quot;簡介&quot;&gt;&lt;/a&gt;簡介&lt;/h3&gt;&lt;p&gt;在 JavaScript 中，有許多數值，在邏輯判斷中，其結果與 &lt;code&gt;false&lt;/code&gt; 等價。由於其數值實際上並非 &lt;code&gt;false&lt;/code&gt;，因此，特別稱此類數值為 &lt;strong&gt;falsy&lt;/strong&gt; value。&lt;/p&gt;
&lt;!-- JavaScript - The Good Parts, P.106 --&gt;
&lt;p&gt;&lt;strong&gt;falsy values&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NaN&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Global Variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;#39;&amp;#39;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;Object&lt;/td&gt;
&lt;td&gt;Constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;undefined&lt;/td&gt;
&lt;td&gt;Undefined&lt;/td&gt;
&lt;td&gt;Global Variable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="pitfalls" scheme="https://amobiz.github.io/tags/pitfalls/"/>
    
  </entry>
  
  <entry>
    <title>Open Source json-normalizer</title>
    <link href="https://amobiz.github.io/2015/09/06/open-source-json-normalizer/"/>
    <id>https://amobiz.github.io/2015/09/06/open-source-json-normalizer/</id>
    <published>2015-09-06T05:22:50.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://github.com/" target="_blank" rel="external"><br><img src="http://2.bp.blogspot.com/-0nmrdX7VFbs/U-eoW6u1oZI/AAAAAAAAAHg/ogSeGtS54sY/s320/Octocat.png" alt="">
</a></p>
<h2 id="Open-Sourced-json-normalizer"><a href="#Open-Sourced-json-normalizer" class="headerlink" title="Open Sourced json-normalizer"></a>Open Sourced <a href="https://npmjs.com/amobiz/json-normalizer" target="_blank" rel="external">json-normalizer</a></h2><p>Hi there, I&#39;ve open-sourced my new library, <a href="https://github.com/amobiz/json-normalizer" target="_blank" rel="external">json-normalizer</a>, a JavaScript library that normalize a json object to meets a json-schema using extended schema descriptor. Please give it a try. Comments and issue reports are welcome. Thank you!</p>
<a id="more"></a>
<a id="forkme" href="https://github.com/amobiz/json-normalizer" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png" class="nofancybox"></a>
<h2 id="json-normalizer"><a href="#json-normalizer" class="headerlink" title="json-normalizer"></a>json-normalizer</h2><p>Normalize a json object to meets a json-schema using extended schema descriptor.</p>
<p><a href="https://github.com/amobiz/json-normalizer/blob/master/LICENSE" target="_blank" rel="external"><img src="http://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT"></a> <a href="http://badge.fury.io/js/json-normalizer" target="_blank" rel="external"><img src="https://badge.fury.io/js/json-normalizer.svg" alt="npm version"></a> <a href="https://david-dm.org/amobiz/json-normalizer" target="_blank" rel="external"><img src="https://david-dm.org/amobiz/json-normalizer.svg" alt="David Dependency Badge"></a></p>
<p><a href="https://nodei.co/npm/json-normalizer.png?downloads=true&amp;downloadRank=true&amp;stars=true" target="_blank" rel="external"><img src="https://nodei.co/npm/json-normalizer.png?downloads=true&amp;downloadRank=true&amp;stars=true" alt="NPM"></a> <a href="https://nodei.co/npm/json-normalizer/" target="_blank" rel="external"><img src="https://nodei.co/npm-dl/json-normalizer.png?months=6&amp;height=3" alt="NPM"></a></p>
<p>Note: Json-normalizer is not a <a href="http://json-schema.org/" target="_blank" rel="external">json-schema</a> validator. If you are looking for json-schema validators,<br>please check out here: <a href="https://www.npmjs.com/search?q=json-schema+validator" target="_blank" rel="external">json-schema validators</a>.</p>
<h4 id="Contents"><a href="#Contents" class="headerlink" title="Contents"></a>Contents</h4><ul>
<li><a href="#overview">Overview</a><ul>
<li><a href="#the-problems">The Problems</a></li>
<li><a href="#the-solution">The Solution</a></li>
</ul>
</li>
<li><a href="#install">Install</a></li>
<li><a href="#api">API</a></li>
<li><a href="#other-functions-that-may-be-handy">Other functions that may be handy</a><ul>
<li><a href="#write-your-own-loader">Write your own loader</a></li>
</ul>
</li>
<li><a href="usage-examples">Usage Examples</a><ul>
<li><a href="#alias-and-array-type">Alias and Array Type</a></li>
<li><a href="#primary-property-and-gathering-property">Primary Property and Gathering Property</a></li>
<li><a href="#extending-schema">Extending Schema</a></li>
</ul>
</li>
<li><a href="#build-and-contribute">Build and Contribute</a></li>
<li><a href="#issues">Issues</a></li>
<li><a href="#test">Test</a></li>
<li><a href="#change-logs">Change Logs</a></li>
<li><a href="#license">License</a></li>
</ul>
<h2 id="Overview"><a href="#Overview" class="headerlink" title="Overview"></a>Overview</h2><h3 id="The-Problems"><a href="#The-Problems" class="headerlink" title="The Problems"></a>The Problems</h3><p>You want:</p>
<ul>
<li>Alias, e.g., both &quot;entry&quot; and &quot;entries&quot; are valid property name, but you don&#39;t want to check both of them,</li>
<li>Support either single item or an array of items, but you don&#39;t want to handle both of them,</li>
<li>Flat data objects, i.e., user don&#39;t have to nest properties, and</li>
<li>Maximum reuse of schemas, i.e., you want multiple inheritance.</li>
</ul>
<h3 id="The-Solution"><a href="#The-Solution" class="headerlink" title="The Solution"></a>The Solution</h3><p>The normalizer is based on json-schema with the &quot;<a href="https://raw.githubusercontent.com/amobiz/json-normalizer/master/test/spec/schema/normalizable#" target="_blank" rel="external">normalizable</a>&quot; extension:</p>
<ol>
<li>Properties can have alias and being normalized, using the &quot;<code>alias</code>&quot; keyword.</li>
<li>For schemas of &quot;<code>object</code>&quot; type, a &quot;<code>primary</code>&quot; property can be specified,<br>that when provided value is not an object (a primitive or an array), can be assigned to.</li>
<li>For schemas of &quot;<code>object</code>&quot; type, a &quot;<code>gathering</code>&quot; property can be specified,  that when &quot;<code>additionalProperties</code>&quot; is set to false, all unrecognized additional properties can be gathered to. The &quot;<code>gathering</code>&quot; property name defaults to &quot;<code>others</code>&quot;.</li>
<li>For schemas of &quot;<code>array</code>&quot; type, if the provided value is not an array, converts that value to an array automatically.</li>
<li>Allow schema extends other schemas, using the &quot;<code>extends</code>&quot; keyword.</li>
</ol>
<h2 id="Install"><a href="#Install" class="headerlink" title="Install"></a>Install</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install --save json-normalizer</span><br></pre></td></tr></table></figure>

<h2 id="API"><a href="#API" class="headerlink" title="API"></a>API</h2><h3 id="normalize-schema-data-options-callback"><a href="#normalize-schema-data-options-callback" class="headerlink" title="normalize(schema, data, [options,] callback)"></a><code>normalize(schema, data, [options,] callback)</code></h3><p>Normalizes a loose JSON data object to a strict json-schema data object.</p>
<h4 id="Context"><a href="#Context" class="headerlink" title="Context"></a>Context</h4><p>Don&#39;t care.</p>
<h4 id="Parameters"><a href="#Parameters" class="headerlink" title="Parameters"></a>Parameters</h4><h5 id="schema"><a href="#schema" class="headerlink" title="schema"></a><code>schema</code></h5><p>The schema used to normalize the given JSON data object.</p>
<h5 id="data"><a href="#data" class="headerlink" title="data"></a><code>data</code></h5><p>The JSON data object.</p>
<h5 id="options"><a href="#options" class="headerlink" title="options"></a><code>options</code></h5><p>Optional. Currently only accepts a loader or an array of loaders.</p>
<h6 id="options-loader-options-loaders"><a href="#options-loader-options-loaders" class="headerlink" title="options.loader | options.loaders"></a><code>options.loader</code> | <code>options.loaders</code></h6><p>A loader or an array of loaders that help loading remote schemas. Loaders are tested in the order listed.</p>
<h4 id="callback"><a href="#callback" class="headerlink" title="callback"></a><code>callback</code></h4><p>The callback function with <code>function (err, detail)</code> signature that the normalizer delivers the normalized JSON object to. Called with null context.</p>
<h4 id="Returns"><a href="#Returns" class="headerlink" title="Returns"></a>Returns</h4><p>No return value.</p>
<h4 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> normalize = <span class="built_in">require</span>(<span class="string">&#39;json-normalizer&#39;</span>);</span><br><span class="line"><span class="keyword">var</span> schema = &#123;</span><br><span class="line">    <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;entries&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;alias&quot;</span>: <span class="string">&quot;entry&quot;</span>,</span><br><span class="line">            <span class="string">&quot;type&quot;</span>: <span class="string">&quot;array&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> data = &#123;</span><br><span class="line">    <span class="string">&quot;entry&quot;</span>: <span class="string">&quot;index.js&quot;</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// optional, only needed if your schema references remote schema.</span></span><br><span class="line"><span class="keyword">var</span> mapper = ...; <span class="comment">// see example bellow</span></span><br><span class="line"><span class="keyword">var</span> options = &#123;</span><br><span class="line">    <span class="attr">loaders</span>: [mapper]</span><br><span class="line">&#125;;</span><br><span class="line">normalize(schema, data, options, <span class="function"><span class="keyword">function</span> (<span class="params">err, result</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!err) &#123;</span><br><span class="line">        <span class="comment">// process the normalized JSON data object here.</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="normalize-sync-schema-data-options"><a href="#normalize-sync-schema-data-options" class="headerlink" title="normalize.sync(schema, data [, options])"></a><code>normalize.sync(schema, data [, options])</code></h3><p>Same as the async version but returns normalized schema. Throws when error;<br>Note that loaders must also be sync version.</p>
<h4 id="Returns-1"><a href="#Returns-1" class="headerlink" title="Returns"></a>Returns</h4><p>The normalized schema.</p>
<h4 id="Example-1"><a href="#Example-1" class="headerlink" title="Example"></a>Example</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> normalize = <span class="built_in">require</span>(<span class="string">&#39;json-normalizer&#39;</span>);</span><br><span class="line"><span class="keyword">var</span> schema = &#123;</span><br><span class="line">    <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;entries&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;alias&quot;</span>: <span class="string">&quot;entry&quot;</span>,</span><br><span class="line">            <span class="string">&quot;type&quot;</span>: <span class="string">&quot;array&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> data = &#123;</span><br><span class="line">    <span class="string">&quot;entry&quot;</span>: <span class="string">&quot;index.js&quot;</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// optional, only needed if your schema references remote schema.</span></span><br><span class="line"><span class="keyword">var</span> mapper = ...; <span class="comment">// see example bellow</span></span><br><span class="line"><span class="keyword">var</span> options = &#123;</span><br><span class="line">    <span class="attr">loaders</span>: [mapper]</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> normalized = normalize(schema, data, options);</span><br><span class="line">    <span class="comment">// process the normalized JSON data object here.</span></span><br><span class="line">&#125; <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">    <span class="comment">// handle error here.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="Other-functions-that-may-be-handy"><a href="#Other-functions-that-may-be-handy" class="headerlink" title="Other functions that may be handy:"></a>Other functions that may be handy:</h2><h3 id="deref-schema-options-callback"><a href="#deref-schema-options-callback" class="headerlink" title="deref(schema, [options,] callback)"></a><code>deref(schema, [options,] callback)</code></h3><p>Dereferences JSON references in a schema. Default implementation supports only local references, i.e. references that starts with &quot;<code>#</code>&quot;. Custom loaders can be provided via the <code>options</code> object.</p>
<h4 id="Context-1"><a href="#Context-1" class="headerlink" title="Context"></a>Context</h4><p>Don&#39;t care.</p>
<h4 id="Parameters-1"><a href="#Parameters-1" class="headerlink" title="Parameters"></a>Parameters</h4><h5 id="schema-1"><a href="#schema-1" class="headerlink" title="schema"></a><code>schema</code></h5><p>The schema that contains JSON references.</p>
<h5 id="options-1"><a href="#options-1" class="headerlink" title="options"></a><code>options</code></h5><p>Optional. Currently only accepts a loader or an array of loaders.</p>
<h6 id="options-loader-options-loaders-1"><a href="#options-loader-options-loaders-1" class="headerlink" title="options.loader | options.loaders"></a><code>options.loader</code> | <code>options.loaders</code></h6><p>A loader or an array of loaders that help loading remote schemas. Loaders are tested in the order listed.</p>
<h5 id="callback-1"><a href="#callback-1" class="headerlink" title="callback"></a><code>callback</code></h5><p>The callback function with <code>function (err, detail)</code> signature that the <code>deref</code> function delivers the deferenced schema to. Called with null context.</p>
<h4 id="Returns-2"><a href="#Returns-2" class="headerlink" title="Returns"></a>Returns</h4><p>No return value.</p>
<h3 id="deref-sync-schema-options"><a href="#deref-sync-schema-options" class="headerlink" title="deref.sync(schema [, options])"></a><code>deref.sync(schema [, options])</code></h3><p>Same as the async version but returns dereferenced schema. Throws when error;<br>Note that loaders must also be sync version.</p>
<h3 id="maperFactory-definitions-function"><a href="#maperFactory-definitions-function" class="headerlink" title="maperFactory([definitions]): function"></a><code>maperFactory([definitions]): function</code></h3><p>Creates a mapper schema loader, i.e. a schema loader that holds schema definitions in memory, initialized with the optional <code>definitions</code> hash object.</p>
<h4 id="Context-2"><a href="#Context-2" class="headerlink" title="Context"></a>Context</h4><p>Don&#39;t care.</p>
<h4 id="Parameters-2"><a href="#Parameters-2" class="headerlink" title="Parameters"></a>Parameters</h4><h5 id="definitions"><a href="#definitions" class="headerlink" title="definitions"></a><code>definitions</code></h5><p>Optional. If provided, all definitions in the <code>definitions</code> hash object will be available for mapping. See <code>mapper#map(definitions)</code> for more details.</p>
<h4 id="Returns-3"><a href="#Returns-3" class="headerlink" title="Returns"></a>Returns</h4><p>The mapper schema loader.</p>
<h3 id="mapper-root-ref-callback"><a href="#mapper-root-ref-callback" class="headerlink" title="mapper(root, $ref, callback)"></a><code>mapper(root, $ref, callback)</code></h3><p>The async version of mapper.</p>
<h3 id="mapper-sync-root-ref"><a href="#mapper-sync-root-ref" class="headerlink" title="mapper#sync(root, $ref)"></a><code>mapper#sync(root, $ref)</code></h3><p>The sync version of mapper.</p>
<h3 id="mapper-map-ref-schema"><a href="#mapper-map-ref-schema" class="headerlink" title="mapper#map($ref, schema)"></a><code>mapper#map($ref, schema)</code></h3><p>Add a <code>schema</code> with the <code>$ref</code> JSON pointer. Subsequent calls with the same JSON pointer will override definition of preceding calls.</p>
<h4 id="Context-3"><a href="#Context-3" class="headerlink" title="Context"></a>Context</h4><p>Don&#39;t care.</p>
<h4 id="Parameters-3"><a href="#Parameters-3" class="headerlink" title="Parameters"></a>Parameters</h4><h5 id="ref"><a href="#ref" class="headerlink" title="$ref"></a><code>$ref</code></h5><p>The JSON pointer the schema being published.</p>
<h5 id="schema-2"><a href="#schema-2" class="headerlink" title="schema"></a><code>schema</code></h5><p>The schema.</p>
<h4 id="Returns-4"><a href="#Returns-4" class="headerlink" title="Returns"></a>Returns</h4><p>No return value.</p>
<h3 id="mapper-map-definitions"><a href="#mapper-map-definitions" class="headerlink" title="mapper#map(definitions)"></a><code>mapper#map(definitions)</code></h3><p>Add all <code>$ref</code> : <code>schema</code> pairs defined in the <code>definitions</code> hash object. Subsequent calls with the same JSON pointer will override definition of preceding calls.</p>
<h4 id="Context-4"><a href="#Context-4" class="headerlink" title="Context"></a>Context</h4><p>Don&#39;t care.</p>
<h4 id="Parameters-4"><a href="#Parameters-4" class="headerlink" title="Parameters"></a>Parameters</h4><h5 id="definitions-1"><a href="#definitions-1" class="headerlink" title="definitions"></a><code>definitions</code></h5><p>The definitions hash object. Keys in the hash object should be valid JSON pointers; Values in the hash object should be the schema of the corresponding JSON pointer.</p>
<h4 id="Returns-5"><a href="#Returns-5" class="headerlink" title="Returns"></a>Returns</h4><p>No return value.</p>
<h4 id="Example-2"><a href="#Example-2" class="headerlink" title="Example"></a>Example</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> mapperFactory = <span class="built_in">require</span>(<span class="string">&#39;json-normalizer/src/loader/mapper&#39;</span>);</span><br><span class="line"><span class="comment">// map json references to local schema files.</span></span><br><span class="line"><span class="keyword">var</span> mapper = mapperFactory(&#123;</span><br><span class="line">    <span class="string">&#39;<a href="http://json-schema.org/draft-04/schema#" target="_blank" rel="external">http://json-schema.org/draft-04/schema#</a>&#39;</span>: <span class="built_in">require</span>(<span class="string">&#39;schema.json&#39;</span>),</span><br><span class="line">    <span class="string">&#39;<a href="http://json-schema.org/geo" target="_blank" rel="external">http://json-schema.org/geo</a>&#39;</span>: <span class="built_in">require</span>(<span class="string">&#39;geo.json&#39;</span>),</span><br><span class="line">    <span class="string">&#39;<a href="http://json-schema.org/card" target="_blank" rel="external">http://json-schema.org/card</a>&#39;</span>: <span class="built_in">require</span>(<span class="string">&#39;card.json&#39;</span>),</span><br><span class="line">    <span class="string">&#39;<a href="http://json-schema.org/calendar" target="_blank" rel="external">http://json-schema.org/calendar</a>&#39;</span>: <span class="built_in">require</span>(<span class="string">&#39;calendar.json&#39;</span>),</span><br><span class="line">    <span class="string">&#39;<a href="http://json-schema.org/address" target="_blank" rel="external">http://json-schema.org/address</a>&#39;</span>: <span class="built_in">require</span>(<span class="string">&#39;address.json&#39;</span>)</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">var</span> schema = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;userId&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;integer&quot;</span> &#125;,</span><br><span class="line">        <span class="string">&quot;userProfile&quot;</span>: &#123; <span class="string">&quot;$ref&quot;</span>: <span class="string">&quot;<a href="http://json-schema.org/card" target="_blank" rel="external">http://json-schema.org/card</a>&quot;</span> &#125;</span><br><span class="line">    &#125;     </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Async Version</span></span><br><span class="line">deref(schema, &#123; <span class="attr">loader</span>: mapper &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, schema</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (err) &#123;</span><br><span class="line">        <span class="comment">// handle error here.</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// use the dereferenced schema.</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Sync Version</span></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> deferenced = deref.sync(schema, &#123; <span class="attr">loader</span>: mapper.sync &#125;);</span><br><span class="line">    <span class="comment">// use the dereferenced schema.</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> (err) &#123;</span><br><span class="line">    <span class="comment">// handle error here.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Write-Your-Own-Loader"><a href="#Write-Your-Own-Loader" class="headerlink" title="Write Your Own Loader"></a>Write Your Own Loader</h3><ul>
<li>Async loaders are functions with <code>function (rootSchema, $ref, callback(err, schema))</code> signature.</li>
<li>Async loaders must return truthy if it can handle the given reference <code>$ref</code>, and the callback be called later when the schema is ready. However, if error occurred, e.g., network failure, the callback be called with the <code>err</code> set to the type of error and the second argument provides details of that error.</li>
<li>Async loaders must return falsy if it can not handle the given reference <code>$ref</code>, and the <code>callback</code> should not be called at all.</li>
<li>Sync loaders are functions with <code>function (rootSchema, $ref)</code> signature.</li>
<li>A local loader is always tested first, and other loaders provided in <code>options.loader</code> or <code>options.loaders</code> argument are then tested by the order listed.</li>
</ul>
<h2 id="Usage-Examples"><a href="#Usage-Examples" class="headerlink" title="Usage Examples"></a>Usage Examples</h2><h3 id="Alias-and-Array-Type"><a href="#Alias-and-Array-Type" class="headerlink" title="Alias and Array Type"></a>Alias and Array Type</h3><p>With the schema:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;entries&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;alias&quot;</span>: [<span class="string">&quot;entry&quot;</span>],</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;array&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>will normalize this data object:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;entry&quot;</span>: <span class="string">&quot;index.js&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>to:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;entries&quot;: [&quot;index.js&quot;]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Primary-Property-and-Gathering-Property"><a href="#Primary-Property-and-Gathering-Property" class="headerlink" title="Primary Property and Gathering Property"></a>Primary Property and Gathering Property</h3><p>With the schema:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;src&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;string | array&quot;</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">&quot;options&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">                <span class="attr">&quot;base&quot;</span>: &#123;</span><br><span class="line">                    <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;string&quot;</span></span><br><span class="line">                &#125;,</span><br><span class="line">                <span class="attr">&quot;read&quot;</span>: &#123;</span><br><span class="line">                    <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;boolean&quot;</span></span><br><span class="line">                &#125;,</span><br><span class="line">                <span class="attr">&quot;buffer&quot;</span>: &#123;</span><br><span class="line">                    <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;boolean&quot;</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">&quot;required&quot;</span>: [<span class="string">&quot;src&quot;</span>]</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">&quot;primary&quot;</span>: <span class="string">&quot;src&quot;</span>,</span><br><span class="line">    <span class="attr">&quot;additionalProperties&quot;</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">&quot;gathering&quot;</span>: <span class="string">&quot;options&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>will normalize this data object:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;src/index.js&quot;</span></span><br></pre></td></tr></table></figure>

<p>to:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;src&quot;</span>: <span class="string">&quot;src/index.js&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>and this data object:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;src&quot;</span>: <span class="string">&quot;index.js&quot;</span>,</span><br><span class="line">    <span class="attr">&quot;base&quot;</span>: <span class="string">&quot;src&quot;</span>,</span><br><span class="line">    <span class="attr">&quot;read&quot;</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">&quot;unknown&quot;</span>: <span class="string">&quot;...&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>to:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;src&quot;</span>: <span class="string">&quot;index.js&quot;</span>,</span><br><span class="line">    <span class="attr">&quot;options&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;base&quot;</span>: <span class="string">&quot;src&quot;</span>,</span><br><span class="line">        <span class="attr">&quot;read&quot;</span>: <span class="literal">false</span>,</span><br><span class="line">        <span class="attr">&quot;unknown&quot;</span>: <span class="string">&quot;...&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Extending-Schema"><a href="#Extending-Schema" class="headerlink" title="Extending Schema"></a>Extending Schema</h3><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;definitions&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;options&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">                <span class="attr">&quot;debug&quot;</span>: &#123;</span><br><span class="line">                    <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;boolean&quot;</span></span><br><span class="line">                &#125;,</span><br><span class="line">                <span class="attr">&quot;sourcemap&quot;</span>: &#123;</span><br><span class="line">                    <span class="attr">&quot;enum&quot;</span>: [<span class="literal">false</span>, <span class="string">&quot;inline&quot;</span>, <span class="string">&quot;external&quot;</span>]</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">&quot;extends&quot;</span>: [&#123;</span><br><span class="line">        <span class="attr">&quot;$ref&quot;</span>: <span class="string">&quot;#/definitions/options&quot;</span></span><br><span class="line">    &#125;],</span><br><span class="line">    <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;bundles&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;alias&quot;</span>: [<span class="string">&quot;bundle&quot;</span>],</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;array&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;extends&quot;</span>: [&#123;</span><br><span class="line">                <span class="attr">&quot;$ref&quot;</span>: <span class="string">&quot;#/definitions/options&quot;</span></span><br><span class="line">            &#125;],</span><br><span class="line">            <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">                <span class="attr">&quot;entries&quot;</span>: &#123;</span><br><span class="line">                    <span class="attr">&quot;alias&quot;</span>: [<span class="string">&quot;entry&quot;</span>],</span><br><span class="line">                    <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;array&quot;</span></span><br><span class="line">                &#125;,</span><br><span class="line">                <span class="attr">&quot;options&quot;</span>: &#123;</span><br><span class="line">                    <span class="attr">&quot;$ref&quot;</span>: <span class="string">&quot;#/definitions/options&quot;</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">&quot;options&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;$ref&quot;</span>: <span class="string">&quot;#/definitions/options&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Please refer to test for more examples.</p>
<h2 id="Build-and-Contribute"><a href="#Build-and-Contribute" class="headerlink" title="Build and Contribute"></a>Build and Contribute</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ git <span class="built_in">clone</span> <a href="https://github.com/amobiz/json-normalizer.git" target="_blank" rel="external">https://github.com/amobiz/json-normalizer.git</a></span><br><span class="line">$ <span class="built_in">cd</span> json-normalizer</span><br><span class="line">$ npm install</span><br></pre></td></tr></table></figure>

<h2 id="Issues"><a href="#Issues" class="headerlink" title="Issues"></a>Issues</h2><p><a href="https://github.com/amobiz/json-normalizer/issues" target="_blank" rel="external">Iessues</a></p>
<h2 id="Test"><a href="#Test" class="headerlink" title="Test"></a>Test</h2><p>Tests are written in mocha. Run tests in terminal:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm <span class="built_in">test</span></span><br></pre></td></tr></table></figure>

<h2 id="Change-Logs"><a href="#Change-Logs" class="headerlink" title="Change Logs"></a>Change Logs</h2><ul>
<li>2015/09/15 - 0.1.1<ul>
<li>Add sync version.</li>
<li>Remove mapper#add() method, use mapper#map() instead.</li>
</ul>
</li>
</ul>
<h2 id="License"><a href="#License" class="headerlink" title="License"></a>License</h2><p>MIT</p>
<h2 id="Author"><a href="#Author" class="headerlink" title="Author"></a>Author</h2><p><a href="https://github.com/amobiz" target="_blank" rel="external">Amobiz</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;a href=&quot;https://github.com/&quot;&gt;&lt;br&gt;&lt;img src=&quot;http://2.bp.blogspot.com/-0nmrdX7VFbs/U-eoW6u1oZI/AAAAAAAAAHg/ogSeGtS54sY/s320/Octocat.png&quot; alt=&quot;&quot;&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Open-Sourced-json-normalizer&quot;&gt;&lt;a href=&quot;#Open-Sourced-json-normalizer&quot; class=&quot;headerlink&quot; title=&quot;Open Sourced json-normalizer&quot;&gt;&lt;/a&gt;Open Sourced &lt;a href=&quot;https://npmjs.com/amobiz/json-normalizer&quot;&gt;json-normalizer&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Hi there, I&amp;#39;ve open-sourced my new library, &lt;a href=&quot;https://github.com/amobiz/json-normalizer&quot;&gt;json-normalizer&lt;/a&gt;, a JavaScript library that normalize a json object to meets a json-schema using extended schema descriptor. Please give it a try. Comments and issue reports are welcome. Thank you!&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="GitHub" scheme="https://amobiz.github.io/tags/GitHub/"/>
    
      <category term="nodejs" scheme="https://amobiz.github.io/tags/nodejs/"/>
    
      <category term="CommonJS" scheme="https://amobiz.github.io/tags/CommonJS/"/>
    
      <category term="npm" scheme="https://amobiz.github.io/tags/npm/"/>
    
      <category term="open source" scheme="https://amobiz.github.io/tags/open-source/"/>
    
      <category term="JSON" scheme="https://amobiz.github.io/tags/JSON/"/>
    
      <category term="normalize" scheme="https://amobiz.github.io/tags/normalize/"/>
    
      <category term="normalizer" scheme="https://amobiz.github.io/tags/normalizer/"/>
    
  </entry>
  
  <entry>
    <title>在 Blogger 上顯示 GitHub Ribbon</title>
    <link href="https://amobiz.github.io/2014/09/22/blogger-github-ribbon/"/>
    <id>https://amobiz.github.io/2014/09/22/blogger-github-ribbon/</id>
    <published>2014-09-21T16:55:48.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://unsplash.imgix.net/3/alejandroescamilla-tenedores.jpg?q=75&amp;fm=jpg&amp;auto=format&amp;s=927ccb71c1941511657cd27823e96129" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Fork me on GitHub &lt; Fork &lt; 叉子</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/alejandroescamilla">Alejandro Escamilla</a></div></div></div></div>
<aside class="cheatsheet"><h5>異動說明</h5><p>由於部落格已經改用 <a href="https://pages.github.com/" target="_blank" rel="external">GitHub Pages</a> 並搬家到 <a href="/">這裡</a>。本文提到的 <a href="https://github.com/blog/273-github-ribbons" target="_blank" rel="external">GitHub Ribbons</a> 的部份，依然對所有的部落格平台有效，但 <strong>本文提到的 <a href="http://www.blogger.com/" target="_blank" rel="external">Google Blogger</a> 的操作部份，僅對於使用 Blogger 的朋友有效</strong>。</p>
</aside>
<p>首先，到 <a href="https://github.com/blog/273-github-ribbons" target="_blank" rel="external">GitHub Ribbons</a> 挑選喜歡的配色，抄下其中 <code>src</code> 及 <code>data-canonical-src</code> (非必要) 屬性。</p>
<p>在 <a href="http://www.blogger.com/" target="_blank" rel="external">Google Blogger</a> 部落格的「版面配置」中，捲動到最後面，找到「新增小工具」，確認它是在「網誌文章」的後面，點選後，選擇「HTML/JavaScript」，然後插入下面程式碼：</p>
<a id="more"></a>
<a id="forkme" href="https://gist.github.com/amobiz/0c74f9c72bde9afd1780" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png" class="nofancybox"></a>
<!-- github.ribbon -->
<!-- gist inno-v/7b440e836f879e7348a0 -->
<script src="//gist.github.com/amobiz/0c74f9c72bde9afd1780.js"></script>
<div id="source" class="hidden"><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line">//&lt;![CDATA[</span><br><span class="line">/<em> Add GitHub Ribbons </em>/</span><br><span class="line">(function (options) &#123;</span><br><span class="line">  var el, href, pos;</span><br><span class="line"></span><br><span class="line">  if (location.pathname===&apos;/&apos;) &#123;</span><br><span class="line">    href = options.home;</span><br><span class="line">  &#125;</span><br><span class="line">  else &#123;</span><br><span class="line">    el = document.querySelector(&apos;*[data-forkme]&apos;);</span><br><span class="line">    if (el) &#123;</span><br><span class="line">      href = el.getAttribute(&apos;data-forkme&apos;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  pos = options.position || &apos;right&apos;;</span><br><span class="line">  if (href) &#123;</span><br><span class="line">    el = document.createElement(&apos;div&apos;);</span><br><span class="line">    el.innerHTML = &apos;&lt;a href=&quot;&apos; + href + &apos;&quot;&gt;&lt;img style=&quot;display: block; position: fixed; top: 0; &apos; + pos + &apos;: 0; border: 0; background:none; border-radius:0; box-shadow:none; padding:0; z-index: 9999;&quot; src=&quot;&apos; + options.src + &apos;&quot; alt=&quot;Fork me on GitHub&quot; data-canonical-src=&quot;&apos; + options.canonicalSrc + &apos;&quot;&gt;&lt;/a&gt;&apos;;</span><br><span class="line">    el.style.cssText = &apos;padding:0; margin:0; background-color:none;&apos;;</span><br><span class="line">    document.body.appendChild(el);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)(&#123;</span><br><span class="line">  home: &apos;<a href="https://github.com/amobiz&amp;apos" target="_blank" rel="external">https://github.com/amobiz&amp;apos</a>;,</span><br><span class="line">  src: &apos;<a href="https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67&amp;apos" target="_blank" rel="external">https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67&amp;apos</a>;,</span><br><span class="line">  canonicalSrc: &apos;<a href="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png&amp;apos" target="_blank" rel="external">https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png&amp;apos</a>;,</span><br><span class="line">  position: &apos;right&apos;</span><br><span class="line">&#125;);</span><br><span class="line">//]]&gt;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure>
</div>
<p>記得將最後面的 <code>home</code> 改為你自己的 GitHub 的個人頁面，並且分別填入剛剛抄下的 <code>src</code> 和 <code>data-canonical-src</code> 到 <code>src</code> 及 <code>canonicalSrc</code> 屬性中。彩帶預設放在右邊，如果挑選的彩帶必須放在左邊，則要加上 <code>position: &#39;left&#39;</code> 屬性。</p>
<p>這樣設定之後，在部落格的首頁就會出現「Fork me on GitHub」的彩帶，點擊後會連到你的 GitHub 個人首頁。</p>
<p>預設在文章中不顯示彩帶。若要在文章中連到個人首頁或特定的專案，只要在文章的任意 HTML 標籤中，埋入 <code>data-forkme</code> 屬性，即可顯示彩帶：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">data-forkme</span>=<span class="string">&quot;<a href="https://github.com/amobiz/lazy-umd.js" target="_blank" rel="external">https://github.com/amobiz/lazy-umd.js</a>&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料:"></a>參考資料:</h3><ul>
<li><a href="https://github.com/blog/273-github-ribbons" target="_blank" rel="external">GitHub Ribbons</a></li>
<li><a href="https://github.com/simonwhitaker/github-fork-ribbon-css" title="Fork me on GitHub CSS ribbon" target="_blank" rel="external">Fork me on GitHub CSS ribbon</a></li>
<li><a href="https://codepo8.github.io/css-fork-on-github-ribbon/" title="Pure CSS responsive Fork me on GitHub ribbon" target="_blank" rel="external">Pure CSS responsive Fork me on GitHub ribbon</a></li>
</ul>
<!-- cross references -->
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://unsplash.imgix.net/3/alejandroescamilla-tenedores.jpg?q=75&amp;amp;fm=jpg&amp;amp;auto=format&amp;amp;s=927ccb71c1941511657cd27823e96129&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Fork me on GitHub &amp;lt; Fork &amp;lt; 叉子&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/alejandroescamilla&quot;&gt;Alejandro Escamilla&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;aside class=&quot;cheatsheet&quot;&gt;&lt;h5&gt;異動說明&lt;/h5&gt;&lt;p&gt;由於部落格已經改用 &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt; 並搬家到 &lt;a href=&quot;/&quot;&gt;這裡&lt;/a&gt;。本文提到的 &lt;a href=&quot;https://github.com/blog/273-github-ribbons&quot;&gt;GitHub Ribbons&lt;/a&gt; 的部份，依然對所有的部落格平台有效，但 &lt;strong&gt;本文提到的 &lt;a href=&quot;http://www.blogger.com/&quot;&gt;Google Blogger&lt;/a&gt; 的操作部份，僅對於使用 Blogger 的朋友有效&lt;/strong&gt;。&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;首先，到 &lt;a href=&quot;https://github.com/blog/273-github-ribbons&quot;&gt;GitHub Ribbons&lt;/a&gt; 挑選喜歡的配色，抄下其中 &lt;code&gt;src&lt;/code&gt; 及 &lt;code&gt;data-canonical-src&lt;/code&gt; (非必要) 屬性。&lt;/p&gt;
&lt;p&gt;在 &lt;a href=&quot;http://www.blogger.com/&quot;&gt;Google Blogger&lt;/a&gt; 部落格的「版面配置」中，捲動到最後面，找到「新增小工具」，確認它是在「網誌文章」的後面，點選後，選擇「HTML/JavaScript」，然後插入下面程式碼：&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="howto" scheme="https://amobiz.github.io/tags/howto/"/>
    
      <category term="Blogger" scheme="https://amobiz.github.io/tags/Blogger/"/>
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="GitHub" scheme="https://amobiz.github.io/tags/GitHub/"/>
    
  </entry>
  
  <entry>
    <title>Lazy-umd.js - 懶人 UMD (Universal Module Definition)</title>
    <link href="https://amobiz.github.io/2014/09/20/lazy-umdjs-lazy-bones-umd-universal-module-definition/"/>
    <id>https://amobiz.github.io/2014/09/20/lazy-umdjs-lazy-bones-umd-universal-module-definition/</id>
    <published>2014-09-20T10:32:59.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://images.unsplash.com/44/E5KMvPp9SsCnqmEGUwAS_2014-08-10%2012.56.40%201.jpg?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;w=1080&amp;fit=max&amp;s=12eb02a4fdd9dded1148a672890b50ae" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>Open source &lt; 模組 &lt; beans</p>
</div><div><a class="Credit-link" target="_blank" href="https://unsplash.com/danielruswick">Daniel Ruswick</a></div></div></div></div>
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>最近在整理過去寫的程式庫，想要統一支援 script loader，同時又很貪心地想要：</p>
<p>1.同時支援 AMD (RequireJS)、CommonJS (node.js) 及 browser，解決不同語法差異。<br>2.模組套用時，避免修改 script loader 程式碼的需要。</p>
<p>研究了一下，發現偉大的 <a href="https://github.com/addyosmani" target="_blank" rel="external">Addy Osmani</a> 已經寫了 <a href="https://github.com/umdjs/umd" target="_blank" rel="external">UMD</a>。UMD 可以滿足第一點，但是應該是為了避免 script loader 程式碼擁腫，所以套用的時候，一定要修改 script loader 的部分。所以我自己整理出兩種做法，放在 <a href="https://github.com/amobiz/lazy-umd.js" target="_blank" rel="external">Github</a> 供大家參考：</p>
<a id="more"></a>
<a id="forkme" href="https://github.com/amobiz/lazy-umd.js" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png" class="nofancybox"></a>
<h3 id="方法一、傳統的-factory-模式"><a href="#方法一、傳統的-factory-模式" class="headerlink" title="方法一、傳統的 factory 模式"></a>方法一、傳統的 factory 模式</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">(function (name, /<em> optional </em>/deps, factory) &#123;</span><br><span class="line">    &apos;use strict&apos;;</span><br><span class="line"></span><br><span class="line">    /<em> global define, require, module, window </em>/</span><br><span class="line">    var i;</span><br><span class="line"></span><br><span class="line">    // deps is not an array, means the module has no dependencies, and deps is a factory function.</span><br><span class="line">    if (typeof deps === &apos;function&apos;) &#123;</span><br><span class="line">        factory = deps;</span><br><span class="line">        deps = [];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    // AMD (RequireJS)</span><br><span class="line">    if (typeof define === &apos;function&apos; &amp;&amp; define.amd) &#123;</span><br><span class="line">        define(deps, factory);</span><br><span class="line">    &#125;</span><br><span class="line">    // CommonJS (node.js)</span><br><span class="line">    else if (typeof module !== &apos;undefined&apos; &amp;&amp; module.exports) &#123;</span><br><span class="line">        if (deps.length) &#123;</span><br><span class="line">            for (i = 0; i &lt; deps.length; ++i) &#123;</span><br><span class="line">                deps[i] = require(deps[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        module.exports = factory.apply(null, deps);</span><br><span class="line">    &#125;</span><br><span class="line">    // Browser globals</span><br><span class="line">    else &#123;</span><br><span class="line">        if (deps.length) &#123;</span><br><span class="line">            for (i = 0; i &lt; deps.length; ++i) &#123;</span><br><span class="line">                deps[i] = window[deps[i]];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        window[name] = factory.apply(null, deps);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)(&apos;MyModule&apos;, [&apos;Promise&apos;], function (Promise) &#123;</span><br><span class="line">    &apos;use strict&apos;;</span><br><span class="line"></span><br><span class="line">    return &#123;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>模組的定義寫在後半段的 factory 方法中，然後當作參數傳遞給前面的 script loader 函數，控制權在 script loader 函數。這種寫法在語法上比較接近 RequireJS 的做法。</p>
<h4 id="優點："><a href="#優點：" class="headerlink" title="優點："></a>優點：</h4><ol>
<li>大量的程式庫都是採取與 UMD 類似的做法。容易了解。</li>
</ol>
<h4 id="缺點："><a href="#缺點：" class="headerlink" title="缺點："></a>缺點：</h4><ol>
<li>如果相依性較多，則會有很長的相依陣列，這是 RequireJS 原本就有的問題。</li>
<li>模組的主體定義在後面，強迫程式設計師先看到與模組無關的程式碼。</li>
<li>針對 RequireJS 而言，無法支援不匯出模組的 <code>require()</code> 函數。      (這其實不算缺點，畢竟就是要定義模組，讓模組能盡量支援各種不同的執行環境，才需要考慮 script loader 的相容性問題。一般應用程式通常執行環境早已確認。)</li>
</ol>
<h3 id="方法二、builder-串接語法模式"><a href="#方法二、builder-串接語法模式" class="headerlink" title="方法二、builder 串接語法模式"></a>方法二、builder 串接語法模式</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">(function (module) &#123;</span><br><span class="line">    &apos;use strict&apos;;</span><br><span class="line"></span><br><span class="line">    module(&apos;MyModule&apos;)</span><br><span class="line">        .require(&apos;Promise&apos;)</span><br><span class="line">        .define(function (Promise) &#123;</span><br><span class="line">            return &#123;</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">&#125;)(function (name) &#123;</span><br><span class="line">    &apos;use strict&apos;;</span><br><span class="line"></span><br><span class="line">    /<em> global define, require, module, window </em>/</span><br><span class="line">    var deps = [];</span><br><span class="line"></span><br><span class="line">    // AMD (RequireJS)</span><br><span class="line">    if (typeof define === &apos;function&apos; &amp;&amp; define.amd) &#123;</span><br><span class="line">        return &#123;</span><br><span class="line">            require: function (lib) &#123;</span><br><span class="line">                deps.push(lib);</span><br><span class="line">                return this;</span><br><span class="line">            &#125;,</span><br><span class="line">            define: function (factory) &#123;</span><br><span class="line">                define(deps, factory);</span><br><span class="line">            &#125;,</span><br><span class="line">            run: function (script) &#123;</span><br><span class="line">                require(deps, script);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    // CommonJS (node.js)</span><br><span class="line">    else if (typeof module !== &apos;undefined&apos; &amp;&amp; module.exports) &#123;</span><br><span class="line">        return &#123;</span><br><span class="line">            require: function (lib) &#123;</span><br><span class="line">                deps.push(require(lib));</span><br><span class="line">                return this;</span><br><span class="line">            &#125;,</span><br><span class="line">            define: function (factory) &#123;</span><br><span class="line">                module.exports = factory.apply(null, deps);</span><br><span class="line">            &#125;,</span><br><span class="line">            run: function (script) &#123;</span><br><span class="line">                script();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    // Browser globals</span><br><span class="line">    else &#123;</span><br><span class="line">        return &#123;</span><br><span class="line">            require: function (lib) &#123;</span><br><span class="line">                deps.push(window[lib]);</span><br><span class="line">                return this;</span><br><span class="line">            &#125;,</span><br><span class="line">            define: function (factory) &#123;</span><br><span class="line">                window[name] = factory.apply(null, deps);</span><br><span class="line">            &#125;,</span><br><span class="line">            run: function (script) &#123;</span><br><span class="line">                script();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>這是模仿 <a href="https://github.com/voronianski/melchior.js" target="_blank" rel="external">melchior.js</a> 的做法。</p>
<p>在後半段先定義好 script loader，然後當作參數傳遞給模組定義函數，控制權在模組定義函數。</p>
<h4 id="優點：-1"><a href="#優點：-1" class="headerlink" title="優點："></a>優點：</h4><ol>
<li>模組的主體定義在前面，避免與模組無關的程式碼的干擾。</li>
<li>相依模組的定義清晰易懂；若無相依也可以直接略掉 <code>.require()</code> 的呼叫。</li>
</ol>
<h4 id="缺點：-1"><a href="#缺點：-1" class="headerlink" title="缺點："></a>缺點：</h4><ol>
<li>Script loader 部分的程式碼變得更為冗長。</li>
</ol>
<aside class="cheatsheet"><h5>題外話</h5><p><a href="https://github.com/voronianski/melchior.js" target="_blank" rel="external">melchior.js</a> 為了避免在它的模組定義函數 <code>body()</code> 中重新宣告相依模組變數，選擇將相依模組變數都放到 global，如果採用的話需要多加注意。</p>
</aside>
<h3 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料:"></a>參考資料:</h3><ul>
<li><a href="https://github.com/addyosmani" target="_blank" rel="external">Addy Osmani</a></li>
<li><a href="https://github.com/umdjs/umd" target="_blank" rel="external">UMD</a></li>
<li><a href="https://github.com/amobiz/lazy-umd.js" target="_blank" rel="external">lazy-umd.js</a></li>
<li><a href="https://github.com/voronianski/melchior.js" target="_blank" rel="external">melchior.js</a></li>
</ul>
<!-- cross references -->
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://images.unsplash.com/44/E5KMvPp9SsCnqmEGUwAS_2014-08-10%2012.56.40%201.jpg?ixlib=rb-0.3.5&amp;amp;q=80&amp;amp;fm=jpg&amp;amp;crop=entropy&amp;amp;w=1080&amp;amp;fit=max&amp;amp;s=12eb02a4fdd9dded1148a672890b50ae&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;Open source &amp;lt; 模組 &amp;lt; beans&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://unsplash.com/danielruswick&quot;&gt;Daniel Ruswick&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;最近在整理過去寫的程式庫，想要統一支援 script loader，同時又很貪心地想要：&lt;/p&gt;
&lt;p&gt;1.同時支援 AMD (RequireJS)、CommonJS (node.js) 及 browser，解決不同語法差異。&lt;br&gt;2.模組套用時，避免修改 script loader 程式碼的需要。&lt;/p&gt;
&lt;p&gt;研究了一下，發現偉大的 &lt;a href=&quot;https://github.com/addyosmani&quot;&gt;Addy Osmani&lt;/a&gt; 已經寫了 &lt;a href=&quot;https://github.com/umdjs/umd&quot;&gt;UMD&lt;/a&gt;。UMD 可以滿足第一點，但是應該是為了避免 script loader 程式碼擁腫，所以套用的時候，一定要修改 script loader 的部分。所以我自己整理出兩種做法，放在 &lt;a href=&quot;https://github.com/amobiz/lazy-umd.js&quot;&gt;Github&lt;/a&gt; 供大家參考：&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="RequireJS" scheme="https://amobiz.github.io/tags/RequireJS/"/>
    
      <category term="JavaScript" scheme="https://amobiz.github.io/tags/JavaScript/"/>
    
      <category term="GitHub" scheme="https://amobiz.github.io/tags/GitHub/"/>
    
      <category term="CommonJS" scheme="https://amobiz.github.io/tags/CommonJS/"/>
    
      <category term="open source" scheme="https://amobiz.github.io/tags/open-source/"/>
    
      <category term="AMD" scheme="https://amobiz.github.io/tags/AMD/"/>
    
      <category term="UMD" scheme="https://amobiz.github.io/tags/UMD/"/>
    
  </entry>
  
  <entry>
    <title>將 SVN Local Repository 轉換為 Git Local Repository (Windows)</title>
    <link href="https://amobiz.github.io/2014/09/07/convert-svn-local-repository-to-git-local-repository-windows/"/>
    <id>https://amobiz.github.io/2014/09/07/convert-svn-local-repository-to-git-local-repository-windows/</id>
    <published>2014-09-07T13:38:15.000Z</published>
    <updated>2016-06-28T03:07:50.000Z</updated>
    
    <content type="html"><![CDATA[<div class="Credit"><p><img src="https://s3.amazonaws.com/ooomf-com-files/Vo7YbYQQ8iyOo4J9bOoj_ggb24.jpg" alt=""></p>
<div class="Credit-backdrop"><div class="Credit-info"><div class="Credit-desc"><p>意象說明<br><br>轉換 &lt; 過渡 &lt; 大橋</p>
</div><div><a class="Credit-link" target="_blank" href="https://www.flickr.com/photos/lucastheis/">Lucas Theis</a></div></div></div></div>
<p>如果有舊專案使用 <a href="https://subversion.apache.org/" target="_blank" rel="external">Subversion</a> 管理程式碼，而且不是儲存在 svn server 上，而是使用 <a href="http://tortoisesvn.net/" target="_blank" rel="external">TortoiseSVN</a> 的 create repository here 指令所建立的 repository。或者是 server 已不復存在，只剩下 repository 目錄的備份，那麼可以試著使用本文介紹的方式，轉換為 git 格式。</p>
<a id="more"></a>
<p>首先，根據 <a href="http://blog.miniasp.com/post/2014/09/06/Useful-tools-SubGit-svn-to-git-migration.aspx" title="The Will Will Web | 介紹好用工具：SubGit ( 輕鬆將 SVN 專案移轉到 Git 的工具 )" target="_blank" rel="external">The Will Will Web | 介紹好用工具：SubGit ( 輕鬆將 SVN 專案移轉到 Git 的工具 )</a> 這一篇文章的介紹，可以將 svn server 的 repository 轉為 local git bare repository。</p>
<p>再來，根據 StackOverflow 的 <a href="http://stackoverflow.com/a/16005321" title="Converting a local svn repo dump to git" target="_blank" rel="external">Converting a local svn repo dump to git</a> 這一個回答，似乎沒有辦法直接讀取 local svn repository。所以，還是要先安裝 svn server 才行。</p>
<p>由於早先使用 TortoiseSVN 管理專案，所以本文決定還是以 TortoiseSVN 來介紹如何進行轉換。</p>
<p>安裝好 <a href="http://sourceforge.net/projects/tortoisesvn" target="_blank" rel="external">TortoiseSVN</a> 和 <a href="http://subgit.com/download/index.html" target="_blank" rel="external">subgit</a>，設定好 subgit\bin 的 <code>PATH</code> 路徑後，就可以開始進行轉換。</p>
<p>以下假設 local repository 放在 <code>C:\svn</code> 目錄下，不需要是當初 repository 實際運作的目錄。另外，git 專案想放在 <code>D:\workspace</code> 目錄下。</p>
<p>1.在 cmd 模式下，先切換到該目錄下 (<code>cd/d D:\workspace</code>)。</p>
<p>2.在檔案總管裡，在 <code>C:\svn</code> 目錄下點右鍵，點選 TortoiseSVN / Repo-browser，這會列出 local repository 中的所有專案。假設現在要轉換 <code>editor</code> 專案。進入該專案，看一下所有 checkin 的 author 名稱，編輯 <code>D:\workspace\authors.txt</code>，設定好 svn 的作者名稱要如何對應 git 的作者名稱及 email，譬如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">svn-user1 = git-user1 &lt;git-user1@gmail.com&gt;</span><br><span class="line">svn-user2 = git-user2 &lt;git-user2@gmail.com&gt;</span><br></pre></td></tr></table></figure>

<p>3.回到 cmd，使用這個指令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">subgit import --non-interactive --authors-file authors.txt --svn-url file:///C:\svn\editor editor.git</span><br></pre></td></tr></table></figure>

<p>就可以建立 D:\workspace\editor.git 目錄，裡面就是轉換成功的 git bare repository。<br>注意這裡的 <code>file:///C:\svn\editor</code>，其中 <code>file:///C:\svn\</code> 是 local repository 所在目錄，而 <code>editor</code> 是專案名稱。<code>editor.git</code> 則是要匯出的 git bare repository 目錄名稱，在這裡是匯出到 <code>D:\workspace\editor.git</code>。</p>
<p>這裡需要注意的是，即使專案名稱指定錯誤，subgit 仍然會顯示轉換成功。可參考第 5 步驟確認。</p>
<p>4.成功匯出之後，就可以將 git 專案 push 到 server：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push --mirror <a href="https://remote.github.com/editor.git" target="_blank" rel="external">https://remote.github.com/editor.git</a></span><br></pre></td></tr></table></figure>

<p>5.或者要直接 clone 出來，建立 working repository:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone -l editor.git</span><br></pre></td></tr></table></figure>

<p>這樣就會建立 <code>D:\workspace\editor</code> 專案目錄。<br>如果第 3 步驟發生錯誤，這裡的 clone 指令會提示：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">warning: You appear to have cloned an empty repository.</span><br></pre></td></tr></table></figure>

<p>6.如果還有其他專案需要轉換，重複上面 5 個步驟即可。</p>
<h3 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料:"></a>參考資料:</h3><ul>
<li><a href="http://blog.miniasp.com/post/2014/09/06/Useful-tools-SubGit-svn-to-git-migration.aspx" title="The Will Will Web | 介紹好用工具：SubGit ( 輕鬆將 SVN 專案移轉到 Git 的工具 )" target="_blank" rel="external">The Will Will Web | 介紹好用工具：SubGit ( 輕鬆將 SVN 專案移轉到 Git 的工具 )</a></li>
<li><a href="http://stackoverflow.com/a/16005321" title="Converting a local svn repo dump to git" target="_blank" rel="external">Converting a local svn repo dump to git</a></li>
</ul>
<!-- cross references -->
<!-- external references -->
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;Credit&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://s3.amazonaws.com/ooomf-com-files/Vo7YbYQQ8iyOo4J9bOoj_ggb24.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;Credit-backdrop&quot;&gt;&lt;div class=&quot;Credit-info&quot;&gt;&lt;div class=&quot;Credit-desc&quot;&gt;&lt;p&gt;意象說明&lt;br&gt;&lt;br&gt;轉換 &amp;lt; 過渡 &amp;lt; 大橋&lt;/p&gt;
&lt;/div&gt;&lt;div&gt;&lt;a class=&quot;Credit-link&quot; target=&quot;_blank&quot; href=&quot;https://www.flickr.com/photos/lucastheis/&quot;&gt;Lucas Theis&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;如果有舊專案使用 &lt;a href=&quot;https://subversion.apache.org/&quot;&gt;Subversion&lt;/a&gt; 管理程式碼，而且不是儲存在 svn server 上，而是使用 &lt;a href=&quot;http://tortoisesvn.net/&quot;&gt;TortoiseSVN&lt;/a&gt; 的 create repository here 指令所建立的 repository。或者是 server 已不復存在，只剩下 repository 目錄的備份，那麼可以試著使用本文介紹的方式，轉換為 git 格式。&lt;/p&gt;
    
    </summary>
    
      <category term="Programming" scheme="https://amobiz.github.io/categories/Programming/"/>
    
    
      <category term="howto" scheme="https://amobiz.github.io/tags/howto/"/>
    
      <category term="git" scheme="https://amobiz.github.io/tags/git/"/>
    
      <category term="svn" scheme="https://amobiz.github.io/tags/svn/"/>
    
  </entry>
  
</feed>
