<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title><![CDATA[Alon's Blog]]></title>
  <subtitle><![CDATA[一边翻译一边学习---涂鸦码龙]]></subtitle>
  <link href="/atom.xml" rel="self"/>
  <link href="http://jinlong.github.io/"/>
  <updated>2017-05-08T07:01:31.000Z</updated>
  <id>http://jinlong.github.io/</id>
  
  <author>
    <name><![CDATA[Alon Zhang]]></name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title><![CDATA[浏览器前端优化]]></title>
    <link href="http://jinlong.github.io/2017/05/08/optimising-the-front-end-for-the-browser/"/>
    <id>http://jinlong.github.io/2017/05/08/optimising-the-front-end-for-the-browser/</id>
    <published>2017-05-08T12:33:31.000Z</published>
    <updated>2017-05-08T07:01:31.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="https://hackernoon.com/optimising-the-front-end-for-the-browser-f2f51a29c572" target="_blank" rel="external">Optimising the front end for the browser</a><br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<p>优化关乎速度和满意度。</p>
<ul>
<li>从用户体验（UX）角度，我们希望前端网页可以快速加载</li>
<li><p>从开发体验（DX）角度，我们希望前端是快速，简洁，规范的</p>
<h1 id="浏览器都做了什么">浏览器都做了什么</h1></li>
</ul>
<p>我们希望浏览器打开一个简单的网页</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">head</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>The "Click the button" page<span class="tag">&lt;/<span class="name">title</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"styles.css"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">head</span>&gt;</span></div><div class="line">  </div><div class="line">  <span class="tag">&lt;<span class="name">body</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span></div><div class="line">      Click the button.</div><div class="line">    <span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">    </div><div class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">"button"</span>&gt;</span>Click me<span class="tag">&lt;/<span class="name">button</span>&gt;</span></div><div class="line">    </div><div class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></div><div class="line">      var button = document.querySelector("button");</div><div class="line">      button.style.fontWeight = "bold";</div><div class="line">      button.addEventListener("click", function () &#123;</div><div class="line">        alert("Well done.");</div><div class="line">      &#125;);</div><div class="line">    <span class="tag">&lt;/<span class="name">script</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">body</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></div></pre></td></tr></table></figure>
<h2 id="浏览器如何渲染网页">浏览器如何渲染网页</h2><ol>
<li>使用 HTML 创建文档对象模型（<strong>DOM</strong>）</li>
<li>使用 CSS 创建 CSS 对象模型（<strong>CSSOM</strong>）</li>
<li>基于 DOM 和 CSSOM 执行脚本（<strong>Scripts</strong>）</li>
<li>合并 DOM 和 CSSOM 形成渲染树（<strong>Render Tree</strong>）</li>
<li>使用渲染树布局（<strong>Layout</strong>）所有元素</li>
<li>渲染（<strong>Paint</strong>）所有元素</li>
</ol>
<p><img src="/image/browser-fe-optimizing/p1.png" alt="附图"></p>
<a id="more"></a>
<h1 id="步骤一_—_HTML">步骤一 — HTML</h1><p>浏览器从上到下读取标签，把他们分解成节点，从而创建 DOM 。</p>
<p><img src="/image/browser-fe-optimizing/p2.png" alt="附图"></p>
<h2 id="HTML_加载优化策略">HTML 加载优化策略</h2><ul>
<li>样式在顶部，脚本在底部</li>
</ul>
<p>总体思路是尽可能早的加载样式，尽可能晚的加载脚本。原因是脚本执行之前，需要 HTML 和 CSS 解析完成，因此，样式尽可能的往顶部放，当底部脚本开始执行之前，样式有足够的时间完成计算。</p>
<p>进一步讲讲如何优化</p>
<ul>
<li>最小化和压缩</li>
</ul>
<p>方法可用于所有内容，包括 HTML，CSS，JavaScript，图片和其它资源。</p>
<p>最小化是移除所有多余的字符，包括空格，注释，多余的分号，等等。</p>
<p>压缩比如 GZip，大大压缩下载文件的大小</p>
<p>两种方法都用的情况下，资源加载量减少了 80% 到 90%。比如：<a href="https://css-tricks.com/the-difference-between-minification-and-gzipping/#article-header-id-2" target="_blank" rel="external">bootstrap 节省了 87% 的流量</a>。</p>
<ul>
<li>无障碍</li>
</ul>
<p>不会提升页面的下载速度，但会大大提升残障人士的满意度。给元素加上 <code>aria</code> 标签，图片提供 <code>alt</code> 文本，<a href="http://www.clarissapeterson.com/2012/11/html5-accessibility/" target="_blank" rel="external">HTML 5 无障碍参见</a>。</p>
<p>使用诸如 <a href="http://wave.webaim.org/" target="_blank" rel="external">WAVE</a> 的工具鉴别哪些地方可以提高可访问性。</p>
<h1 id="步骤二_—_CSS">步骤二 — CSS</h1><p>当浏览器发现任何与节点相关的样式时，比如：外部，内部，或行内样式，立即停止<strong>渲染</strong> DOM ，并利用这些节点创建 CSSOM。这就是 CSS “<strong>渲染阻塞</strong>“ 的由来。这里是不同类型样式的<a href="https://vineetgupta22.wordpress.com/2011/07/09/inline-vs-internal-vs-external-css/" target="_blank" rel="external">优缺点</a>。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">//外部样式</div><div class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"styles.css"</span>&gt;</span></div><div class="line"></div><div class="line">// 内部样式</div><div class="line"><span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="undefined"></span></div><div class="line">  h1 &#123;</div><div class="line">    font-size: 18px;</div><div class="line">  &#125;</div><div class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></div><div class="line"></div><div class="line">// 行内样式</div><div class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">style</span>=<span class="string">"background-color: blue;"</span>&gt;</span>Click me<span class="tag">&lt;/<span class="name">button</span>&gt;</span></div></pre></td></tr></table></figure>
<p>CSSOM 节点创建与 DOM 节点创建类似，随后，两者合并如下：</p>
<p><img src="/image/browser-fe-optimizing/p3.png" alt="附图"></p>
<p>CSSOM 的构建会阻塞页面的渲染，因此我们想尽早加载样式，</p>
<h2 id="CSS_加载优化策略">CSS 加载优化策略</h2><ul>
<li>使用 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media" target="_blank" rel="external">media 属性</a></li>
</ul>
<p>media 属性指定加载样式的条件，比如：符合最大或最小分辨率？还是面向屏幕阅读器？</p>
<ul>
<li>延迟加载 CSS</li>
</ul>
<p>有些样式，比如：首屏以下的，或者不那么重要的，可以等待首屏最有价值的内容渲染完成再加载，可以使用脚本等待页面加载，然后再插入样式。</p>
<p>这有两个栗子：<a href="https://jakearchibald.com/2016/link-in-body/" target="_blank" rel="external">The future of loading CSS</a>，<a href="https://www.giftofspeed.com/defer-loading-css/" target="_blank" rel="external">Defer load CSS</a></p>
<ul>
<li>只加载需要的样式</li>
</ul>
<p>使用 <a href="https://github.com/giakki/uncss" target="_blank" rel="external">uncss</a> 类似的工具，尽量移除不需要的样式。</p>
<h1 id="步骤三_—_JavaScript">步骤三 — JavaScript</h1><p>浏览器不断构建 DOM / CSSOM 节点，直到发现外部或者行内的脚本。</p>
<p>由于脚本可能需要访问或操作之前的 HTML 或样式，我们必须等待它们构建完成。</p>
<p>因此浏览器必须停止<strong>解析</strong>节点，完成构建 CSSOM，执行脚本，然后再继续。这就是 JavaScript 被称作“<strong>解析器阻塞</strong>”的原因。</p>
<p>脚本只能等到先前的 CSS 节点构建完成。</p>
<p><img src="/image/browser-fe-optimizing/p4.png" alt="附图"></p>
<h2 id="JavaScript_加载优化策略">JavaScript 加载优化策略</h2><ul>
<li>异步加载脚本</li>
</ul>
<p>脚本添加 <code>async</code> 属性，可以通知浏览器不要阻塞其余页面的加载，下载脚本处于较低的优先级。一旦下载完成，就可以执行。</p>
<p><img src="/image/browser-fe-optimizing/p5.png" alt="附图"></p>
<p><code>async</code> 适用于不影响 DOM 或 CSSOM 的脚本，对一些跟我们的代码无关的，不影响用户体验的外部脚本尤其适用，比如：分析统计脚本。</p>
<ul>
<li>延迟加载脚本</li>
</ul>
<p><code>defer</code> 跟 <code>async</code> 非常相似，不会阻塞页面加载，但会等到 HTML 完成解析后再执行。</p>
<p><img src="/image/browser-fe-optimizing/p6.png" alt="附图"></p>
<p>使用 defer 策略的 <a href="https://varvy.com/pagespeed/defer-loading-javascript.html" target="_blank" rel="external">另一个好选择</a>，或者也可以使用 <code>addEventListener</code>，了解更多，参加<a href="http://stackoverflow.com/questions/588040/window-onload-vs-document-onload" target="_blank" rel="external">这里</a>。</p>
<p>不幸的是 <code>async</code> 和 <code>defer</code> 对于行内的脚本不起作用，浏览器默认会编译执行它们。</p>
<ul>
<li>操作之前克隆节点</li>
</ul>
<p>多次操作 DOM 时可以尝试，首先克隆整个 DOM 节点更加高效，操作克隆后的节点，然后替换先前的节点，避免了多次重绘，降低了 CPU 和内存消耗，同时也避免了不必要的页面闪烁。</p>
<p>需要注意，克隆的时候并没有克隆事件监听。</p>
<ul>
<li>Preload/Prefetch/Prerender/Preconnect</li>
</ul>
<p>这些新属性并不是所有的浏览器都支持。了解详情可以看这里：<a href="https://css-tricks.com/prefetching-preloading-prebrowsing/" target="_blank" rel="external">Prefetching, preloading, prebrowsing</a></p>
<h1 id="步骤四_—_渲染树（Render_Tree）">步骤四 — 渲染树（Render Tree）</h1><p>一旦所有节点已被解析，DOM 和 CSSOM 准备合并，浏览器便会构建渲染树。如果我们把节点想象成单词，那么对象模型就是句子，渲染树便是整个页面。</p>
<p><img src="/image/browser-fe-optimizing/p7.png" alt="附图"></p>
<h1 id="步骤五_—_布局（Layout）">步骤五 — 布局（Layout）</h1><p>布局阶段需要确定页面上所有元素的大小和位置。</p>
<p><img src="/image/browser-fe-optimizing/p8.png" alt="附图"></p>
<h1 id="步骤六_—_渲染（Paint）">步骤六 — 渲染（Paint）</h1><p>最终的渲染阶段，会真正地光栅化屏幕上的像素，把页面呈现给用户。</p>
<p><img src="/image/browser-fe-optimizing/p9.png" alt="附图"></p>
<p>整个过程耗时1秒或十分之一秒，我们的任务是让它更快。</p>
<p>如果 JavaScript 事件改变了页面的某部分，便会引起渲染树的重绘，并且迫使布局（Layout）和渲染（Paint）过程再次进行。</p>
<h1 id="浏览器如何发起网络请求">浏览器如何发起网络请求</h1><p>当浏览器请求一个 URL，服务端会响应一些 HTML。</p>
<p>我们需要认识一个新术语，关键渲染路径（Critical Rendering Path (CRP)），就是浏览器渲染页面的步骤数，如下图。</p>
<p><img src="/image/browser-fe-optimizing/p10.png" alt="附图"></p>
<h2 id="关键路径长度">关键路径长度</h2><p>关键渲染路径的度量标准是路径长度。最理想的关键路径长度是1。</p>
<p>如果页面包含一些内部样式和 JavaScript ，关键路径发生以下改变。</p>
<p><img src="/image/browser-fe-optimizing/p11.png" alt="附图"></p>
<p>新增两步，<strong>构建 CSSOM</strong>和<strong>执行脚本</strong>，因为我们的 HTML 有内部样式和脚本需要计算。由于没有外部请求，我们的关键路径长度没变。</p>
<p>但是注意，我们的 HTML 大小增加到了 2kb，某些地方还是受了影响。</p>
<h2 id="关键字节数">关键字节数</h2><p>三个度量标准之二出现了，关键字节数，它用来衡量渲染页面需要传送多少字节数。</p>
<p>如果你认为不需要外部资源，就大错特错了，外部资源可以被缓存。</p>
<p>我们使用一个外部 CSS 文件，一个外部 JavaScript 文件，和一个外部带 <code>async</code> 属性的 JavaScript 文件。关键路径图如下：</p>
<p><img src="/image/browser-fe-optimizing/p12.png" alt="附图"></p>
<p>浏览器请求页面，构建 DOM，发现外部资源后开始下载，CSS 和 JavaScript 有较高的优先级，其它资源次之。</p>
<p><code>styles.css</code> 和 <code>app.js</code> 通过另一个关键路径获取。暂时不获取 <code>analytics.js</code> ，因为加了 <code>async</code> 属性，浏览器将用另一个线程下载它，它处于较低优先级，不会阻塞页面渲染，也不影响关键路径。</p>
<h2 id="关键文件">关键文件</h2><p>最后一个度量标准是关键文件，浏览器渲染页面需要下载的文件总量。以上例子，HTML 文件，CSS 和 JavaScript 文件算关键文件，<code>async</code> 的脚本不算。当然是文件越少越好。</p>
<h2 id="回到关键路径长度">回到关键路径长度</h2><p>以上例子就是最长的渲染路径吗？我认为渲染页面时，我们仅需要下载 HTML，CSS 和 JavaScript 文件，仅通过两次服务器往返就做到了。</p>
<h2 id="HTTP1_文件限制">HTTP1 文件限制</h2><p>我们浏览器的 HTTP1 协议，在同一个域名，同一次，允许下载的文件数有最大限制，范围从 2（老旧的浏览器）到 6（Edge，Chrome）。</p>
<p>各种浏览器请求文件的最大并发数，参见<a href="http://sgdev-blog.blogspot.co.uk/2014/01/maximum-concurrent-connection-to-same.html" target="_blank" rel="external">Maximum concurrent connections to the same domain for browsers</a>。</p>
<p>通过把一些资源存放到影子域名，可以绕过这个限制，以达到最佳优化效果。</p>
<p><strong>注意</strong>：不要把关键的 CSS 放到根域名之外的其他域名，有些场景下会对 DNS 查找和延迟起反作用。</p>
<h2 id="HTTP2">HTTP2</h2><p>如果网站使用了 HTTP2，并且用户的浏览器也兼容，则可以完全避开这个下载限制。</p>
<p><a href="https://tools.keycdn.com/http2-test" target="_blank" rel="external">这里</a>有个 HTTP2 测试网站。</p>
<h2 id="TCP_往返限制">TCP 往返限制</h2><p>每一次服务器往返可以传送的最大数据量是 14kb，包括所有 HTML，CSS 和脚本的网络请求。</p>
<p>如果我们的 HTML，或者积累的资源请求超过 14kb时，需要多做一次服务器往返。</p>
<h2 id="大魔法师">大魔法师</h2><p>我们整个 HTML 页面可以很好的压缩， GZip 可以压缩到 2kb，远低于 14kb 的限制，因此，一次服务器往返就可以搞定。</p>
<p><img src="/image/browser-fe-optimizing/p13.png" alt="附图"></p>
<p><code>关键路径度量: 长度 1，文件数 1，字节数 2kb</code></p>
<p>浏览器发现外部资源（CSS 和 JavaScript）时，发起请求开始下载它们。首要下载的 CSS 文件是 14kb，达到了往返传输的最大限制，因此增加了一条关键路径。</p>
<p><img src="/image/browser-fe-optimizing/p14.png" alt="附图"></p>
<p><code>关键路径度量: 长度 2，文件数 2，字节数 16kb</code></p>
<p>余下的资源低于 14kb，但是总共有 7 个资源，由于网站未启用 HTTP2，我们的 Chrome，每一次往返仅可以下载 6 个文件。</p>
<p><img src="/image/browser-fe-optimizing/p15.png" alt="附图"></p>
<p><code>关键路径度量: 长度 3，文件数 8，字节数 28kb</code></p>
<p>下载完最终文件，并开始渲染 DOM。</p>
<p><img src="/image/browser-fe-optimizing/p16.png" alt="附图"></p>
<p><code>关键路径度量: 长度 4，文件数 9，字节数 30kb</code></p>
<p>基于以上的信息和知识，发起每个连接时，就可以准确地预估页面的性能了。</p>
<h2 id="浏览器网络优化策略">浏览器网络优化策略</h2><ul>
<li>Pagespeed Insights</li>
</ul>
<p>使用 <a href="https://developers.google.com/speed/pagespeed/insights/" target="_blank" rel="external">Insights</a> 鉴别性能问题，Chrome DevTools 也有个 <code>audit</code> 标签。</p>
<ul>
<li>充分利用 Chrome 开发者工具</li>
</ul>
<p><a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing" target="_blank" rel="external">这篇文章</a> 值得一读，帮你理解网络资源</p>
<ul>
<li>在优质的环境里开发，在艰苦的环境里测试</li>
</ul>
<p>开发时大可使用 1Tb SSD，32G 内存的 Macbook Pro ，但是性能测试时还是要到 Chrome 的 <code>network</code> 标签下模拟低带宽的情形，从而获取有价值的信息。</p>
<ul>
<li>合并资源/文件</li>
</ul>
<p>基本上，每接收到一个外部 CSS 和 JavaScript 文件，浏览器都会构建 CSSOM，执行脚本。尽管几个文件可以在一次往返中传送，但也浪费了浏览器的宝贵时间和资源，最好还是合并文件，减少不必要的加载。</p>
<ul>
<li>首屏内容使用内部样式</li>
</ul>
<p>内部 CSS 和 JavaScript 不需要请求外部资源，相反，外部资源又可以被缓存，并保持 DOM 轻量，两者没有非黑即白。</p>
<p>但是一个非常好的论点是首屏关键内容使用内部样式，可以避免请求额外的资源，节省时间做最有意义的渲染。</p>
<ul>
<li><p>最小化/压缩图片</p>
</li>
<li><p>延迟加载图片</p>
</li>
<li><p>异步记载字体</p>
</li>
<li><p>是否真正需要 JavaScript / CSS?</p>
</li>
</ul>
<p>原生 HTML 元素可以实现的行为是否用了脚本？是否有样式或者图标可以行内创建的，不需要内部/外部资源？比如：<a href="https://css-tricks.com/using-svg/#article-header-id-7" target="_blank" rel="external">行内 SVG</a>。</p>
<ul>
<li>CDN</li>
</ul>
<p>可以利用 CDN（内容分发网络）存储资源，它会从离用户最近，延迟最低的位置分发到用户设备，加载时间更快。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="https://developers.google.com/web/fundamentals/performance/" target="_blank" rel="external">Google 的优化文档</a></li>
<li><a href="https://hpbn.co/" target="_blank" rel="external">高性能浏览器网络</a></li>
</ul>
<h1 id="综述">综述</h1><p>关键渲染路径是最重要的，它使得网站优化有规律可循。需要关注3个指标：</p>
<p>1—关键字节数<br>2—关键文件数<br>3—关键路径长度</p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="https://hackernoon.com/optimising-the-front-end-for-the-browser-f2f51a29c572">Optimising the front end for the browser</a><br>笔记：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
</blockquote>
<p>优化关乎速度和满意度。</p>
<ul>
<li>从用户体验（UX）角度，我们希望前端网页可以快速加载</li>
<li><p>从开发体验（DX）角度，我们希望前端是快速，简洁，规范的</p>
<h1 id="浏览器都做了什么">浏览器都做了什么</h1></li>
</ul>
<p>我们希望浏览器打开一个简单的网页</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">head</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>The "Click the button" page<span class="tag">&lt;/<span class="name">title</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"styles.css"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">head</span>&gt;</span></div><div class="line">  </div><div class="line">  <span class="tag">&lt;<span class="name">body</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span></div><div class="line">      Click the button.</div><div class="line">    <span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">    </div><div class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">"button"</span>&gt;</span>Click me<span class="tag">&lt;/<span class="name">button</span>&gt;</span></div><div class="line">    </div><div class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></div><div class="line">      var button = document.querySelector("button");</div><div class="line">      button.style.fontWeight = "bold";</div><div class="line">      button.addEventListener("click", function () &#123;</div><div class="line">        alert("Well done.");</div><div class="line">      &#125;);</div><div class="line">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">body</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></div></pre></td></tr></table></figure>
<h2 id="浏览器如何渲染网页">浏览器如何渲染网页</h2><ol>
<li>使用 HTML 创建文档对象模型（<strong>DOM</strong>）</li>
<li>使用 CSS 创建 CSS 对象模型（<strong>CSSOM</strong>）</li>
<li>基于 DOM 和 CSSOM 执行脚本（<strong>Scripts</strong>）</li>
<li>合并 DOM 和 CSSOM 形成渲染树（<strong>Render Tree</strong>）</li>
<li>使用渲染树布局（<strong>Layout</strong>）所有元素</li>
<li>渲染（<strong>Paint</strong>）所有元素</li>
</ol>
<p><img src="/image/browser-fe-optimizing/p1.png" alt="附图"></p>]]>
    
    </summary>
    
      <category term="前端优化" scheme="http://jinlong.github.io/tags/%E5%89%8D%E7%AB%AF%E4%BC%98%E5%8C%96/"/>
    
      <category term="前端优化" scheme="http://jinlong.github.io/categories/%E5%89%8D%E7%AB%AF%E4%BC%98%E5%8C%96/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[JavaScript 数组方法对比]]></title>
    <link href="http://jinlong.github.io/2017/02/04/javascript-array-methods-mutating-vs-non-mutating/"/>
    <id>http://jinlong.github.io/2017/02/04/javascript-array-methods-mutating-vs-non-mutating/</id>
    <published>2017-02-04T10:17:47.000Z</published>
    <updated>2017-02-04T09:29:35.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：“<a href="http://lorenstewart.me/2017/01/22/javascript-array-methods-mutating-vs-non-mutating/" target="_blank" rel="external">JavaScript Array Methods: Mutating vs. Non-Mutating</a>”<br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a> </p>
</blockquote>
<p>JavaScript 提供了多种新增，移除，替换数组元素的方法，但是有些会影响原来的数组；有些则不会，它是新建了一个数组。</p>
<p><strong>注意</strong>：区分以下两个方法的不同点：</p>
<ol>
<li><code>array.splice()</code> 影响原来的数组</li>
<li><code>array.slice()</code> 不影响原来的数组</li>
</ol>
<h1 id="I-_新增：影响原数组">I. 新增：影响原数组</h1><p>使用 <code>array.push()</code> 和 <code>array.ushift()</code> 新增元素会影响原来的数组。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> mutatingAdd = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>]; </div><div class="line">mutatingAdd.push(<span class="string">'f'</span>); <span class="comment">// ['a', 'b', 'c', 'd', 'e', 'f']</span></div><div class="line">mutatingAdd.unshift(<span class="string">'z'</span>); <span class="comment">// ['z', 'b', 'c', 'd', 'e' 'f']</span></div></pre></td></tr></table></figure>
<a id="more"></a>
<h1 id="II-_新增：不影响原数组">II. 新增：不影响原数组</h1><p>两种方式新增元素不会影响原数组，第一种是 <code>array.concat()</code> 。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> arr1 = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];</div><div class="line"></div><div class="line"><span class="keyword">const</span> arr2 = arr1.concat(<span class="string">'f'</span>); <span class="comment">// ['a', 'b', 'c', 'd', 'e', 'f']  （注：原文有误，我做了修改 “.” ---&gt; “,”）</span></div><div class="line"><span class="built_in">console</span>.log(arr1); <span class="comment">// ['a', 'b', 'c', 'd', 'e']</span></div></pre></td></tr></table></figure>
<p>第二种方法是使用 JavaScript 的展开（spread）操作符，展开操作符是三个点（…）</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> arr1 = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];</div><div class="line"></div><div class="line"><span class="keyword">const</span> arr2 = [...arr1, <span class="string">'f'</span>]; <span class="comment">// ['a', 'b', 'c', 'd', 'e', 'f']  </span></div><div class="line"><span class="keyword">const</span> arr3 = [<span class="string">'z'</span>, ...arr1]; <span class="comment">// ['z', 'a', 'b', 'c', 'd', 'e']</span></div></pre></td></tr></table></figure>
<p>展开操作符会复制原来的数组，从原数组取出所有元素，然后存入新的环境。</p>
<h1 id="III-_移除：影响原数组">III. 移除：影响原数组</h1><p>使用 <code>array.pop()</code> 和 <code>array.shift()</code> 移除数组元素时，会影响原来的数组。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> mutatingRemove = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];  </div><div class="line">mutatingRemove.pop(); <span class="comment">// ['a', 'b', 'c', 'd']  </span></div><div class="line">mutatingRemove.shift(); <span class="comment">// ['b', 'c', 'd']</span></div></pre></td></tr></table></figure>
<p><code>array.pop()</code> 和 <code>array.shift()</code> 返回被移除的元素，你可以通过一个变量获取被移除的元素。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> mutatingRemove = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];</div><div class="line"></div><div class="line"><span class="keyword">const</span> returnedValue1 = mutatingRemove.pop();  </div><div class="line"><span class="built_in">console</span>.log(mutatingRemove); <span class="comment">// ['a', 'b', 'c', 'd']  </span></div><div class="line"><span class="built_in">console</span>.log(returnedValue1); <span class="comment">// 'e'</span></div><div class="line"></div><div class="line"><span class="keyword">const</span> returnedValue2 = mutatingRemove.shift();  </div><div class="line"><span class="built_in">console</span>.log(mutatingRemove); <span class="comment">// ['b', 'c', 'd']  </span></div><div class="line"><span class="built_in">console</span>.log(returnedValue2); <span class="comment">// 'a'</span></div></pre></td></tr></table></figure>
<p><code>array.splice()</code> 也可以删除数组的元素。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> mutatingRemove = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];  </div><div class="line">mutatingRemove.splice(<span class="number">0</span>, <span class="number">2</span>); <span class="comment">// ['c', 'd', 'e']</span></div></pre></td></tr></table></figure>
<p>像 <code>array.pop()</code> 和 <code>array.shift()</code> 一样，<code>array.splice()</code> 同样返回移除的元素。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> mutatingRemove = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];  </div><div class="line"><span class="keyword">let</span> returnedItems = mutatingRemove.splice(<span class="number">0</span>, <span class="number">2</span>);  </div><div class="line"><span class="built_in">console</span>.log(mutatingRemove); <span class="comment">// ['c', 'd', 'e']  </span></div><div class="line"><span class="built_in">console</span>.log(returnedItems) <span class="comment">// ['a', 'b']</span></div></pre></td></tr></table></figure>
<h1 id="IV-_移除：不影响原数组">IV. 移除：不影响原数组</h1><p>JavaScript 的 <code>array.filter()</code> 方法基于原数组创建一个新数组，新数组仅包含匹配特定条件的元素。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> arr1 = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];</div><div class="line"></div><div class="line"><span class="keyword">const</span> arr2 = arr1.filter(<span class="function"><span class="params">a</span> =&gt;</span> a !== <span class="string">'e'</span>); <span class="comment">// ['a', 'b', 'c', 'd']（注：原文有误，我做了修改）  </span></div><div class="line"><span class="comment">// 或者</span></div><div class="line"><span class="keyword">const</span> arr2 = arr1.filter(<span class="function"><span class="params">a</span> =&gt;</span> &#123;  </div><div class="line">  <span class="keyword">return</span> a !== <span class="string">'e'</span>;</div><div class="line">&#125;); <span class="comment">// ['a', 'b', 'c', 'd']（注：原文有误，我做了修改）</span></div></pre></td></tr></table></figure>
<p>以上代码的条件是“不等于 ‘e’ ”，因此新数组（<code>arr2</code>）里面没有包含 ‘e’。</p>
<hr>
<p><strong>箭头函数的独特性</strong>：</p>
<p><strong>单行箭头函数</strong>，’return’ 关键字是默认自带的，不需要手动书写。</p>
<p>可是，<strong>多行箭头函数</strong>就需要明确地返回一个值。</p>
<hr>
<p>另一种不影响原数组的方式是 <code>array.slice()</code>（不要与 <code>array.splice()</code> 混淆）。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> arr1 = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];  </div><div class="line"><span class="keyword">const</span> arr2 = arr1.slice(<span class="number">1</span>, <span class="number">5</span>) <span class="comment">// ['b', 'c', 'd', 'e']  </span></div><div class="line"><span class="keyword">const</span> arr3 = arr1.slice(<span class="number">2</span>) <span class="comment">// ['c', 'd', 'e']</span></div></pre></td></tr></table></figure>
<h1 id="V-_替换：影响原数组">V. 替换：影响原数组</h1><p>如果知道替换哪一个元素，可以使用 <code>array.splice()</code> 。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> mutatingReplace = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];  </div><div class="line">mutatingReplace.splice(<span class="number">2</span>, <span class="number">1</span>, <span class="number">30</span>); <span class="comment">// ['a', 'b', 30, 'd', 'e']  </span></div><div class="line"><span class="comment">// 或者</span></div><div class="line">mutatingReplace.splice(<span class="number">2</span>, <span class="number">1</span>, <span class="number">30</span>, <span class="number">31</span>); <span class="comment">// ['a', 'b', 30, 31, 'd', 'e']</span></div></pre></td></tr></table></figure>
<h1 id="VI-_替换：不影响原数组">VI. 替换：不影响原数组</h1><p>可以使用 <code>array.map()</code> 创建一个新数组，并且可以检查每一个元素，根据特定的条件替换它们。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> arr1 = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>]  </div><div class="line"><span class="keyword">const</span> arr2 = arr1.map(<span class="function"><span class="params">item</span> =&gt;</span> &#123;  </div><div class="line">  <span class="keyword">if</span>(item === <span class="string">'c'</span>) &#123;</div><div class="line">    item = <span class="string">'CAT'</span>;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> item;</div><div class="line">&#125;); <span class="comment">// ['a', 'b', 'CAT', 'd', 'e']</span></div></pre></td></tr></table></figure>
<p><strong>使用  <code>array.map()</code> 转换数据</strong></p>
<p> <code>array.map()</code> 是个强力方法，可以用于转换数据，而不污染原先的数据源。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> origArr = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>];  </div><div class="line"><span class="keyword">const</span> transformedArr = origArr.map(<span class="function"><span class="params">n</span> =&gt;</span> n + <span class="string">'Hi!'</span>); <span class="comment">// ['aHi!', 'bHi!', 'cHi!', 'dHi!', 'eHi!']  </span></div><div class="line"><span class="built_in">console</span>.log(origArr); <span class="comment">// ['a', 'b', 'c', 'd', 'e']; // 原数组毫发无损</span></div></pre></td></tr></table></figure>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：“<a href="http://lorenstewart.me/2017/01/22/javascript-array-methods-mutating-vs-non-mutating/">JavaScript Array Methods: Mutating vs. Non-Mutating</a>”<br>笔记：<a href="http://weibo.com/newwave">涂鸦码龙</a> </p>
</blockquote>
<p>JavaScript 提供了多种新增，移除，替换数组元素的方法，但是有些会影响原来的数组；有些则不会，它是新建了一个数组。</p>
<p><strong>注意</strong>：区分以下两个方法的不同点：</p>
<ol>
<li><code>array.splice()</code> 影响原来的数组</li>
<li><code>array.slice()</code> 不影响原来的数组</li>
</ol>
<h1 id="I-_新增：影响原数组">I. 新增：影响原数组</h1><p>使用 <code>array.push()</code> 和 <code>array.ushift()</code> 新增元素会影响原来的数组。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> mutatingAdd = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>]; </div><div class="line">mutatingAdd.push(<span class="string">'f'</span>); <span class="comment">// ['a', 'b', 'c', 'd', 'e', 'f']</span></div><div class="line">mutatingAdd.unshift(<span class="string">'z'</span>); <span class="comment">// ['z', 'b', 'c', 'd', 'e' 'f']</span></div></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="Array" scheme="http://jinlong.github.io/tags/Array/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[React Native 的组件通信方式]]></title>
    <link href="http://jinlong.github.io/2016/12/16/react-native-component-communication/"/>
    <id>http://jinlong.github.io/2016/12/16/react-native-component-communication/</id>
    <published>2016-12-16T05:11:24.000Z</published>
    <updated>2016-12-16T07:27:19.000Z</updated>
    <content type="html"><![CDATA[<p>题外话，说几句我对 React 与 React Native 关系的理解：</p>
<ul>
<li>React 主要用于浏览器端实现一些 UI 组件，也可用于服务端渲染。React 可以使用 HTML 提供的标签，也可封装自定义的组件，React 也提供直接操作 DOM 的方法；</li>
<li>React Native 主要用于实现客户端应用（App）的 UI 组件，它只能使用 Facebook 封装的 Native 组件，或者自己封装的 Native 组件。开发中主要借助 JavaScript，基本告别 HTML 和 CSS 了，不过优点是可以用 ES6。</li>
</ul>
<hr>
<p>言归正传，正文开始</p>
<p>之前没搞过 React ，直接开撸的 React Native，使用过程也是各种踩坑填坑，磕磕绊绊，这里简单总结一下我用过的组件通信的几种方式吧。</p>
<h1 id="React_最基础的_props_和_state">React 最基础的 props 和 state</h1><h2 id="组件内部用_state">组件内部用 state</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">constructor</span>(props) &#123;</div><div class="line">    <span class="keyword">super</span>(props);</div><div class="line"></div><div class="line">    <span class="keyword">this</span>.state = &#123;</div><div class="line">        <span class="attr">isOnline</span>: <span class="literal">true</span>	<span class="comment">//组件 state</span></div><div class="line">    &#125;;</div><div class="line">&#125;</div><div class="line">render() &#123;</div><div class="line">    <span class="keyword">if</span>(<span class="keyword">this</span>.state.isOnline)&#123;</div><div class="line">        <span class="comment">//...剩余代码</span></div><div class="line">    &#125;</div><div class="line">    <span class="comment">//...剩余代码</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="父子组件通信用_props">父子组件通信用 props</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//父组件设置属性参数</span></div><div class="line">&lt;MyComponet isOnline=&#123;<span class="literal">true</span>&#125; /&gt;</div><div class="line"></div><div class="line"><span class="comment">//子组件</span></div><div class="line"><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;</div><div class="line">    <span class="keyword">constructor</span>(props) &#123;</div><div class="line">        <span class="keyword">super</span>(props);</div><div class="line"></div><div class="line">        <span class="comment">//子组件获取属性</span></div><div class="line">        <span class="keyword">let</span> isOnline = <span class="keyword">this</span>.props.isOnline;  </div><div class="line">    &#125;</div><div class="line">    <span class="comment">//...剩余代码</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="子父组件通信也可用_props">子父组件通信也可用 props</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//子组件</span></div><div class="line"><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;</div><div class="line">    <span class="keyword">constructor</span>(props) &#123;</div><div class="line">        <span class="keyword">super</span>(props);</div><div class="line">    &#125;</div><div class="line">    componentDidMount() &#123;</div><div class="line">        <span class="comment">//子组件给父组件的方法传参</span></div><div class="line">        <span class="keyword">this</span>.props.onChange(<span class="string">'newVal'</span>);</div><div class="line">    &#125;</div><div class="line">    render() &#123;</div><div class="line">        <span class="keyword">return</span> (</div><div class="line">            <span class="xml"><span class="tag">&lt;<span class="name">View</span> /&gt;</span></span></div><div class="line">        );</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//父组件</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">parentCpt</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</div><div class="line">    <span class="keyword">constructor</span>(props) &#123;</div><div class="line">        <span class="keyword">super</span>(props);</div><div class="line">        <span class="keyword">this</span>.state = &#123;</div><div class="line">            <span class="attr">key</span>: <span class="string">'defVal'</span></div><div class="line">        &#125;;</div><div class="line">    &#125;</div><div class="line">    <span class="comment">//父组件接受子组件的参数，并改变 state</span></div><div class="line">    handleChange(val) &#123;</div><div class="line">        <span class="keyword">this</span>.setState(&#123;</div><div class="line">            <span class="attr">key</span>: val </div><div class="line">        &#125;);</div><div class="line">    &#125;</div><div class="line">    render() &#123;</div><div class="line">        <span class="comment">//...剩余代码</span></div><div class="line">        <span class="keyword">return</span> (</div><div class="line">            <span class="xml"><span class="tag">&lt;<span class="name">MyComponent</span> <span class="attr">onChange</span>=<span class="string">&#123;(val)</span> =&gt;</span> &#123;this.handleChange(val)&#125;&#125; /&gt;</span></div><div class="line">        );</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="使用_Refs">使用 Refs</h1><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//子组件</span></div><div class="line"><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;</div><div class="line">    <span class="keyword">constructor</span>(props) &#123;</div><div class="line">        <span class="keyword">super</span>(props);</div><div class="line">    &#125;</div><div class="line">    <span class="comment">//开放的实例方法</span></div><div class="line">    doIt() &#123;</div><div class="line">        <span class="comment">//...做点什么</span></div><div class="line">    &#125;</div><div class="line">    render() &#123;</div><div class="line">        <span class="keyword">return</span> (</div><div class="line">            <span class="xml"><span class="tag">&lt;<span class="name">View</span> /&gt;</span></span></div><div class="line">        );</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//父组件</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">parentCpt</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</div><div class="line">    <span class="keyword">constructor</span>(props) &#123;</div><div class="line">        <span class="keyword">super</span>(props);</div><div class="line">    &#125;</div><div class="line">    render() &#123;</div><div class="line">        <span class="comment">//this.myCpt 保存组件的实例</span></div><div class="line">        <span class="keyword">return</span> (</div><div class="line">            <span class="xml"><span class="tag">&lt;<span class="name">MyComponent</span> <span class="attr">ref</span>=<span class="string">&#123;(c)</span> =&gt;</span> &#123;this.myCpt = c;&#125;&#125; /&gt;</span></div><div class="line">        );</div><div class="line">    &#125;</div><div class="line">    componentDidMount() &#123;</div><div class="line">        //调用组件的实例方法</div><div class="line">        this.myCpt.doIt();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="使用_global">使用 global</h1><p><code>global</code> 类似浏览器里的 <code>window</code> 对象，它是全局的，一处定义，所有组件都可以访问，一般用于存储一些全局的配置参数或方法。</p>
<p><strong><em>使用场景：全局参数不想通过 <code>props</code> 层层组件传递，有些组件对此参数并不关心，只有嵌套的某个组件使用</em></strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">global.isOnline = <span class="literal">true</span>;</div></pre></td></tr></table></figure>
<h1 id="使用_RCTDeviceEventEmitter">使用 RCTDeviceEventEmitter</h1><p><code>RCTDeviceEventEmitter</code> 是一种事件机制，<a href="http://facebook.github.io/react-native/releases/0.39/docs/native-modules-android.html#sending-events-to-javascript" target="_blank" rel="external">React Native 的文档</a>只是草草带过，也可以使用 <code>DeviceEventEmitter</code> ，它是把 <code>RCTDeviceEventEmitter</code> 封装了一层，用法略不同。</p>
<p>按文档所言，<code>RCTDeviceEventEmitter</code> 主要用于 Native 发送事件给 JavaScript，实际上也可以用来发送自定义事件。</p>
<p><strong><em>使用场景：多个组件都使用了异步模块，且异步模块之间有顺序依赖时，可以使用。</em></strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//引入模块</span></div><div class="line"><span class="keyword">import</span> RCTDeviceEventEmitter <span class="keyword">from</span> <span class="string">'RCTDeviceEventEmitter'</span>;</div><div class="line"></div><div class="line"><span class="comment">//监听自定义事件</span></div><div class="line">RCTDeviceEventEmitter.addListener(<span class="string">'customEvt'</span>, (o) =&gt; &#123;</div><div class="line">    <span class="built_in">console</span>.log(o.data);    <span class="comment">//'some data'</span></div><div class="line"></div><div class="line">    <span class="comment">//做点其它的</span></div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">//发送自定义事件，可传数据</span></div><div class="line">RCTDeviceEventEmitter.emit(<span class="string">'customEvt'</span>, &#123;</div><div class="line">    <span class="attr">data</span>: <span class="string">'some data'</span></div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<h1 id="使用_AsyncStorage">使用 <a href="http://facebook.github.io/react-native/releases/0.39/docs/asyncstorage.html" target="_blank" rel="external">AsyncStorage</a></h1><p>这是官方提供的持久缓存的模块，类似浏览器端的 <code>localStorage</code>，用法也很类似，不过比 <code>localStorage</code> 多了不少 API。</p>
<p>使用场景：当然也类似，退出应用需要保存的少量数据，可以存在这里，至于大小限制，<a href="https://github.com/facebook/react-native/issues/3387" target="_blank" rel="external">Android 貌似是 6M</a> 。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> &#123;</div><div class="line">  AsyncStorage</div><div class="line">&#125; <span class="keyword">from</span> <span class="string">'react-native'</span>;</div><div class="line"></div><div class="line"><span class="comment">//设置</span></div><div class="line">AsyncStorage.setItem(<span class="string">'@MySuperStore:key'</span>, <span class="string">'I like to save it.'</span>);</div><div class="line"><span class="comment">//获取</span></div><div class="line">AsyncStorage.getItem(<span class="string">'@MySuperStore:key'</span>)</div></pre></td></tr></table></figure>
<p>综上所述，这是我能想到的组件通信方式，其它想到了再补充吧。</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>题外话，说几句我对 React 与 React Native 关系的理解：</p>
<ul>
<li>React 主要用于浏览器端实现一些 UI 组件，也可用于服务端渲染。React 可以使用 HTML 提供的标签，也可封装自定义的组件，React 也提供直接操作 DOM 的方法；</li>
<li>React Native 主要用于实现客户端应用（App）的 UI 组件，它只能使用 Facebook 封装的 Native 组件，或者自己封装的 Native 组件。开发中主要借助 JavaScript，基本告别 HTML 和 CSS 了，不过优点是可以用 ES6。</li>
</ul>
<hr>
<p>言归正传，正文开始</p>
<p>之前没搞过 React ，直接开撸的 React Native，使用过程也是各种踩坑填坑，磕磕绊绊，这里简单总结一下我用过的组件通信的几种方式吧。</p>
<h1 id="React_最基础的_props_和_state">React 最基础的 props 和 state</h1><h2 id="组件内部用_state">组件内部用 state</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">constructor</span>(props) &#123;</div><div class="line">    <span class="keyword">super</span>(props);</div><div class="line"></div><div class="line">    <span class="keyword">this</span>.state = &#123;</div><div class="line">        <span class="attr">isOnline</span>: <span class="literal">true</span>	<span class="comment">//组件 state</span></div><div class="line">    &#125;;</div><div class="line">&#125;</div><div class="line">render() &#123;</div><div class="line">    <span class="keyword">if</span>(<span class="keyword">this</span>.state.isOnline)&#123;</div><div class="line">        <span class="comment">//...剩余代码</span></div><div class="line">    &#125;</div><div class="line">    <span class="comment">//...剩余代码</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="React Native" scheme="http://jinlong.github.io/tags/React-Native/"/>
    
      <category term="React Native" scheme="http://jinlong.github.io/categories/React-Native/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[针对易读性的背景滤镜兼容方案（CSS filter 兼容方案）]]></title>
    <link href="http://jinlong.github.io/2016/05/15/Filtered-background-with-fallback-for-legibility/"/>
    <id>http://jinlong.github.io/2016/05/15/Filtered-background-with-fallback-for-legibility/</id>
    <published>2016-05-15T13:47:29.000Z</published>
    <updated>2016-05-23T10:08:43.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="http://codepen.io/tigt/post/blurred-background-image-with-fallback" target="_blank" rel="external">Filtered background with fallback for legibility</a><br>翻译：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<p>你知道现在有多火吗？用这种很大的，高质量的，支持 Retina 屏的<a href="https://www.google.com/search?q=blurred+background+web&amp;tbm=isch" target="_blank" rel="external">模糊的 JPEG 图片作为 header 背景</a> :</p>
<p data-height="265" data-theme-id="0" data-slug-hash="RaprZK" data-default-tab="css,result" data-user="tigt" data-embed-version="2" class="codepen">See the Pen <a href="http://codepen.io/tigt/pen/RaprZK/" target="_blank" rel="external">Web site header, circa 2016</a> by Taylor Hunt (<a href="http://codepen.io/tigt" target="_blank" rel="external">@tigt</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>潜在的问题是如果浏览器不支持滤镜 filter，<a href="https://www.nngroup.com/articles/text-over-images/" target="_blank" rel="external">文字将不可读</a>  。这违背了可访问性的原则，再完美的视觉也无济于事。</p>
<a id="more"></a>
<p><img src="/image/Filtered-background/p1.png" alt="附图1"><br>支持和不支持 filter 的效果对比</p>
<p>因此呢，你需要提前准备好处理模糊的图片，可是设计师偏要你实现动态模糊呢，图片不固定，跟 Apple 的效果一样一样的，咋办？如果是用户上传的图片又咋办？嗯，你需要一台图片处理服务器，听起来成本很高。</p>
<p>我碰巧想到一个妙招，让不支持 <code>filter</code> 的浏览器用颜色图层代替，方案并不完美，但是具有高可读性：</p>
<p><img src="/image/Filtered-background/p2.png" alt="附图2"><br>不支持 filter 的效果</p>
<h1 id="基本实现">基本实现</h1><p>我用伪元素作为背景，因为给元素加 <code>filter</code> 比<a href="http://caniuse.com/css-filter-function" target="_blank" rel="external">单纯给背景加 filter</a> <a href="http://caniuse.com/css-filters" target="_blank" rel="external">兼容性更好</a>  。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.backdrop</span> &#123; <span class="attribute">position</span>: relative &#125;</div><div class="line"><span class="selector-class">.backdrop</span><span class="selector-pseudo">::after</span> &#123;</div><div class="line">  <span class="attribute">content</span>: <span class="string">""</span>;</div><div class="line"></div><div class="line">  <span class="comment">/* 铺满整个父元素 */</span></div><div class="line">  <span class="attribute">position</span>: absolute;</div><div class="line">  <span class="attribute">top</span>: <span class="number">0</span>; <span class="attribute">right</span>: <span class="number">0</span>; <span class="attribute">bottom</span>: <span class="number">0</span>; <span class="attribute">left</span>: <span class="number">0</span>;</div><div class="line"></div><div class="line">  <span class="comment">/* 放到父元素内容的下层 */</span></div><div class="line">  <span class="attribute">z-index</span>: -<span class="number">2</span>;</div><div class="line"></div><div class="line">  <span class="comment">/* 像父元素一样展示背景 */</span></div><div class="line">  <span class="attribute">background</span>: <span class="number">#222</span> <span class="built_in">url</span>(<span class="string">"inspirational-landscape-and/or-laughing-with-salad.jpg"</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="加特效">加特效</h1><p>此时，设计师发话了，让背景图模糊一下，再来个轻微变暗效果就更好了。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/* 不要忘记前缀，Safari &gt;9.1 和所有 Chrome 仍然需要 -webkit- */</span></div><div class="line"><span class="selector-tag">filter</span>: <span class="selector-tag">blur</span>(4<span class="selector-tag">px</span>) <span class="selector-tag">brightness</span>(75%);</div></pre></td></tr></table></figure>
<p>这么做还远远不够，不支持 <code>filter</code> 的浏览器将严重影响阅读。</p>
<h1 id="上妙招">上妙招</h1><p>是否听过 <code>filter</code> 有 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter#opacity%28%29_2" target="_blank" rel="external">opacity() 效果</a>  ？对比广泛支持的 <code>opacity</code> 属性，它显得有点鸡肋，但是它的存在才使得兼容方案得以实现：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.backdrop</span><span class="selector-pseudo">::before</span> &#123;</div><div class="line">  <span class="attribute">content</span>: <span class="string">""</span>;</div><div class="line">  <span class="attribute">position</span>: absolute;</div><div class="line">  <span class="attribute">top</span>: <span class="number">0</span>; <span class="attribute">right</span>: <span class="number">0</span>; <span class="attribute">bottom</span>: <span class="number">0</span>; <span class="attribute">left</span>: <span class="number">0</span>;</div><div class="line"></div><div class="line">  <span class="comment">/* 把它放到其它伪元素的上面，但是仍在父元素内容的下面 */</span></div><div class="line">  <span class="attribute">z-index</span>: -<span class="number">1</span>;</div><div class="line"></div><div class="line">  <span class="comment">/* 让遮罩层足够的暗，无论什么背景都可以看清楚上面的文字 */</span></div><div class="line">  <span class="attribute">background</span>: <span class="built_in">rbgba</span>(0,0,0, 0.5);</div><div class="line"></div><div class="line">  <span class="comment">/* 使用滤镜完全隐藏它？？ */</span></div><div class="line">  <span class="attribute">filter</span>: <span class="built_in">opacity</span>(0%);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>如果支持 filter 滤镜，带图片的伪元素是模糊的，微微变暗的，这一黑色的遮罩层是完全透明的。如果不支持滤镜呢，显示效果并不完美，但是不会阻碍任何人阅读上面的文字。</p>
<p>CodePen 的例子在此，希望你喜欢：</p>
<p data-height="265" data-theme-id="0" data-slug-hash="bpqpMP" data-default-tab="css,result" data-user="tigt" data-embed-version="2" class="codepen">See the Pen <a href="http://codepen.io/tigt/pen/bpqpMP/" target="_blank" rel="external">Filtered background with fallback for legibility</a> by Taylor Hunt (<a href="http://codepen.io/tigt" target="_blank" rel="external">@tigt</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>其它 filter 滤镜的使用或许也能受此启发，我把它放到了我的锦囊里。</p>
<h1 id="何不用_@supports_代替？">何不用 @supports 代替？</h1><p>浏览器对 CSS 特性检测（<code>@supports</code>）的<a href="http://caniuse.com/#feat=css-featurequeries" target="_blank" rel="external">支持程度跟 filter 保持一致</a> ，替换以后代码更加清晰明了：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">@<span class="keyword">supports</span> (filter: blur(<span class="number">4px</span>) brightness(<span class="number">75%</span>)) or (-webkit-filter: blur(<span class="number">4px</span>) brightness(<span class="number">75%</span>)) &#123;</div><div class="line">  <span class="comment">/* 浏览器支持 filter 的话... */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>你完全可以这么写，全看个人喜好。我查阅了 CanIUse 的使用数据，有一些环境仍不支持 <code>@supports</code> ：</p>
<ul>
<li>Chrome 18–27</li>
<li>Safari 8.x</li>
<li>UC Browser 9.9（撰写本文时）</li>
</ul>
<p>直到<strong><em>2016年5月</em></strong>，以上浏览器的联合使用份额是 ≈ 美国 3.6%、世界范围 10.5%（感谢大洋彼岸 UC 的盛行）。这些数字会随着时间流逝逐渐减少，到那时，如果想写更加清晰的 CSS 就推荐使用 <code>@supports</code> 了。</p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="http://codepen.io/tigt/post/blurred-background-image-with-fallback">Filtered background with fallback for legibility</a><br>翻译：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
</blockquote>
<p>你知道现在有多火吗？用这种很大的，高质量的，支持 Retina 屏的<a href="https://www.google.com/search?q=blurred+background+web&amp;tbm=isch">模糊的 JPEG 图片作为 header 背景</a> :</p>
<p data-height="265" data-theme-id="0" data-slug-hash="RaprZK" data-default-tab="css,result" data-user="tigt" data-embed-version="2" class="codepen">See the Pen <a href="http://codepen.io/tigt/pen/RaprZK/">Web site header, circa 2016</a> by Taylor Hunt (<a href="http://codepen.io/tigt">@tigt</a>) on <a href="http://codepen.io">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>潜在的问题是如果浏览器不支持滤镜 filter，<a href="https://www.nngroup.com/articles/text-over-images/">文字将不可读</a>  。这违背了可访问性的原则，再完美的视觉也无济于事。</p>]]>
    
    </summary>
    
      <category term="CSS3" scheme="http://jinlong.github.io/tags/CSS3/"/>
    
      <category term="CSS3" scheme="http://jinlong.github.io/categories/CSS3/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[4类 JavaScript 内存泄漏及如何避免]]></title>
    <link href="http://jinlong.github.io/2016/05/01/4-Types-of-Memory-Leaks-in-JavaScript-and-How-to-Get-Rid-Of-Them/"/>
    <id>http://jinlong.github.io/2016/05/01/4-Types-of-Memory-Leaks-in-JavaScript-and-How-to-Get-Rid-Of-Them/</id>
    <published>2016-05-01T13:16:27.000Z</published>
    <updated>2016-05-26T02:08:11.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="https://auth0.com/blog/2016/01/26/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/" target="_blank" rel="external">4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them</a><br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
<p>译者注：本文并没有逐字逐句的翻译，而是把我认为重要的信息做了翻译。如果您的英文熟练，可以直接阅读原文。</p>
</blockquote>
<p>本文将探索常见的客户端 JavaScript 内存泄漏，以及如何使用 Chrome 开发工具发现问题。</p>
<h1 id="简介">简介</h1><p>内存泄漏是每个开发者最终都要面对的问题，它是许多问题的根源：反应迟缓，崩溃，高延迟，以及其他应用问题。</p>
<h2 id="什么是内存泄漏？">什么是内存泄漏？</h2><p>本质上，内存泄漏可以定义为：应用程序不再需要占用内存的时候，由于某些原因，内存没有被操作系统或可用内存池回收。编程语言管理内存的方式各不相同。只有开发者最清楚哪些内存不需要了，操作系统可以回收。一些编程语言提供了语言特性，可以帮助开发者做此类事情。另一些则寄希望于开发者对内存是否需要清晰明了。</p>
<h2 id="JavaScript_内存管理">JavaScript 内存管理</h2><p>JavaScript 是一种垃圾回收语言。垃圾回收语言通过周期性地检查先前分配的内存是否可达，帮助开发者管理内存。换言之，垃圾回收语言减轻了“内存仍可用”及“内存仍可达”的问题。两者的区别是微妙而重要的：仅有开发者了解哪些内存在将来仍会使用，而不可达内存通过算法确定和标记，适时被操作系统回收。</p>
<h1 id="JavaScript_内存泄漏">JavaScript 内存泄漏</h1><p>垃圾回收语言的内存泄漏主因是不需要的引用。理解它之前，还需了解垃圾回收语言如何辨别内存的可达与不可达。</p>
<h2 id="Mark-and-sweep">Mark-and-sweep</h2><p>大部分垃圾回收语言用的算法称之为 Mark-and-sweep 。算法由以下几步组成：</p>
<ol>
<li>垃圾回收器创建了一个“roots”列表。Roots 通常是代码中全局变量的引用。JavaScript  中，“window” 对象是一个全局变量，被当作 root 。window 对象总是存在，因此垃圾回收器可以检查它和它的所有子对象是否存在（即不是垃圾）；</li>
<li>所有的 roots 被检查和标记为激活（即不是垃圾）。所有的子对象也被递归地检查。从 root 开始的所有对象如果是可达的，它就不被当作垃圾。</li>
<li>所有未被标记的内存会被当做垃圾，收集器现在可以释放内存，归还给操作系统了。</li>
</ol>
<p>现代的垃圾回收器改良了算法，但是本质是相同的：可达内存被标记，其余的被当作垃圾回收。</p>
<p>不需要的引用是指开发者明知内存引用不再需要，却由于某些原因，它仍被留在激活的 root 树中。在 JavaScript 中，不需要的引用是保留在代码中的变量，它不再需要，却指向一块本该被释放的内存。有些人认为这是开发者的错误。</p>
<p>为了理解 JavaScript 中最常见的内存泄漏，我们需要了解哪种方式的引用容易被遗忘。</p>
<a id="more"></a>
<h1 id="三种类型的常见_JavaScript_内存泄漏">三种类型的常见 JavaScript 内存泄漏</h1><h2 id="1：意外的全局变量">1：意外的全局变量</h2><p>JavaScript 处理未定义变量的方式比较宽松：未定义的变量会在全局对象创建一个新变量。在浏览器中，全局对象是 <code>window</code> 。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">arg</span>) </span>&#123;</div><div class="line">    bar = <span class="string">"this is a hidden global variable"</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>真相是：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">arg</span>) </span>&#123;</div><div class="line">    <span class="built_in">window</span>.bar = <span class="string">"this is an explicit global variable"</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>函数 <code>foo</code> 内部忘记使用 <code>var</code> ，意外创建了一个全局变量。此例泄漏了一个简单的字符串，无伤大雅，但是有更糟的情况。</p>
<p>另一种意外的全局变量可能由 <code>this</code> 创建：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">this</span>.variable = <span class="string">"potential accidental global"</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// Foo 调用自己，this 指向了全局对象（window）</span></div><div class="line"><span class="comment">// 而不是 undefined</span></div><div class="line">foo();</div></pre></td></tr></table></figure>
<blockquote>
<p>在 JavaScript 文件头部加上 <code>&#39;use strict&#39;</code>，可以避免此类错误发生。启用严格模式解析 JavaScript ，避免意外的全局变量。</p>
</blockquote>
<p><strong>全局变量注意事项</strong></p>
<p>尽管我们讨论了一些意外的全局变量，但是仍有一些明确的全局变量产生的垃圾。它们被定义为不可回收（除非定义为空或重新分配）。尤其当全局变量用于临时存储和处理大量信息时，需要多加小心。如果必须使用全局变量存储大量数据时，确保用完以后把它设置为 null 或者重新定义。与全局变量相关的增加内存消耗的一个主因是缓存。缓存数据是为了重用，缓存必须有一个大小上限才有用。高内存消耗导致缓存突破上限，因为缓存内容无法被回收。</p>
<h2 id="2：被遗忘的计时器或回调函数">2：被遗忘的计时器或回调函数</h2><p>在 JavaScript 中使用 <code>setInterval</code> 非常平常。一段常见的代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> someResource = getData();</div><div class="line">setInterval(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> node = <span class="built_in">document</span>.getElementById(<span class="string">'Node'</span>);</div><div class="line">    <span class="keyword">if</span>(node) &#123;</div><div class="line">        <span class="comment">// 处理 node 和 someResource</span></div><div class="line">        node.innerHTML = <span class="built_in">JSON</span>.stringify(someResource));</div><div class="line">    &#125;</div><div class="line">&#125;, <span class="number">1000</span>);</div></pre></td></tr></table></figure>
<p>此例说明了什么：与节点或数据关联的计时器不再需要，<code>node</code> 对象可以删除，整个回调函数也不需要了。可是，计时器回调函数仍然没被回收（计时器停止才会被回收）。同时，<code>someResource</code> 如果存储了大量的数据，也是无法被回收的。</p>
<p>对于观察者的例子，一旦它们不再需要（或者关联的对象变成不可达），明确地移除它们非常重要。老的 IE 6 是无法处理循环引用的。如今，即使没有明确移除它们，一旦观察者对象变成不可达，大部分浏览器是可以回收观察者处理函数的。</p>
<p>观察者代码示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> element = <span class="built_in">document</span>.getElementById(<span class="string">'button'</span>);</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">onClick</span>(<span class="params">event</span>) </span>&#123;</div><div class="line">    element.innerHTML = <span class="string">'text'</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line">element.addEventListener(<span class="string">'click'</span>, onClick);</div></pre></td></tr></table></figure>
<p><strong>对象观察者和循环引用注意事项</strong></p>
<p>老版本的 IE 是无法检测 DOM 节点与 JavaScript 代码之间的循环引用，会导致内存泄漏。如今，现代的浏览器（包括 IE 和 Microsoft Edge）使用了更先进的垃圾回收算法，已经可以正确检测和处理循环引用了。换言之，回收节点内存时，不必非要调用 <code>removeEventListener</code> 了。</p>
<h2 id="3：脱离_DOM_的引用">3：脱离 DOM 的引用</h2><p>有时，保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容，把每一行 DOM 存成字典（JSON 键值对）或者数组很有意义。此时，同样的 DOM 元素存在两个引用：一个在 DOM 树中，另一个在字典中。将来你决定删除这些行时，需要把两个引用都清除。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> elements = &#123;</div><div class="line">    <span class="attr">button</span>: <span class="built_in">document</span>.getElementById(<span class="string">'button'</span>),</div><div class="line">    <span class="attr">image</span>: <span class="built_in">document</span>.getElementById(<span class="string">'image'</span>),</div><div class="line">    <span class="attr">text</span>: <span class="built_in">document</span>.getElementById(<span class="string">'text'</span>)</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">doStuff</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    image.src = <span class="string">'http://some.url/image'</span>;</div><div class="line">    button.click();</div><div class="line">    <span class="built_in">console</span>.log(text.innerHTML);</div><div class="line">    <span class="comment">// 更多逻辑</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">removeButton</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="comment">// 按钮是 body 的后代元素</span></div><div class="line">    <span class="built_in">document</span>.body.removeChild(<span class="built_in">document</span>.getElementById(<span class="string">'button'</span>));</div><div class="line"></div><div class="line">    <span class="comment">// 此时，仍旧存在一个全局的 #button 的引用</span></div><div class="line">    <span class="comment">// elements 字典。button 元素仍旧在内存中，不能被 GC 回收。</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>此外还要考虑 DOM 树内部或子节点的引用问题。假如你的 JavaScript 代码中保存了表格某一个 <code>&lt;td&gt;</code> 的引用。将来决定删除整个表格的时候，直觉认为 GC 会回收除了已保存的 <code>&lt;td&gt;</code> 以外的其它节点。实际情况并非如此：此 <code>&lt;td&gt;</code> 是表格的子节点，子元素与父元素是引用关系。由于代码保留了 <code>&lt;td&gt;</code> 的引用，导致整个表格仍待在内存中。保存 DOM 元素引用的时候，要小心谨慎。</p>
<h2 id="4：闭包">4：闭包</h2><p>闭包是 JavaScript 开发的一个关键方面：匿名函数可以访问父级作用域的变量。</p>
<p>代码示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> theThing = <span class="literal">null</span>;</div><div class="line"><span class="keyword">var</span> replaceThing = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> originalThing = theThing;</div><div class="line">  <span class="keyword">var</span> unused = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">if</span> (originalThing)</div><div class="line">      <span class="built_in">console</span>.log(<span class="string">"hi"</span>);</div><div class="line">  &#125;;</div><div class="line"></div><div class="line">  theThing = &#123;</div><div class="line">    <span class="attr">longStr</span>: <span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">1000000</span>).join(<span class="string">'*'</span>),</div><div class="line">    <span class="attr">someMethod</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</div><div class="line">      <span class="built_in">console</span>.log(someMessage);</div><div class="line">    &#125;</div><div class="line">  &#125;;</div><div class="line">&#125;;</div><div class="line"></div><div class="line">setInterval(replaceThing, <span class="number">1000</span>);</div></pre></td></tr></table></figure>
<p>代码片段做了一件事情：每次调用 <code>replaceThing</code> <code>，theThing</code> 得到一个包含一个大数组和一个新闭包（<code>someMethod</code>）的新对象。同时，变量 <code>unused</code> 是一个引用 <code>originalThing</code> 的闭包（先前的 <code>replaceThing</code> 又调用了 <code>theThing</code> ）。思绪混乱了吗？最重要的事情是，闭包的作用域一旦创建，它们有同样的父级作用域，作用域是共享的。<code>someMethod</code> 可以通过 <code>theThing</code> 使用，<code>someMethod</code> 与 <code>unused</code> 分享闭包作用域，尽管 <code>unused</code> 从未使用，它引用的 <code>originalThing</code> 迫使它保留在内存中（防止被回收）。当这段代码反复运行，就会看到内存占用不断上升，垃圾回收器（GC）并无法降低内存占用。本质上，闭包的链表已经创建，每一个闭包作用域携带一个指向大数组的间接的引用，造成严重的内存泄漏。</p>
<blockquote>
<p><a href="http://info.meteor.com/blog/an-interesting-kind-of-javascript-memory-leak" target="_blank" rel="external">Meteor 的博文</a> 解释了如何修复此种问题。在 <code>replaceThing</code> 的最后添加 <code>originalThing = null</code> 。</p>
</blockquote>
<h1 id="Chrome_内存剖析工具概览">Chrome 内存剖析工具概览</h1><p>Chrome 提供了一套很棒的检测 JavaScript 内存占用的工具。与内存相关的两个重要的工具：<code>timeline</code> 和 <code>profiles</code>。</p>
<h2 id="Timeline">Timeline</h2><p><img src="/image/4-Types-of-Memory-Leaks/p1.png" alt="附图1"></p>
<p>timeline 可以检测代码中不需要的内存。在此截图中，我们可以看到潜在的泄漏对象稳定的增长，数据采集快结束时，内存占用明显高于采集初期，Node（节点）的总量也很高。种种迹象表明，代码中存在 DOM 节点泄漏的情况。</p>
<h2 id="Profiles">Profiles</h2><p><img src="/image/4-Types-of-Memory-Leaks/p2.png" alt="附图2"></p>
<p>Profiles 是你可以花费大量时间关注的工具，它可以保存快照，对比 JavaScript 代码内存使用的不同快照，也可以记录时间分配。每一次结果包含不同类型的列表，与内存泄漏相关的有 summary（概要） 列表和 comparison（对照） 列表。</p>
<p>summary（概要） 列表展示了不同类型对象的分配及合计大小：shallow size（特定类型的所有对象的总大小），retained size（shallow size 加上其它与此关联的对象大小）。它还提供了一个概念，一个对象与关联的 GC root 的距离。</p>
<p>对比不同的快照的 comparison list 可以发现内存泄漏。</p>
<h1 id="实例：使用_Chrome_发现内存泄漏">实例：使用 Chrome 发现内存泄漏</h1><p>实质上有两种类型的泄漏：周期性的内存增长导致的泄漏，以及偶现的内存泄漏。显而易见，周期性的内存泄漏很容易发现；偶现的泄漏比较棘手，一般容易被忽视，偶尔发生一次可能被认为是优化问题，周期性发生的则被认为是必须解决的 bug。</p>
<p>以 <a href="https://developer.chrome.com/devtools/docs/demos/memory/example1" target="_blank" rel="external">Chrome 文档</a>中的代码为例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> x = [];</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">createSomeNodes</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> div,</div><div class="line">        i = <span class="number">100</span>,</div><div class="line">        frag = <span class="built_in">document</span>.createDocumentFragment();</div><div class="line"></div><div class="line">    <span class="keyword">for</span> (;i &gt; <span class="number">0</span>; i--) &#123;</div><div class="line">        div = <span class="built_in">document</span>.createElement(<span class="string">"div"</span>);</div><div class="line">        div.appendChild(<span class="built_in">document</span>.createTextNode(i + <span class="string">" - "</span>+ <span class="keyword">new</span> <span class="built_in">Date</span>().toTimeString()));</div><div class="line">        frag.appendChild(div);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="built_in">document</span>.getElementById(<span class="string">"nodes"</span>).appendChild(frag);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">grow</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    x.push(<span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">1000000</span>).join(<span class="string">'x'</span>));</div><div class="line">    createSomeNodes();</div><div class="line">    setTimeout(grow,<span class="number">1000</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>当 <code>grow</code> 执行的时候，开始创建 div 节点并插入到 DOM 中，并且给全局变量分配一个巨大的数组。通过以上提到的工具可以检测到内存稳定上升。</p>
<h2 id="找出周期性增长的内存">找出周期性增长的内存</h2><p>timeline 标签擅长做这些。在 Chrome 中<a href="https://developer.chrome.com/devtools/docs/demos/memory/example1" target="_blank" rel="external">打开例子</a>，打开 Dev Tools ，切换到 timeline，勾选 memory 并点击记录按钮，然后点击页面上的 <code>The Button</code> 按钮。过一阵停止记录看结果：</p>
<p><img src="/image/4-Types-of-Memory-Leaks/p3.png" alt="附图3"></p>
<p>两种迹象显示出现了内存泄漏，图中的 Nodes（绿线）和 JS heap（蓝线）。Nodes 稳定增长，并未下降，这是个显著的信号。</p>
<p>JS heap 的内存占用也是稳定增长。由于垃圾收集器的影响，并不那么容易发现。图中显示内存占用忽涨忽跌，实际上每一次下跌之后，JS heap 的大小都比原先大了。换言之，尽管垃圾收集器不断的收集内存，内存还是周期性的泄漏了。</p>
<p>确定存在内存泄漏之后，我们找找根源所在。</p>
<h2 id="保存两个快照">保存两个快照</h2><p>切换到 Chrome Dev Tools 的 profiles 标签，刷新页面，等页面刷新完成之后，点击 Take Heap Snapshot 保存快照作为基准。而后再次点击 <code>The Button</code> 按钮，等数秒以后，保存第二个快照。</p>
<p><img src="/image/4-Types-of-Memory-Leaks/p4.png" alt="附图4"></p>
<p>筛选菜单选择 Summary，右侧选择 Objects allocated between Snapshot 1 and Snapshot 2，或者筛选菜单选择 Comparison ，然后可以看到一个对比列表。</p>
<p>此例很容易找到内存泄漏，看下  <code>(string)</code> 的 <code>Size Delta</code> Constructor，8MB，58个新对象。新对象被分配，但是没有释放，占用了8MB。</p>
<p>如果展开  <code>(string)</code>  Constructor，会看到许多单独的内存分配。选择某一个单独的分配，下面的 retainers 会吸引我们的注意。</p>
<p><img src="/image/4-Types-of-Memory-Leaks/p5.png" alt="附图5"></p>
<p>我们已选择的分配是数组的一部分，数组关联到 <code>window</code> 对象的 <code>x</code> 变量。这里展示了从巨大对象到无法回收的 root（<code>window</code>）的完整路径。我们已经找到了潜在的泄漏以及它的出处。</p>
<p>我们的例子还算简单，只泄漏了少量的 DOM 节点，利用以上提到的快照很容易发现。对于更大型的网站，Chrome 还提供了 Record Heap Allocations 功能。</p>
<h2 id="Record_heap_allocations_找内存泄漏">Record heap allocations 找内存泄漏</h2><p>回到 Chrome Dev Tools 的 profiles 标签，点击 Record Heap Allocations。工具运行的时候，注意顶部的蓝条，代表了内存分配，每一秒有大量的内存分配。运行几秒以后停止。</p>
<p><img src="/image/4-Types-of-Memory-Leaks/p6.png" alt="附图6"></p>
<p>上图中可以看到工具的杀手锏：选择某一条时间线，可以看到这个时间段的内存分配情况。尽可能选择接近峰值的时间线，下面的列表仅显示了三种 constructor：其一是泄漏最严重的<code>（string）</code>，下一个是关联的 DOM 分配，最后一个是 <code>Text</code> constructor（DOM 叶子节点包含的文本）。</p>
<p>从列表中选择一个 <code>HTMLDivElement</code> constructor，然后选择 <code>Allocation stack</code>。</p>
<p><img src="/image/4-Types-of-Memory-Leaks/p7.png" alt="附图7"></p>
<p>现在知道元素被分配到哪里了吧（<code>grow</code> -&gt; <code>createSomeNodes</code>），仔细观察一下图中的时间线，发现 <code>HTMLDivElement</code> constructor 调用了许多次，意味着内存一直被占用，无法被 GC 回收，我们知道了这些对象被分配的确切位置（<code>createSomeNodes</code>）。回到代码本身，探讨下如何修复内存泄漏吧。</p>
<h2 id="另一个有用的特性">另一个有用的特性</h2><p>在 heap allocations 的结果区域，选择 Allocation。</p>
<p><img src="/image/4-Types-of-Memory-Leaks/p8.png" alt="附图8"></p>
<p>这个视图呈现了内存分配相关的功能列表，我们立刻看到了 <code>grow</code> 和 <code>createSomeNodes</code>。当选择 <code>grow</code> 时，看看相关的 object constructor，清楚地看到 <code>(string)</code>, <code>HTMLDivElement</code> 和 <code>Text</code> 泄漏了。</p>
<p>结合以上提到的工具，可以轻松找到内存泄漏。</p>
<h1 id="延伸阅读">延伸阅读</h1><ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management" target="_blank" rel="external">Memory Management - Mozilla Developer Network</a></li>
<li><a href="http://javascript.crockford.com/memory/leak.html" target="_blank" rel="external">JScript Memory Leaks - Douglas Crockford (old, in relation to Internet Explorer 6 leaks)</a></li>
<li><a href="https://developer.chrome.com/devtools/docs/javascript-memory-profiling" target="_blank" rel="external">JavaScript Memory Profiling - Chrome Developer Docs</a></li>
<li><a href="https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/memory-diagnosis" target="_blank" rel="external">Memory Diagnosis - Google Developers</a></li>
<li><a href="http://info.meteor.com/blog/an-interesting-kind-of-javascript-memory-leak" target="_blank" rel="external">An Interesting Kind of JavaScript Memory Leak - Meteor blog</a></li>
<li><a href="http://mrale.ph/blog/2012/09/23/grokking-v8-closures-for-fun.html" target="_blank" rel="external">Grokking V8 closures</a></li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="https://auth0.com/blog/2016/01/26/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/">4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them</a><br>笔记：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
<p>译者注：本文并没有逐字逐句的翻译，而是把我认为重要的信息做了翻译。如果您的英文熟练，可以直接阅读原文。</p>
</blockquote>
<p>本文将探索常见的客户端 JavaScript 内存泄漏，以及如何使用 Chrome 开发工具发现问题。</p>
<h1 id="简介">简介</h1><p>内存泄漏是每个开发者最终都要面对的问题，它是许多问题的根源：反应迟缓，崩溃，高延迟，以及其他应用问题。</p>
<h2 id="什么是内存泄漏？">什么是内存泄漏？</h2><p>本质上，内存泄漏可以定义为：应用程序不再需要占用内存的时候，由于某些原因，内存没有被操作系统或可用内存池回收。编程语言管理内存的方式各不相同。只有开发者最清楚哪些内存不需要了，操作系统可以回收。一些编程语言提供了语言特性，可以帮助开发者做此类事情。另一些则寄希望于开发者对内存是否需要清晰明了。</p>
<h2 id="JavaScript_内存管理">JavaScript 内存管理</h2><p>JavaScript 是一种垃圾回收语言。垃圾回收语言通过周期性地检查先前分配的内存是否可达，帮助开发者管理内存。换言之，垃圾回收语言减轻了“内存仍可用”及“内存仍可达”的问题。两者的区别是微妙而重要的：仅有开发者了解哪些内存在将来仍会使用，而不可达内存通过算法确定和标记，适时被操作系统回收。</p>
<h1 id="JavaScript_内存泄漏">JavaScript 内存泄漏</h1><p>垃圾回收语言的内存泄漏主因是不需要的引用。理解它之前，还需了解垃圾回收语言如何辨别内存的可达与不可达。</p>
<h2 id="Mark-and-sweep">Mark-and-sweep</h2><p>大部分垃圾回收语言用的算法称之为 Mark-and-sweep 。算法由以下几步组成：</p>
<ol>
<li>垃圾回收器创建了一个“roots”列表。Roots 通常是代码中全局变量的引用。JavaScript  中，“window” 对象是一个全局变量，被当作 root 。window 对象总是存在，因此垃圾回收器可以检查它和它的所有子对象是否存在（即不是垃圾）；</li>
<li>所有的 roots 被检查和标记为激活（即不是垃圾）。所有的子对象也被递归地检查。从 root 开始的所有对象如果是可达的，它就不被当作垃圾。</li>
<li>所有未被标记的内存会被当做垃圾，收集器现在可以释放内存，归还给操作系统了。</li>
</ol>
<p>现代的垃圾回收器改良了算法，但是本质是相同的：可达内存被标记，其余的被当作垃圾回收。</p>
<p>不需要的引用是指开发者明知内存引用不再需要，却由于某些原因，它仍被留在激活的 root 树中。在 JavaScript 中，不需要的引用是保留在代码中的变量，它不再需要，却指向一块本该被释放的内存。有些人认为这是开发者的错误。</p>
<p>为了理解 JavaScript 中最常见的内存泄漏，我们需要了解哪种方式的引用容易被遗忘。</p>]]>
    
    </summary>
    
      <category term="内存泄漏" scheme="http://jinlong.github.io/tags/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[实例解析防抖动（Debouncing）和节流阀（Throttling）]]></title>
    <link href="http://jinlong.github.io/2016/04/24/Debouncing-and-Throttling-Explained-Through-Examples/"/>
    <id>http://jinlong.github.io/2016/04/24/Debouncing-and-Throttling-Explained-Through-Examples/</id>
    <published>2016-04-24T14:30:00.000Z</published>
    <updated>2016-04-24T15:03:12.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="https://css-tricks.com/debouncing-throttling-explained-examples/" target="_blank" rel="external">Debouncing and Throttling Explained Through Examples</a><br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<p>防抖（<strong>Debounce</strong>）和节流（<strong>throttle</strong>）都是用来控制某个函数在一定时间内执行多少次的技巧，两者相似而又不同。</p>
<p>当我们给 DOM 绑定事件的时候，加了防抖和节流的函数变得特别有用。为什么呢？因为我们在事件和函数执行之间加了一个控制层。记住，我们是无法控制 DOM 事件触发频率的。</p>
<p>看下滚动事件的例子：</p>
<p data-height="268" data-theme-id="0" data-slug-hash="PZOZgB" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/PZOZgB/" target="_blank" rel="external">Scroll events counter</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>当使用触控板，滚动滚轮，或者拖拽滚动条的时候，一秒可以轻松触发30次事件。经我的测试，在智能手机上，慢慢滚动一下，一秒可以触发事件100次之多。这么高的执行频率，你的滚动回调函数压力大吗？</p>
<p>早在2011年，Twitter 网站抛出了一个问题：向下滚动 Twitter 信息流的时候，变得很慢，很迟钝。John Resig 发表了<a href="http://ejohn.org/blog/learning-from-twitter" target="_blank" rel="external">一篇博客解释这个问题</a>，文中解释到直接给 <code>scroll</code> 事件关联昂贵的函数，是多么糟糕的主意。</p>
<p>John（5年前）建议的解决方案是，在 <code>onScroll</code> 事件外部，每 250ms 循环执行一次。简单的技巧，避免了影响用户体验。</p>
<p>现如今，有一些稍微高端的方式处理事件。我来结合用例介绍下 Debounce，Throttle 和 requestAnimationFrame 吧。</p>
<a id="more"></a>
<h1 id="防抖动（Debounce）">防抖动（Debounce）</h1><p>防抖技术可以把多个顺序地调用合并成一次。</p>
<p><img src="/image/debounce-throttle/p1.png" alt="附图1"></p>
<p>假想一下，你在电梯中，门快要关了，突然有人准备上来。电梯并没有改变楼层，而是再次打开梯门。电梯延迟了改变楼层的功能，但是优化了资源。</p>
<p>在顶部按钮上点击或移动鼠标试一下：</p>
<p data-height="268" data-theme-id="0" data-slug-hash="KVxGqN" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/KVxGqN/" target="_blank" rel="external">Debounce. Trailing</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>你可以看到连续快速的事件是如何被一个 debounce 事件替代的。但是如果事件触发的时间间隔过长，debounce 则不会生效。</p>
<h2 id="前缘（或者“immediate”）">前缘（或者“immediate”）</h2><p>你会发现，直到事件停止快速执行以后，debounce 事件才会触发相应功能。为何不立即触发呢？那样的话就跟原本的非 debounce 处理无异了。</p>
<p>直到两次快速调用之间的停顿结束，事件才会再次触发。</p>
<p>这是带 <code>leading</code> 标记的例子：</p>
<p><img src="/image/debounce-throttle/p2.png" alt="附图1"><br><em>前缘 debounce 的例子</em></p>
<p>在 underscore.js 中，选项叫 <code>immediate</code> ，而不是 <code>leading</code>：</p>
<p data-height="268" data-theme-id="0" data-slug-hash="GZWqNV" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/GZWqNV/" target="_blank" rel="external">Debounce. Leading</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<h2 id="Debounce_实现">Debounce 实现</h2><p>我首次看到 debounce 的 JavaScript 实现是在 2009 年的 <a href="http://unscriptable.com/2009/03/20/debouncing-javascript-methods/" target="_blank" rel="external">John Hann 的博文</a>。</p>
<p>不久后，Ben Alman 做了个 <a href="http://benalman.com/projects/jquery-throttle-debounce-plugin/" target="_blank" rel="external">jQuery 插件</a>（不再维护），一年后 Jeremy Ashkenas 把它<a href="https://github.com/jashkenas/underscore/commit/9e3e067f5025dbe5e93ed784f93b233882ca0ffe" target="_blank" rel="external">加入了 underscore.js</a>。而后加入了 Lodash 。</p>
<p data-height="268" data-theme-id="0" data-slug-hash="YwNwgy" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/YwNwgy/" target="_blank" rel="external">New example</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>Lodash 给 <code>_.debounce</code> 和 <code>_.throttle</code> 添加了<a href="https://lodash.com/docs#debounce" target="_blank" rel="external">不少特性</a>。之前的 <code>immediate</code> 被 <code>leading</code>（最前面） 和 <code>trailing</code>（最后面） 选项取代。你可以选一种，或者都选，默认只有 <code>trailing</code> 启用。</p>
<p>新的 <code>maxWait</code> 选项（仅 Lodash 有）本文未提及，但是也很有用。事实上，throttle 方法是用 <code>_.debounce</code> 加 <code>maxWait</code> 实现的，你可以看 <a href="https://github.com/lodash/lodash/blob/4.7.0/lodash.js#L9840" target="_blank" rel="external">lodash 源码</a>。</p>
<h2 id="Debounce_实例">Debounce 实例</h2><p><strong>调整大小的例子</strong></p>
<p>调整桌面浏览器窗口大小的时候，会触发很多次 <code>resize</code> 事件。</p>
<p>看下面 demo：</p>
<p data-height="268" data-theme-id="0" data-slug-hash="XXPjpd" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/XXPjpd/" target="_blank" rel="external">Debounce Resize Event Example</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>如你所见，我们为 resize 事件使用了默认的 <code>trailing</code> 选项，因为我们只关心用户停止调整大小后的最终值。</p>
<p><strong>基于 AJAX 请求的自动完成功能，通过 keypress 触发</strong></p>
<p>为什么用户还在输入的时候，每隔50ms就向服务器发送一次 AJAX 请求？<code>_.debounce</code> 可以帮忙，当用户停止输入的时候，再发送请求。</p>
<p>此处也不需要 <code>leading</code> 标记，我们想等最后一个字符输完。</p>
<p data-height="268" data-theme-id="0" data-slug-hash="mVGVOL" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/mVGVOL/" target="_blank" rel="external">Debouncing keystrokes Example</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>相似的使用场景还有，直到用户输完，才验证输入的正确性，显示错误信息。</p>
<h1 id="如何使用_debounce_和_throttle_以及常见的坑">如何使用 debounce 和 throttle 以及常见的坑</h1><p>自己造一个 debounce / throttle 的轮子看起来多么诱人，或者随便找个博文复制过来。<strong>我是建议直接使用 underscore 或 Lodash</strong> 。如果仅需要 <code>_.debounce</code> 和 <code>_.throttle</code> 方法，可以使用 Lodash 的自定义构建工具，生成一个 2KB 的压缩库。使用以下的简单命令即可：</p>
<figure class="highlight stata"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">npm i -<span class="keyword">g</span> lodash-<span class="keyword">cli</span></div><div class="line">lodash-<span class="keyword">cli</span> <span class="keyword">include</span>=debounce,throttle</div></pre></td></tr></table></figure>
<p>常见的坑是，不止一次地调用 <code>_.debounce</code> 方法：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 错误</span></div><div class="line">$(<span class="built_in">window</span>).on(<span class="string">'scroll'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">   _.debounce(doSomething, <span class="number">300</span>); </div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">// 正确</span></div><div class="line">$(<span class="built_in">window</span>).on(<span class="string">'scroll'</span>, _.debounce(doSomething, <span class="number">200</span>));</div></pre></td></tr></table></figure>
<p>debounce 方法保存到一个变量以后，就可以用它的私有方法 <code>debounced_version.cancel()</code>，lodash 和 underscore.js 都有效。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> debounced_version = _.debounce(doSomething, <span class="number">200</span>);</div><div class="line">$(<span class="built_in">window</span>).on(<span class="string">'scroll'</span>, debounced_version);</div><div class="line"></div><div class="line"><span class="comment">// 如果需要的话</span></div><div class="line">debounced_version.cancel();</div></pre></td></tr></table></figure>
<h1 id="Throttle（节流阀）">Throttle（节流阀）</h1><p>使用 _.throttle 的时候，只允许一个函数在 X 毫秒内执行一次。</p>
<p>跟 debounce 主要的不同在于，throttle 保证 X 毫秒内至少执行一次。</p>
<h2 id="节流阀实例">节流阀实例</h2><p><strong>无限滚动</strong></p>
<p>用户向下滚动无限滚动页面，需要检查滚动位置距底部多远，如果邻近底部了，我们可以发 AJAX 请求获取更多的数据插入到页面中。</p>
<p>我们心爱的 <code>_.debounce</code> 就不适用了，只有当用户停止滚动的时候它才会触发。只要用户滚动至邻近底部时，我们就想获取内容。</p>
<p>使用 <code>_.throttle</code> 可以保证我们不断检查距离底部有多远。</p>
<p data-height="268" data-theme-id="0" data-slug-hash="eJLMxa" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/eJLMxa/" target="_blank" rel="external">Infinite scrolling throttled</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<h1 id="requestAnimationFrame（rAF）">requestAnimationFrame（rAF）</h1><p><code>requestAnimationFrame</code> 是另一种限速执行的方式。</p>
<p>跟 <code>_.throttle(dosomething, 16)</code> 等价。它是高保真的，如果追求更好的精确度的话，可以用浏览器原生的 API 。</p>
<p>可以使用 rAF API 替换 throttle 方法，考虑一下优缺点：</p>
<h2 id="优点">优点</h2><ul>
<li>动画保持 60fps（每一帧 16 ms），浏览器内部决定渲染的最佳时机</li>
<li>简洁标准的 API，后期维护成本低</li>
</ul>
<h2 id="缺点">缺点</h2><ul>
<li>动画的开始/取消需要开发者自己控制，不像 ‘.debounce’ 或 ‘.throttle’由函数内部处理。</li>
<li>浏览器标签未激活时，一切都不会执行。</li>
<li>尽管所有的现代浏览器<a href="http://caniuse.com/#feat=requestanimationframe" target="_blank" rel="external">都支持 rAF</a>  ，IE9，Opera Mini 和 老的 Android 还是<a href="http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/" target="_blank" rel="external">需要打补丁</a>。</li>
<li>Node.js 不支持，无法在服务器端用于文件系统事件。</li>
</ul>
<p>根据经验，如果 JavaScript 方法需要绘制或者直接改变属性，我会选择 <code>requestAnimationFrame</code>，只要涉及到重新计算元素位置，就可以使用它。</p>
<p>涉及到 AJAX 请求，添加/移除 class （可以触发 CSS 动画），我会选择 <code>_.debounce</code> 或者 <code>_.throttle</code> ，可以设置更低的执行频率（例子中的200ms 换成16ms）。</p>
<h2 id="rAF_实例">rAF 实例</h2><p>灵感来自于 <a href="http://www.html5rocks.com/en/tutorials/speed/animations/" target="_blank" rel="external">Paul Lewis 的文章</a>，我将用 requestAnimationFrame 控制 scroll 。</p>
<p>16ms 的 <code>_.throttle</code> 拿来做对比，性能相仿，用于更复杂的场景时，rAF 可能效果更佳。</p>
<p data-height="268" data-theme-id="0" data-slug-hash="pgOKKw" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/pgOKKw/" target="_blank" rel="external">Scroll comparison requestAnimationFrame vs throttle</a> by Corbacho (<a href="http://codepen.io/dcorb" target="_blank" rel="external">@dcorb</a>) on <a href="http://codepen.io" target="_blank" rel="external">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>headroom.js 是个更<a href="https://github.com/WickyNilliams/headroom.js/blob/3282c23bc69b14f21bfbaf66704fa37b58e3241d/src/Debouncer.js" target="_blank" rel="external">高级的例子</a>。</p>
<h1 id="结论">结论</h1><p>使用 debounce，throttle 和 <code>requestAnimationFrame</code> 都可以优化事件处理，三者各不相同，又相辅相成。</p>
<p>总之：</p>
<ul>
<li><p><strong>debounce</strong>：把触发非常频繁的事件（比如按键）合并成一次执行。</p>
</li>
<li><p><strong>throttle</strong>：保证每 X 毫秒恒定的执行次数，比如每200ms检查下滚动位置，并触发 CSS 动画。</p>
</li>
<li><p><strong>requestAnimationFrame</strong>：可替代 throttle ，函数需要重新计算和渲染屏幕上的元素时，想保证动画或变化的平滑性，可以用它。注意：IE9 不支持。</p>
</li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="https://css-tricks.com/debouncing-throttling-explained-examples/">Debouncing and Throttling Explained Through Examples</a><br>笔记：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
</blockquote>
<p>防抖（<strong>Debounce</strong>）和节流（<strong>throttle</strong>）都是用来控制某个函数在一定时间内执行多少次的技巧，两者相似而又不同。</p>
<p>当我们给 DOM 绑定事件的时候，加了防抖和节流的函数变得特别有用。为什么呢？因为我们在事件和函数执行之间加了一个控制层。记住，我们是无法控制 DOM 事件触发频率的。</p>
<p>看下滚动事件的例子：</p>
<p data-height="268" data-theme-id="0" data-slug-hash="PZOZgB" data-default-tab="result" data-user="dcorb" class="codepen">See the Pen <a href="http://codepen.io/dcorb/pen/PZOZgB/">Scroll events counter</a> by Corbacho (<a href="http://codepen.io/dcorb">@dcorb</a>) on <a href="http://codepen.io">CodePen</a>.</p>

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

<p>当使用触控板，滚动滚轮，或者拖拽滚动条的时候，一秒可以轻松触发30次事件。经我的测试，在智能手机上，慢慢滚动一下，一秒可以触发事件100次之多。这么高的执行频率，你的滚动回调函数压力大吗？</p>
<p>早在2011年，Twitter 网站抛出了一个问题：向下滚动 Twitter 信息流的时候，变得很慢，很迟钝。John Resig 发表了<a href="http://ejohn.org/blog/learning-from-twitter">一篇博客解释这个问题</a>，文中解释到直接给 <code>scroll</code> 事件关联昂贵的函数，是多么糟糕的主意。</p>
<p>John（5年前）建议的解决方案是，在 <code>onScroll</code> 事件外部，每 250ms 循环执行一次。简单的技巧，避免了影响用户体验。</p>
<p>现如今，有一些稍微高端的方式处理事件。我来结合用例介绍下 Debounce，Throttle 和 requestAnimationFrame 吧。</p>]]>
    
    </summary>
    
      <category term="Debounce" scheme="http://jinlong.github.io/tags/Debounce/"/>
    
      <category term="Throttle" scheme="http://jinlong.github.io/tags/Throttle/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[你好 ES2015]]></title>
    <link href="http://jinlong.github.io/2016/04/09/Say-Hello-To-ES2015/"/>
    <id>http://jinlong.github.io/2016/04/09/Say-Hello-To-ES2015/</id>
    <published>2016-04-09T03:22:43.000Z</published>
    <updated>2016-04-09T04:59:20.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文： <a href="https://tutor.mantrajs.com/say-hello-to-ES2015/introduction" target="_blank" rel="external">Say Hello To ES2015</a><br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<h1 id="介绍_ES2015">介绍 ES2015</h1><p>ES2015 是新版的 JavaScript，Node.js 已经完全支持，浏览器端可以用 <a href="https://babeljs.io/" target="_blank" rel="external">Babel</a> 库编译。</p>
<p>运行本文的示例代码，可以用 <a href="https://jsbin.com/defeba/edit?js,console" target="_blank" rel="external">JSBin 环境</a>，也可以结合原文中的测试题检测学习效果。</p>
<h1 id="使用_let_和_const">使用 let 和 const</h1><p>ES2015 可以用 <code>const</code> 或 <code>let</code> 替换 <code>var</code> ，它们定义了块级作用域变量。</p>
<p>示例代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> lc=<span class="number">0</span>; lc &lt; <span class="number">10</span>; lc++) &#123;</div><div class="line">  <span class="keyword">let</span> value = lc;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(value); <span class="comment">//抛出错误</span></div></pre></td></tr></table></figure>
<p>变量 <code>value</code> 只能在 <code>for</code> 循环中使用。</p>
<p><code>const</code> 跟 <code>let</code> 很像，但是它定义的变量值无法改变。</p>
<p>示例代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = &#123;<span class="attr">name</span>: <span class="string">"Arunoda"</span>&#125;;</div><div class="line">user.name = <span class="string">"Susiripala"</span>;</div><div class="line"><span class="built_in">console</span>.log(user.name);</div></pre></td></tr></table></figure>
<p>改变的是变量 <code>user</code> 内部的属性，并没有改变 <code>user</code> 本身。</p>
<p>许多人更喜欢用 <code>const</code> 代替 <code>let</code> 。</p>
<h1 id="使用箭头函数整理你的代码">使用箭头函数整理你的代码</h1><p>熟悉的方式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyBy10 = numbers.map(<span class="function"><span class="keyword">function</span>(<span class="params">a</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> a * <span class="number">10</span>;</div><div class="line">&#125;);</div><div class="line"><span class="built_in">console</span>.log(multiplyBy10);</div></pre></td></tr></table></figure>
<p>使用箭头函数以后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyBy10 = numbers.map(<span class="function"><span class="params">a</span> =&gt;</span> a * <span class="number">10</span>);</div><div class="line"><span class="built_in">console</span>.log(multiplyBy10);</div></pre></td></tr></table></figure>
<p>如果方法接受不止1个参数，可以这么写：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyByIndex = numbers.map(<span class="function">(<span class="params">a, i</span>) =&gt;</span> a * i);</div><div class="line"><span class="built_in">console</span>.log(multiplyByIndex);</div></pre></td></tr></table></figure>
<p>箭头函数返回一个对象的话，需要加圆括号：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyBy10 = numbers.map(<span class="function"><span class="params">a</span> =&gt;</span> (&#123;<span class="attr">res</span>: a * <span class="number">10</span>&#125;));</div><div class="line"><span class="built_in">console</span>.log(multiplyBy10);</div></pre></td></tr></table></figure>
<a id="more"></a>
<h1 id="不再使用“self=this”">不再使用“self=this”</h1><p>以前的代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Clock</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">this</span>.currentTime = <span class="keyword">new</span> <span class="built_in">Date</span>();</div><div class="line">&#125;</div><div class="line"></div><div class="line">Clock.prototype.start = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> self = <span class="keyword">this</span>;</div><div class="line">    setInterval(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">        self.currentTime = <span class="keyword">new</span> <span class="built_in">Date</span>();</div><div class="line">    &#125;, <span class="number">1000</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>可以用箭头函数代替 <code>self=this</code>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Clock</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">this</span>.currentTime = <span class="keyword">new</span> <span class="built_in">Date</span>();</div><div class="line">&#125;</div><div class="line"></div><div class="line">Clock.prototype.start = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</div><div class="line">        <span class="keyword">this</span>.currentTime = <span class="keyword">new</span> <span class="built_in">Date</span>();</div><div class="line">    &#125;, <span class="number">1000</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><code>setInterval</code> 里面用了箭头函数，它携带了 <code>start</code> 方法的上下文<code>（this）</code>。</p>
<p><strong>使用箭头函数要多加小心，并不是随处可用的，箭头函数会携带函数定义时的上下文。</strong></p>
<p>正如这个例子：<a href="https://jsbin.com/zuseqap/edit?js,console" target="_blank" rel="external">https://jsbin.com/zuseqap/edit?js,console</a> </p>
<h1 id="改良的对象字面量">改良的对象字面量</h1><p>在对象里面定义一个方法，可以这么写：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = &#123;</div><div class="line">    getName() &#123;</div><div class="line">        <span class="keyword">return</span> <span class="string">'Arunoda'</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(user.getName());</div></pre></td></tr></table></figure>
<p>不必每次都写 <code>function</code> 关键字。</p>
<p>这是最酷的特性，你会喜欢的：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> name = <span class="string">'Arunoda'</span>;</div><div class="line"><span class="keyword">const</span> age = <span class="number">80</span>;</div><div class="line"></div><div class="line"><span class="keyword">const</span> user = &#123;name, age&#125;;</div></pre></td></tr></table></figure>
<p>瞅瞅多简单，并不用这么啰嗦：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> name = <span class="string">'Arunoda'</span>;</div><div class="line"><span class="keyword">const</span> age = <span class="number">80</span>;</div><div class="line"></div><div class="line"><span class="keyword">const</span> user = &#123;</div><div class="line">    <span class="attr">name</span>: name,</div><div class="line">    <span class="attr">age</span>: age</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<h1 id="解构对象">解构对象</h1><p>很容易地提取 <code>user</code> 对象的 <code>name</code> 和 <code>age</code> 字段：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = &#123;</div><div class="line">    <span class="attr">name</span>: <span class="string">'Arunoda'</span>,</div><div class="line">    <span class="attr">age</span>: <span class="number">80</span>,</div><div class="line">    <span class="attr">city</span>: <span class="string">'Colombo'</span></div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="keyword">const</span> &#123;name, age&#125; = user;</div><div class="line"><span class="built_in">console</span>.log(name, age);</div></pre></td></tr></table></figure>
<p>对于函数相当有用，上代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">printName</span>(<span class="params">&#123;name&#125;</span>) </span>&#123;</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">'Name is: '</span> + name);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">const</span> user = &#123;</div><div class="line">    <span class="attr">name</span>: <span class="string">'Arunoda'</span>,</div><div class="line">    <span class="attr">age</span>: <span class="number">80</span>,</div><div class="line">    <span class="attr">city</span>: <span class="string">'Colombo'</span></div><div class="line">&#125;;</div><div class="line">printName(user);</div></pre></td></tr></table></figure>
<p>不仅简化了代码，而且可以自描述。看到函数第一行时，我们便会明白使用传入对象的哪个字段。</p>
<p>可以定义传入对象的默认值。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">printUser</span>(<span class="params">&#123;name, age = <span class="number">20</span>&#125;</span>) </span>&#123;</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">'Name is: '</span> + name + <span class="string">' Age: '</span> + age);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>像传入对象一样，同样可以从传入的数组中解构值：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">printUser</span>(<span class="params">[name, age = <span class="number">20</span>]</span>) </span>&#123;</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">'Name is: '</span> + name + <span class="string">' Age: '</span> + age);</div><div class="line">&#125;</div><div class="line"></div><div class="line">printUser([<span class="string">"Arunoda"</span>, <span class="number">80</span>]);</div></pre></td></tr></table></figure>
<h1 id="前所未见的方式传递（spread）数组">前所未见的方式传递（spread）数组</h1><p>以前的代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params">a, b</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> a + b;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">sumAndLog</span>(<span class="params">a, b</span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> result = sum(a, b);</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">'Result is: '</span> + result);</div><div class="line">    <span class="keyword">return</span> result;</div><div class="line">&#125;</div><div class="line"></div><div class="line">sumAndLog(<span class="number">10</span>, <span class="number">20</span>);</div></pre></td></tr></table></figure>
<p>ES2015 代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params">a, b</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> a + b;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">sumAndLog</span>(<span class="params">...args</span>) </span>&#123;</div><div class="line">    <span class="keyword">const</span> result = sum(...args);</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">'Result is: '</span> + result);</div><div class="line">    <span class="keyword">return</span> result;</div><div class="line">&#125;</div><div class="line"></div><div class="line">sumAndLog(<span class="number">10</span>, <span class="number">20</span>);</div></pre></td></tr></table></figure>
<p>在 <code>sumAndLog</code> 方法中使用 <code>spread</code> 操作符（<code>...</code>），可以很简单地把所有参数存入 <code>args</code> 变量，然后再用 <code>spread</code> 操作符把 <code>args</code> 传入 <code>sum</code> 方法。</p>
<p>再看以下例子：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">printTeam</span>(<span class="params">leader, ...others</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Leader: '</span> + leader + <span class="string">' - Others: '</span> + others);</div><div class="line">&#125;</div><div class="line"></div><div class="line">printTeam(<span class="string">'Arunoda'</span>, <span class="string">'John'</span>, <span class="string">'Singh'</span>);</div><div class="line"><span class="comment">//输出结果："Leader: Arunoda - Others: John,Singh"</span></div></pre></td></tr></table></figure>
<h1 id="克隆、合并对象">克隆、合并对象</h1><p>以往都是用 <a href="http://underscorejs.org/" target="_blank" rel="external">underscore</a> 或者 <a href="https://lodash.com/" target="_blank" rel="external">lodash</a>，克隆、合并对象：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> user = &#123;<span class="attr">name</span>: <span class="string">"Arunoda"</span>&#125;;</div><div class="line"><span class="keyword">var</span> newUser = _.clone(user);</div><div class="line"><span class="keyword">var</span> withAge = _.extend(user, &#123;<span class="attr">age</span>: <span class="number">20</span>&#125;);</div><div class="line"><span class="keyword">var</span> newUserVersion = _.defaults(&#123;<span class="attr">age</span>: <span class="number">80</span>&#125;, user);</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(newUser, withAge, newUserVersion);</div></pre></td></tr></table></figure>
<p>ES2015 不需要任何工具库，轻松实现以上功能。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = &#123;<span class="attr">name</span>: <span class="string">"Arunoda"</span>&#125;;</div><div class="line"><span class="keyword">const</span> newUser = &#123;...user&#125;;</div><div class="line"><span class="keyword">const</span> withAge = &#123;...user, <span class="attr">age</span>: <span class="number">20</span>&#125;;</div><div class="line"><span class="keyword">const</span> newUserVersion = &#123;<span class="attr">age</span>: <span class="number">80</span>, ...user&#125;;</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(newUser, withAge, newUserVersion);</div></pre></td></tr></table></figure>
<p>看以下例子：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = &#123;</div><div class="line">  <span class="attr">name</span>: <span class="string">'Arunoda'</span>,</div><div class="line">  <span class="attr">emails</span>: [<span class="string">'hello@arunoda.io'</span>]</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="keyword">const</span> newUser = &#123;...user&#125;;</div><div class="line">newUser.emails.push(<span class="string">'mail@arunoda.io'</span>);</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(user.emails);</div><div class="line"><span class="comment">//输出结果：["hello@arunoda.io", "mail@arunoda.io"]</span></div></pre></td></tr></table></figure>
<p>尽管我们克隆了对象，但不是深度克隆，只克隆了顶层字段，emails 数组字段使用的仍是同一个。</p>
<p><strong>往数组里添加元素</strong></p>
<p>跟对象类似，我们同样可以克隆数组：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> marks = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>];</div><div class="line"><span class="keyword">const</span> newMarks = [...marks, <span class="number">40</span>];</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(marks, newMarks);</div></pre></td></tr></table></figure>
<h1 id="JavaScript_不变性（Immutability）">JavaScript 不变性（Immutability）</h1><p>这些日子，JavaScript 也兴起函数式编程的概念。因此，我们可以尝试写写纯函数。</p>
<blockquote>
<p>纯函数：一个函数接收一些值，并且返回一些值，但是通过参数接收到的值不会被改变。 同样的输入总是返回同样的值。<br>random() 就不是一个纯函数，任何可以修改全局状态的函数都不能称之为纯。</p>
</blockquote>
<p>用 <code>spread</code> 操作符可以轻松实现。</p>
<p>用于对象：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">addMarks</span>(<span class="params">user, marks</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> &#123;</div><div class="line">    ...user,</div><div class="line">    marks</div><div class="line">  &#125;;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">const</span> user = &#123;<span class="attr">username</span>: <span class="string">'arunoda'</span>&#125;;</div><div class="line"><span class="keyword">const</span> userWithMarks = addMarks(user, <span class="number">80</span>);</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(user, userWithMarks);</div></pre></td></tr></table></figure>
<p>用于数组：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">addUser</span>(<span class="params">users, username</span>) </span>&#123;</div><div class="line">  <span class="keyword">const</span> user = &#123;username&#125;;</div><div class="line">  <span class="keyword">return</span> [</div><div class="line">    ...users,</div><div class="line">    user</div><div class="line">  ];</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">const</span> user = &#123;<span class="attr">username</span>: <span class="string">'arunoda'</span>&#125;;</div><div class="line"><span class="keyword">const</span> users = [user];</div><div class="line"><span class="keyword">const</span> newUsers = addUser(users, <span class="string">'john'</span>);</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(users, newUsers);</div></pre></td></tr></table></figure>
<h1 id="以_Python_方式合并字符串">以 Python 方式合并字符串</h1><p>合并字符串通常很烦，可以用 + 操作符，或者类似 <code>underscore</code> 的模板。</p>
<p>ES2015 的模板字符串，非常简单，看例子：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> name = <span class="string">"Arunoda"</span>;</div><div class="line"><span class="keyword">const</span> welcome = <span class="string">`Hello <span class="subst">$&#123;name&#125;</span>, Good Morning!`</span>;</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(welcome);</div></pre></td></tr></table></figure>
<p>注意 <strong>“`”</strong> 的使用。</p>
<h2 id="多行字符串">多行字符串</h2><p>既然支持模板字符串，多行字符串也不在话下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> message = <span class="string">`</span></div><div class="line">  # Title</div><div class="line"></div><div class="line">  This is a multi line string as markdown.</div><div class="line">  It's pretty nice.</div><div class="line">`;</div><div class="line"><span class="built_in">console</span>.log(message);</div></pre></td></tr></table></figure>
<p>没有模板字符串的话，是这个样子的：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> message = <span class="string">"\n  # Title\n\n  This is a multi line string as markdown.\n  It's pretty nice.\n"</span>;</div><div class="line"><span class="built_in">console</span>.log(message);</div></pre></td></tr></table></figure>
<h1 id="像_Java_一样写_Class">像 Java 一样写 Class</h1><p>JavaScript 并不是真正的面向对象语言，但是可以用函数和原型模拟类。ES2015 可以写真正原生的类了。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Vehicle</span> </span>&#123;</div><div class="line">  <span class="keyword">constructor</span>(type, number) &#123;</div><div class="line">    <span class="keyword">this</span>.type = type;</div><div class="line">    <span class="keyword">this</span>.number = number;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  display() &#123;</div><div class="line">    <span class="keyword">return</span> <span class="string">`Number: <span class="subst">$&#123;<span class="keyword">this</span>.number&#125;</span>`</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">const</span> v1 = <span class="keyword">new</span> Vehicle(<span class="string">'Car'</span>, <span class="string">'GH-2343'</span>);</div><div class="line"><span class="built_in">console</span>.log(v1.display());</div></pre></td></tr></table></figure>
<p>继承一个类：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Vehicle</span> </span>&#123;</div><div class="line">  <span class="keyword">constructor</span>(type, number) &#123;</div><div class="line">    <span class="keyword">this</span>.type = type;</div><div class="line">    <span class="keyword">this</span>.number = number;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  display() &#123;</div><div class="line">    <span class="keyword">return</span> <span class="string">`Number: <span class="subst">$&#123;<span class="keyword">this</span>.number&#125;</span>`</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Car</span> <span class="keyword">extends</span> <span class="title">Vehicle</span> </span>&#123;</div><div class="line">  <span class="keyword">constructor</span>(number) &#123;</div><div class="line">    <span class="keyword">super</span>(<span class="string">'Car'</span>, number);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  display() &#123;</div><div class="line">    <span class="keyword">const</span> value = <span class="keyword">super</span>.display();</div><div class="line">    <span class="keyword">return</span> <span class="string">`Car <span class="subst">$&#123;value&#125;</span>`</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">const</span> v1 = <span class="keyword">new</span> Car(<span class="string">'GH-2343'</span>);</div><div class="line"><span class="built_in">console</span>.log(v1.display());</div></pre></td></tr></table></figure>
<p>小汽车继承了车辆：</p>
<ul>
<li>在 Car constructor 内部调用了 <code>super constructor</code> （Vehicle 的 constructor）。</li>
<li>Car 的 display() 方法内部，调用了 <code>super.display()</code> 。这里展示了子类如何继承方法。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Vehicle</span> </span>&#123;</div><div class="line">  <span class="keyword">constructor</span>(type, number) &#123;</div><div class="line">    <span class="keyword">this</span>.type = type;</div><div class="line">    <span class="keyword">this</span>.number = number;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  display() &#123;</div><div class="line">    <span class="keyword">return</span> <span class="string">`Number: <span class="subst">$&#123;<span class="keyword">this</span>.number&#125;</span>`</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Car</span> <span class="keyword">extends</span> <span class="title">Vehicle</span> </span>&#123;</div><div class="line">  display() &#123;</div><div class="line">    <span class="keyword">const</span> value = <span class="keyword">super</span>.display();</div><div class="line">    <span class="keyword">return</span> <span class="string">`Car <span class="subst">$&#123;value&#125;</span>`</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">const</span> v1 = <span class="keyword">new</span> Car(<span class="string">'GH-2343'</span>);</div><div class="line"><span class="built_in">console</span>.log(v1.display());</div></pre></td></tr></table></figure>
<p>Car 类没有实现 constructor 的话，它会用 Vehicle 的 constructor 。</p>
<h1 id="ES2015_模块系统">ES2015 模块系统</h1><p>ES2015 的模块系统很像 CommonJS 模块系统（或者 Node.js 的模块系统），但是有一点主要的区别：</p>
<blockquote>
<p>所有的模块导入应该是静态的，无法在运行时导入模块。编译时间应该完成导入（或者最好在解释 JavaScript 期间完成）。<br>以下代码在 ES2015 模块里无法使用：</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> router;</div><div class="line"></div><div class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="built_in">window</span> === <span class="string">'function'</span>) &#123;</div><div class="line">  router = <span class="keyword">import</span> <span class="string">'./client-router'</span>;</div><div class="line">&#125; <span class="keyword">else</span> &#123;</div><div class="line">  router = <span class="keyword">import</span> <span class="string">'./server-router'</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="命名导出函数">命名导出函数</h2><p>定义一个简单的导出函数 <code>sum</code> ：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params">a, b</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> a + b;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>然后导入它：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> &#123;sum&#125; <span class="keyword">from</span> <span class="string">'./lib/math'</span>;</div><div class="line"></div><div class="line"><span class="keyword">const</span> total = sum(<span class="number">10</span>, <span class="number">20</span>);</div><div class="line"><span class="built_in">console</span>.log(total);</div></pre></td></tr></table></figure>
<p>导入多个函数：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> &#123;sum, multiply&#125;</div></pre></td></tr></table></figure>
<p>像函数一样，可以导出任何类型的变量，包括类。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">const</span> BASE = <span class="number">10</span>;</div><div class="line"><span class="keyword">export</span> <span class="keyword">let</span> name = <span class="string">'Arunoda'</span>;</div><div class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">Vehicle</span> </span>&#123;&#125;;</div></pre></td></tr></table></figure>
<h2 id="默认导出">默认导出</h2><p>有时需要导出一个独立的模块，叫做默认导出。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> </span>&#123;</div><div class="line">  <span class="keyword">constructor</span>(type, number) &#123;</div><div class="line">    <span class="keyword">this</span>.type = type;</div><div class="line">    <span class="keyword">this</span>.number = number;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  display() &#123;</div><div class="line">    <span class="keyword">return</span> <span class="string">`Number: <span class="subst">$&#123;<span class="keyword">this</span>.number&#125;</span>`</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>可以这么导入：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> Vehicle <span class="keyword">from</span> <span class="string">'./lib/vehicle'</span>;</div><div class="line"></div><div class="line"><span class="keyword">const</span> v1 = <span class="keyword">new</span> Vehicle(<span class="string">'Car'</span>, <span class="string">'GH-3355'</span>);</div><div class="line"><span class="built_in">console</span>.log(v1.display());</div></pre></td></tr></table></figure>
<p>如果再导出一个 <code>print</code> 函数，这么写：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> Vehicle, &#123;print&#125; <span class="keyword">from</span> <span class="string">'./lib/vehicle'</span>;</div></pre></td></tr></table></figure>
<p>这便是我们如何在同一模块导入 “默认导出” 和 “命名导出” 的方式。</p>
<p>重命名导入，一次导入所有命名导出，以及更多知识，参见 <a href="https://developer.mozilla.org/en/docs/web/javascript/reference/statements/import" target="_blank" rel="external">MDN 文档</a>。</p>
<h1 id="在实际的项目中使用_ES2015？">在实际的项目中使用 ES2015？</h1><p>至今还没有浏览器完全实现 ES2015 的所有规范。因此，无法直接在浏览器里使用 ES2015 。</p>
<p>那么我们该怎么做？</p>
<p>欢迎来到 transpiling 的世界。</p>
<p>现在可以按 ES2015 写代码，然后使用一个工具把它转换成 ES5，最有名的一个 transpiler 便是 <a href="https://babeljs.io/" target="_blank" rel="external">Babel</a>。</p>
<p>设置 Babel 并没那么简单，需要一定的经验，这是一些新手包，拿去用吧。</p>
<ul>
<li>在 Node.js 中使用 <a href="https://github.com/kadirahq/node-base" target="_blank" rel="external">node-base</a> </li>
<li>在 NPM 模块中使用 <a href="https://github.com/kadirahq/npm-base" target="_blank" rel="external">npm-base</a> </li>
<li>在 React app 中使用 <a href="https://github.com/gaearon/react-hot-boilerplate" target="_blank" rel="external">react-hot-boilerplate</a></li>
</ul>
<blockquote>
<p>此外，可以使用 <a href="https://www.meteor.com/" target="_blank" rel="external">Meteor</a>，默认支持 ES2015 了。</p>
</blockquote>
<h1 id="深入研究_ES2015">深入研究 ES2015</h1><p>深入研究，可以参考以下链接：</p>
<ul>
<li><a href="https://babeljs.io/docs/learn-es2015/" target="_blank" rel="external">Babel’s Learn ES2015 Guide</a></li>
<li><a href="https://github.com/ericdouglas/ES6-Learning" target="_blank" rel="external">Massive List of ES2015 Learning Resources</a></li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文： <a href="https://tutor.mantrajs.com/say-hello-to-ES2015/introduction">Say Hello To ES2015</a><br>笔记：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
</blockquote>
<h1 id="介绍_ES2015">介绍 ES2015</h1><p>ES2015 是新版的 JavaScript，Node.js 已经完全支持，浏览器端可以用 <a href="https://babeljs.io/">Babel</a> 库编译。</p>
<p>运行本文的示例代码，可以用 <a href="https://jsbin.com/defeba/edit?js,console">JSBin 环境</a>，也可以结合原文中的测试题检测学习效果。</p>
<h1 id="使用_let_和_const">使用 let 和 const</h1><p>ES2015 可以用 <code>const</code> 或 <code>let</code> 替换 <code>var</code> ，它们定义了块级作用域变量。</p>
<p>示例代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> lc=<span class="number">0</span>; lc &lt; <span class="number">10</span>; lc++) &#123;</div><div class="line">  <span class="keyword">let</span> value = lc;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(value); <span class="comment">//抛出错误</span></div></pre></td></tr></table></figure>
<p>变量 <code>value</code> 只能在 <code>for</code> 循环中使用。</p>
<p><code>const</code> 跟 <code>let</code> 很像，但是它定义的变量值无法改变。</p>
<p>示例代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = &#123;<span class="attr">name</span>: <span class="string">"Arunoda"</span>&#125;;</div><div class="line">user.name = <span class="string">"Susiripala"</span>;</div><div class="line"><span class="built_in">console</span>.log(user.name);</div></pre></td></tr></table></figure>
<p>改变的是变量 <code>user</code> 内部的属性，并没有改变 <code>user</code> 本身。</p>
<p>许多人更喜欢用 <code>const</code> 代替 <code>let</code> 。</p>
<h1 id="使用箭头函数整理你的代码">使用箭头函数整理你的代码</h1><p>熟悉的方式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyBy10 = numbers.map(<span class="function"><span class="keyword">function</span>(<span class="params">a</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> a * <span class="number">10</span>;</div><div class="line">&#125;);</div><div class="line"><span class="built_in">console</span>.log(multiplyBy10);</div></pre></td></tr></table></figure>
<p>使用箭头函数以后：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyBy10 = numbers.map(<span class="function"><span class="params">a</span> =&gt;</span> a * <span class="number">10</span>);</div><div class="line"><span class="built_in">console</span>.log(multiplyBy10);</div></pre></td></tr></table></figure>
<p>如果方法接受不止1个参数，可以这么写：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyByIndex = numbers.map(<span class="function">(<span class="params">a, i</span>) =&gt;</span> a * i);</div><div class="line"><span class="built_in">console</span>.log(multiplyByIndex);</div></pre></td></tr></table></figure>
<p>箭头函数返回一个对象的话，需要加圆括号：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> numbers = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>, <span class="number">50</span>];</div><div class="line"><span class="keyword">const</span> multiplyBy10 = numbers.map(<span class="function"><span class="params">a</span> =&gt;</span> (&#123;<span class="attr">res</span>: a * <span class="number">10</span>&#125;));</div><div class="line"><span class="built_in">console</span>.log(multiplyBy10);</div></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/tags/JavaScript/"/>
    
      <category term="ES6" scheme="http://jinlong.github.io/tags/ES6/"/>
    
      <category term="ES2015" scheme="http://jinlong.github.io/tags/ES2015/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[flex-grow 不易理解，难道不是吗?]]></title>
    <link href="http://jinlong.github.io/2016/02/04/flex-grow-is-weird/"/>
    <id>http://jinlong.github.io/2016/02/04/flex-grow-is-weird/</id>
    <published>2016-02-04T14:40:39.000Z</published>
    <updated>2016-02-04T15:22:12.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="https://css-tricks.com/flex-grow-is-weird/" target="_blank" rel="external">‘flex-grow’ is weird. Or is it?</a><br>翻译：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<p>当我刚接触 <code>flex-grow</code> 时，为了探寻它的工作原理，做了一个<a href="http://codepen.io/matuzo/pen/zvXQyj" target="_blank" rel="external">简单的例子</a>。</p>
<p>我以为理解的挺透彻了，但是当我把它应用到同事的网站上时，效果跟我想象的完全不同。无论怎么改，布局都无法像我的demo那样展示。这时我才意识到，我并没有完全掌握 <code>flex-grow</code> 。</p>
<h1 id="flex-grow_为何不正常">flex-grow 为何不正常</h1><p>在我深入剖析 <code>flex-grow</code> 的功能之前，我想解释一下我起初犯了什么错。</p>
<p>我认为所有 flex 元素的 <code>flex-grow</code> 如果设置为 <code>1</code> ，它们将一样宽。如果某一项的 <code>flex-grow</code> 设置为 <code>2</code> ，它将是其它元素的二倍宽。</p>
<p>一切听起来顺理成章。我<a href="http://codepen.io/matuzo/pen/zvXQyj" target="_blank" rel="external">上面的例子</a> 貌似也印证了这点。父元素是900px宽，<code>flex-grow: 2</code> 的 section 元素计算后是600px宽，<code>flex-grow: 1</code> 的 aside 元素计算后是300px宽。</p>
<p>如你所见，它在这个例子中展现的近乎完美，可是在真实的例子中却不尽人意，即使我们用了完全相同的 CSS。事实证明，问题不在 CSS，而在于内容（或者说缺乏内容）。我的测试用例只用了两个空元素，无法展示这个属性最重要的细节。</p>
<h1 id="flex-grow_到底如何工作">flex-grow 到底如何工作</h1><p>啰嗦了半天，我终于要解释 <code>flex-grow</code> 没有尽如人意的原因了。</p>
<p>为了阐明原因，我又搞了个<a href="http://codepen.io/matuzo/pen/ZQEWjg" target="_blank" rel="external">栗子</a> ，所有的设置跟<a href="http://codepen.io/matuzo/pen/zvXQyj" target="_blank" rel="external">第一个栗子</a> 完全一致，只不过 section 和 aside 元素不再是空的。看吧，两个元素的比例不再是 2 : 1，flex-grow 为 1 的元素的确比 flex-grow 为 2 的元素宽不少呐。</p>
<a id="more"></a>
<h2 id="解析">解析</h2><p>如果给父元素设置了 <code>display: flex;</code> ，子元素仅仅是水平排列，没有其它效果了。如果没有足够的空间，它们会收缩一些尺寸。另一方面，如果有足够的空间，它们也不会扩展，因为 Flexbox 希望我们自己定义扩展多少。<strong><em>flex-grow</em></strong> 恰恰用来定义剩余空间如何分配，每一项分享多大宽度。</p>
<p>换言之：</p>
<blockquote>
<p>flex 容器为它的子元素分配剩余空间（它们的扩展系数是成比例的），从而填满整个容器，或者收缩元素（它们的收缩系数也是成比例的），从而阻止溢出。</p>
</blockquote>
<p><a href="https://drafts.csswg.org/css-flexbox/#flexibility" target="_blank" rel="external">https://drafts.csswg.org/css-flexbox/#flexibility</a></p>
<h2 id="证明">证明</h2><p>如果我们可视化一把，概念就很清晰明了了。</p>
<p>首先，我们给父元素设置了 <code>display: flex</code>，然后它的子元素成了 flex 元素，一个挨一个的横向排列。 </p>
<p><img src="/image/flex-grow-weird/pic1.png" alt="附图1"></p>
<p>下一步，我们要决定每个元素能获得多少剩余空间。先前的例子中，第一个元素获得了 2/3 的剩余空间（<code>flex-grow: 2</code>），第二个元素获得了 1/3 的剩余空间（<code>flex-grow: 1</code>）。想知道 flex-grow 总共的值是多少，看看剩余空间被分成了几份吧。</p>
<p><img src="/image/flex-grow-weird/pic2.png" alt="附图2"></p>
<p>最终我们得出了可分配的块数，根据 <code>flex-grow</code> 的值，每个元素可以获得适当的块数。</p>
<p><img src="/image/flex-grow-weird/pic3.png" alt="附图3"></p>
<h2 id="计算结果">计算结果</h2><p>理论和视觉展示都不错，让我们为<a href="http://codepen.io/matuzo/pen/ZQEWjg" target="_blank" rel="external">上面例子</a> 做点算术吧。</p>
<p>我们需要4组数字：父容器宽度， section 和 aside 元素的初始宽度，以及 flex-grow 的总值。</p>
<p>父容器宽：<strong>900px</strong><br>section 宽：<strong>99px</strong><br>aside 宽：<strong>623px</strong><br>flex-grow 总值：<strong>3</strong></p>
<p><strong>1. 首先计算剩余空间</strong></p>
<p>父容器宽减去每一个子元素的初始宽度。</p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">900 </span>- <span class="number">99</span> - <span class="number">623</span> = <span class="number">178</span></div></pre></td></tr></table></figure>
<p>父容器宽 - section 宽 - aside 宽 = 剩余空间</p>
<p><strong>2. 然后计算 flex-grow 的1份是多宽</strong></p>
<p>既然有了剩余空间，我们还需要确定把它切成几份。重要的是，我们不按元素的个数切分剩余空间，而是按 flex-grow 总值，所以这里是 3 （<code>flex-grow: 2</code> + <code>flex-grow: 1</code>）</p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">178 </span>/ <span class="number">3</span> = <span class="number">59.33</span></div></pre></td></tr></table></figure>
<p>剩余空间 / flex-grow 总值 = “1份 flex-grow 的宽”</p>
<p><strong>3. 最终所有的元素瓜分剩余空间</strong></p>
<p>依据它们的 flex-grow 值，section 需要 2 份（2 <em> 59.33），aside 需要 1 份（1</em> 59.33）。这个数字再与每个元素的初始宽度相加。</p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">99 </span>+ (<span class="number">2</span> * <span class="number">59.33</span>) = <span class="number">217.66</span> (≈<span class="number">218</span>px)</div></pre></td></tr></table></figure>
<p>初始 section 宽度 + （section 的 flex-grow 值 * “1 份 flex-grow 的宽”） = 新的宽度</p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">623 </span>+ (<span class="number">1</span> * <span class="number">59.33</span>) = <span class="number">682.33</span> (≈<span class="number">682</span>px)</div></pre></td></tr></table></figure>
<p>初始 aside 宽度 + （aside 的 flex-grow 值 * “1 份 flex-grow 的宽”） = 新的宽度</p>
<p>so easy，不是吗？</p>
<h1 id="好吧，那为什么第一个例子正常呢？">好吧，那为什么第一个例子正常呢？</h1><p>我们按已有的公式，算下<a href="http://codepen.io/matuzo/pen/zvXQyj" target="_blank" rel="external">第一个例子</a> 。</p>
<p>父容器宽：<strong>900px</strong><br>section 宽：<strong>0px</strong><br>aside 宽：<strong>0px</strong><br>flex-grow 总值：<strong>3</strong></p>
<p><strong>1. 计算剩余空间</strong></p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">900 </span>- <span class="number">0</span> - <span class="number">0</span> = <span class="number">900</span></div></pre></td></tr></table></figure>
<p><strong>2. 计算 flex-grow 的1份是多宽</strong></p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">900 </span>/ <span class="number">3</span> = <span class="number">300</span></div></pre></td></tr></table></figure>
<p><strong>3. 分配剩余空间</strong></p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">0 </span>+ (<span class="number">2</span> * <span class="number">300</span>) = <span class="number">600</span>  </div><div class="line"><span class="symbol">0 </span>+ (<span class="number">1</span> * <span class="number">300</span>) = <span class="number">300</span></div></pre></td></tr></table></figure>
<p>如果每个元素的宽是 0，剩余空间等于父容器的宽度，因此，看起来像是 <code>flex-grow</code> 按比例划分了父容器的宽度。</p>
<h1 id="flex-grow_和_flex-basis">flex-grow 和 flex-basis</h1><p>快速回顾一下：剩余空间被 <code>flex-grow</code> 的总值划分，由此产生的商，乘以各自的 <code>flex-grow</code> 值，结果再加上每个元素的初始宽度。</p>
<p>但是如果没有剩余空间或者不想依赖元素的初始宽度，我们可以设置它吗？还能用 <code>flex-grow</code> 吗？</p>
<p>当然可以。有个 <code>flex-basis</code> 属性，它可以定义元素的初始宽度。如果 <code>flex-basis</code> 和 <code>flex-grow</code> 一起设置，宽度的计算方式得变一下。</p>
<blockquote>
<p><code>&lt;‘flex-basis’&gt;</code> ：按 flex 因子分配剩余空间之前，每个 flex 元素的最初主要尺寸。</p>
</blockquote>
<p><a href="https://drafts.csswg.org/css-flexbox/#valdef-flex-flex-basis" target="_blank" rel="external">https://drafts.csswg.org/css-flexbox/#valdef-flex-flex-basis</a> </p>
<p>如果给某个元素设置了 <code>flex-basis</code> 属性，我们计算的时候就不能再用元素本身的初始宽度了，而要用 <code>flex-basis</code> 属性的值。</p>
<p>我调整了一下<a href="http://codepen.io/matuzo/pen/GoRqOP" target="_blank" rel="external">先前的例子</a> ，给每个元素加了 <code>flex-basis</code> 属性。</p>
<p>父容器宽：<strong>900px</strong><br>section 宽：<strong>400px</strong>（flex-basis 值）<br>aside 宽：<strong>200px</strong>（flex-basis 值）<br>flex-grow 总值：<strong>3</strong></p>
<p><strong>1. 计算剩余空间</strong></p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">900 </span>- <span class="number">400</span> - <span class="number">200</span> = <span class="number">300</span></div></pre></td></tr></table></figure>
<p><strong>2. 计算 flex-grow 的1份是多宽</strong></p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">300 </span>/ <span class="number">3</span> = <span class="number">100</span></div></pre></td></tr></table></figure>
<p><strong>3. 分配剩余空间</strong></p>
<figure class="highlight basic"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">400 </span>+ (<span class="number">2</span> * <span class="number">100</span>) = <span class="number">600</span>  </div><div class="line"><span class="symbol">200 </span>+ (<span class="number">1</span> * <span class="number">100</span>) = <span class="number">300</span></div></pre></td></tr></table></figure>
<p>仅仅是为了完整性，我才用到 px 值，用<a href="http://codepen.io/matuzo/pen/pgobag" target="_blank" rel="external">百分比的话当然也没问题</a> 。</p>
<h1 id="与盒模型结合">与盒模型结合</h1><p>为了覆盖所有情况，如果我们加上 padding 和 margin 看看会发生什么，<a href="http://codepen.io/matuzo/pen/Qybgee" target="_blank" rel="external">啥也没发生</a>  ，第一步计算的时候，只要减去 margin 就好了。</p>
<p>唯一需要注意的是，使用 <code>box-sizing</code> 的话， <code>flex-basis</code> 跟 <code>width</code> 属性表现相似。<a href="http://codepen.io/matuzo/pen/wMaeVO" target="_blank" rel="external">如果 box-sizing 属性改变</a>，计算结果也会变化。如果 <code>box-sizing</code> 设置为 <code>border-box</code> ，计算时只需用到 <code>flex-basis</code> 和 <code>margin</code> 值，因为 <code>padding</code> 已经包含到宽度里面了。</p>
<h1 id="更多实用的例子">更多实用的例子</h1><p>好吧，算术是玩够了。我再展示一些项目中合理使用 <code>flex-grow</code> 的例子吧。</p>
<h2 id="不限宽度：[_x_]%">不限宽度：[ x ]%</h2><p>实际应用中，剩余空间是自动分配的，如果想让子元素填满父容器的话，我们没必要再考虑宽度值。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="QyWEBb" data-default-tab="result" data-user="matuzo" class="codepen">See the Pen <a href="http://codepen.io/matuzo/pen/QyWEBb/" target="_blank" rel="external">flex-grow</a> by Manuel Matuzovic (<a href="http://codepen.io/matuzo" target="_blank" rel="external">@matuzo</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>

<h2 id="固定宽度的_“圣杯”_3列流式布局">固定宽度的 “圣杯” 3列流式布局</h2><p>固定加自适应宽度的混合布局，可以用浮动实现，但是既不简单直观，又不灵活。Flexbox 实现的话，加点儿 flex-grow 和 flex-basis 魔法，简直小菜一碟。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="jbRjMG" data-default-tab="result" data-user="matuzo" class="codepen">See the Pen <a href="http://codepen.io/matuzo/pen/jbRjMG/" target="_blank" rel="external">Layout using fluid and fixed widths</a> by Manuel Matuzovic (<a href="http://codepen.io/matuzo" target="_blank" rel="external">@matuzo</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>

<h2 id="使用任何元素填满剩余空间">使用任何元素填满剩余空间</h2><p>比如，一个 label 元素后面紧跟着输入框，想让输入框填满剩余空间，不再需要丑陋的 hacks 。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="eJYdWV" data-default-tab="result" data-user="matuzo" class="codepen">See the Pen <a href="http://codepen.io/matuzo/pen/eJYdWV/" target="_blank" rel="external">Filling up the remaining space in a form</a> by Manuel Matuzovic (<a href="http://codepen.io/matuzo" target="_blank" rel="external">@matuzo</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>Philip Waltons 的 <a href="https://philipwalton.github.io/solved-by-flexbox/" target="_blank" rel="external">Solved by Flexbox</a> 一文可以找到更多示例。</p>
<h1 id="听听标准规范怎么说">听听标准规范怎么说</h1><p>根据标准，使用 <code>flex</code> 的简写，比直接用 <code>flex-grow</code> 更好。</p>
<blockquote>
<p>Authors are encouraged to control flexibility using the flex shorthand rather than flex-grow directly, as the shorthand correctly resets any unspecified components to accommodate common uses.</p>
</blockquote>
<p><a href="https://drafts.csswg.org/css-flexbox/#flex-grow-property" target="_blank" rel="external">https://drafts.csswg.org/css-flexbox/#flex-grow-property</a> </p>
<p>但是小心！如果仅仅使用 <code>flex: 1;</code> ，以上某些例子可能无法正常展示。</p>
<p>我们的例子要用 <code>flex</code> 的话，应该这么定义：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">flex: 2 1 auto;  /* (<span class="tag">&lt;<span class="name">flex-grow</span>&gt;</span> | <span class="tag">&lt;<span class="name">flex-shrink</span>&gt;</span> | <span class="tag">&lt;<span class="name">flex-basis</span>&gt;</span>) */</div></pre></td></tr></table></figure>
<h1 id="深入学习_Flexbox">深入学习 Flexbox</h1><p>如果你想深入学习 Flexbox，可以看看这些不错的资源：</p>
<ul>
<li><a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/" target="_blank" rel="external">A Complete Guide to Flexbox</a> by Chris Coyier</li>
<li><a href="https://chriswrightdesign.com/experiments/flexbox-adventures/" target="_blank" rel="external">Flexbox adventures</a> by Chris Wright</li>
<li><a href="http://flexboxfroggy.com/" target="_blank" rel="external">Flexbox Froggy</a> by Thomas Park</li>
<li><a href="http://flexbox.io/" target="_blank" rel="external">What the Flexbox?</a> by Wes Bos</li>
<li><a href="https://cvan.io/flexboxin5/" target="_blank" rel="external">flexboxin5</a> </li>
<li><a href="http://madebymike.com.au/demos/flexbox-tester/" target="_blank" rel="external">Flexbox Tester</a> by Mike Riethmuller</li>
</ul>
<h1 id="总结经验教训">总结经验教训</h1><p><code>flex-grow</code> 不易理解吗？也不全是。我们只需理解它如何工作，它做了什么。如果一个元素设置 <code>flex-grow</code> 为 <code>3</code> ，并不代表它是 <code>flex-grow</code> 为 <code>1</code> 的元素的3倍大，准确含义是：它的初始宽度可以增加的像素值是另一个元素的3倍。</p>
<p>我通过<a href="http://codepen.io/matuzo/pen/zvXQyj" target="_blank" rel="external">两个空元素</a> 测试 flex-grow ，得到的结论跟<a href="http://codepen.io/matuzo/pen/ZQEWjg" target="_blank" rel="external">真实情况</a> 完全不符。应该在尽可能真实的环境中验证新事物，这样才能得到最切合实际的结论。</p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="https://css-tricks.com/flex-grow-is-weird/">‘flex-grow’ is weird. Or is it?</a><br>翻译：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
</blockquote>
<p>当我刚接触 <code>flex-grow</code> 时，为了探寻它的工作原理，做了一个<a href="http://codepen.io/matuzo/pen/zvXQyj">简单的例子</a>。</p>
<p>我以为理解的挺透彻了，但是当我把它应用到同事的网站上时，效果跟我想象的完全不同。无论怎么改，布局都无法像我的demo那样展示。这时我才意识到，我并没有完全掌握 <code>flex-grow</code> 。</p>
<h1 id="flex-grow_为何不正常">flex-grow 为何不正常</h1><p>在我深入剖析 <code>flex-grow</code> 的功能之前，我想解释一下我起初犯了什么错。</p>
<p>我认为所有 flex 元素的 <code>flex-grow</code> 如果设置为 <code>1</code> ，它们将一样宽。如果某一项的 <code>flex-grow</code> 设置为 <code>2</code> ，它将是其它元素的二倍宽。</p>
<p>一切听起来顺理成章。我<a href="http://codepen.io/matuzo/pen/zvXQyj">上面的例子</a> 貌似也印证了这点。父元素是900px宽，<code>flex-grow: 2</code> 的 section 元素计算后是600px宽，<code>flex-grow: 1</code> 的 aside 元素计算后是300px宽。</p>
<p>如你所见，它在这个例子中展现的近乎完美，可是在真实的例子中却不尽人意，即使我们用了完全相同的 CSS。事实证明，问题不在 CSS，而在于内容（或者说缺乏内容）。我的测试用例只用了两个空元素，无法展示这个属性最重要的细节。</p>
<h1 id="flex-grow_到底如何工作">flex-grow 到底如何工作</h1><p>啰嗦了半天，我终于要解释 <code>flex-grow</code> 没有尽如人意的原因了。</p>
<p>为了阐明原因，我又搞了个<a href="http://codepen.io/matuzo/pen/ZQEWjg">栗子</a> ，所有的设置跟<a href="http://codepen.io/matuzo/pen/zvXQyj">第一个栗子</a> 完全一致，只不过 section 和 aside 元素不再是空的。看吧，两个元素的比例不再是 2 : 1，flex-grow 为 1 的元素的确比 flex-grow 为 2 的元素宽不少呐。</p>]]>
    
    </summary>
    
      <category term="flexbox" scheme="http://jinlong.github.io/tags/flexbox/"/>
    
      <category term="CSS" scheme="http://jinlong.github.io/categories/CSS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[CSS分层动画可以让元素沿弧形路径运动]]></title>
    <link href="http://jinlong.github.io/2016/01/14/moving-along-a-curved-path-in-css-with-layered-animation/"/>
    <id>http://jinlong.github.io/2016/01/14/moving-along-a-curved-path-in-css-with-layered-animation/</id>
    <published>2016-01-14T14:48:56.000Z</published>
    <updated>2016-02-04T14:42:48.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="http://tobiasahlin.com/blog/curved-path-animations-in-css/" target="_blank" rel="external">Moving along a curved path in CSS with layered animation</a><br>翻译：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
<p>译者注：部分代码示例在原文中可以看效果（作者写在博文里面了…），我偷懒把它做成Gif图了。</p>
</blockquote>
<p>CSS 的 animations （动画） 和 transitions（变换）擅于实现从点 A 到点 B 的直线运动，运动轨迹是直线路径。给一个元素添加了 <code>animation</code> 或者 <code>transition</code> 以后，无论你如何调整<a href="https://developer.mozilla.org/en/docs/Web/CSS/timing-function" target="_blank" rel="external">贝塞尔曲线</a>，都无法让它沿着弧形路径运动。你可以通过自定义 timing function 属性，做出弹动的效果，但是它沿着 X 和 Y 轴相对移动的值永远是相同的。</p>
<p>与其使用 JavaScript 实现外观自然的运动，不如尝试用这种简单的方式：分层动画，绕过已有的限制。通过使用两个或多个元素实现动画效果，我们可以更加细粒度地控制某个元素的路径，沿着 X 轴运动使用一种 timing function ，沿着 Y 轴运动使用另一种 timing function 。</p>
<h1 id="问题所在">问题所在</h1><p><img src="/image/layered-anim/exp1.gif" alt="附图1"></p>
<p>当我们深入探讨解决方案之前，看看到底问题在哪。CSS <code>animations</code> 和 <code>transitions</code> 限制我们只能沿直线路径运动。元素总是沿着点 A 到点 B 的最短路径运动，如果我们另辟蹊径，告诉 CSS 沿着“更好的路径”，而不是“最短路径”运动呢？</p>
<p>用 CSS （开启硬件加速）实现两点之间的运动，最直截了当的方式是使用 <code>transform</code> 的 <code>translate</code> 在一定时间内移动某个元素。这就产生了直线运动。在 <code>@keyframes</code> 中，我们打算在 (0,0) 和 (100,-100) 间来回运动，见上图例子：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">@<span class="keyword">keyframes</span> straightLine &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translate3D</span>(100px, -100px, 0);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-class">.dot</span> &#123;</div><div class="line">  <span class="attribute">animation</span>: straightLine <span class="number">2.5s</span> infinite linear;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这些看起来并不难懂，但我们稍等片刻，思考一下我们需要的解决方案，拆分开来的动画，视觉上长什么样子呢。</p>
<p><code>0%</code> 时，元素从 (0,0) 出发，<code>50%</code> 时，我们用了 <code>translate3D(100px, -100px, 0)</code> 把它移动到 (100,-100)，然后原路返回。换句话说，我们把元素向右移动了 <code>100px</code>，向上移动了 <code>100px</code>，两个方向联合作用使元素沿着一个角度运动。</p>
<a id="more"></a>
<p><img src="/image/layered-anim/exp2.gif" alt="附图2"></p>
<h1 id="解决方案：每个轴执行自己的动画函数">解决方案：每个轴执行自己的动画函数</h1><p>那么，原先展示的例子中我们如何实现的弧形路径呢？为了让创建的路径不是直线，<strong><em>我们想让元素沿 X 轴和 Y 轴的运动速度不同步</em></strong>。</p>
<p>先前例子中都用到了 <code>linear</code> 线性运动函数，如果我们给运动的元素包裹一个容器，我们可以为 X 轴应用一种动画函数，Y 轴应用另一种动画函数。以下例子，我们在 X 轴使用 <code>ease-in</code> ，Y 轴使用 <code>ease-out</code> 。</p>
<p><img src="/image/layered-anim/exp3.gif" alt="附图3"></p>
<h1 id="每个轴元素的具体实现">每个轴元素的具体实现</h1><p>不幸的是，我们不能只把 <code>transform</code> 动画简单叠加：因为只有最后声明的动画会执行。那么我们如何把两个动画效果联合起来呢？可以把一个元素放入另一个元素内部，给容器元素加一种动画，给里面的子元素添加另一种动画。</p>
<p>在以上例子中，你已经看到一个点沿着弧形路径运动，看到两个独立的元素一起做动画，只不过容器元素是完全透明的。为了清晰地看到两个元素沿着弧形路径是如何相互作用的，我们给容器元素加个边框看看呗：</p>
<p><img src="/image/layered-anim/exp4.gif" alt="附图4"></p>
<p>那个点藏在带边框的盒子内部，跟随盒子一起沿 X 轴远动，同时它自己又在 Y 轴方向上下运动。去掉容器盒子的边框，我们就得到了弧形路径。与其在 HTML 中用两个元素，还不如用伪元素实现嘞。如果 HTML 是这样：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"dot"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>我们可以添加伪元素：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.dot</span> &#123;</div><div class="line">  <span class="comment">/* 容器：沿 X 轴运动 */</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-class">.dot</span><span class="selector-pseudo">::after</span> &#123;</div><div class="line">  <span class="comment">/* 黑点儿，沿 Y 轴运动 */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>然后，我们需要两块独立的动画代码：X 轴，Y 轴各一块。注意一处用了 <code>ease-in</code>，另一处用了 <code>ease-out</code>：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.dot</span> &#123;</div><div class="line">  <span class="comment">/*省略 一些布局代码...*/</span></div><div class="line">  <span class="attribute">animation</span>: xAxis <span class="number">2.5s</span> infinite ease-in;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-class">.dot</span><span class="selector-pseudo">::after</span> &#123;</div><div class="line">  <span class="comment">/* 渲染小黑点儿*/</span></div><div class="line">  <span class="attribute">animation</span>: yAxis <span class="number">2.5s</span> infinite ease-out;</div><div class="line">&#125;</div><div class="line"></div><div class="line">@<span class="keyword">keyframes</span> xAxis &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">animation-timing-function</span>: ease-in;</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translateX</span>(100px);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">@<span class="keyword">keyframes</span> yAxis &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">animation-timing-function</span>: ease-out;</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-100px);</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>加上 WebKit 前缀，用一些自定义的贝塞尔曲线代替 <code>ease-in</code> 和 <code>ease-out</code>，我们就可以实现文章最开头展示的效果：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.demo-dot</span> &#123;</div><div class="line">  <span class="attribute">-webkit-animation</span>: xAxis <span class="number">2.5s</span> infinite <span class="built_in">cubic-bezier</span>(0.02, 0.01, 0.21, 1);</div><div class="line">  <span class="attribute">animation</span>: xAxis <span class="number">2.5s</span> infinite <span class="built_in">cubic-bezier</span>(0.02, 0.01, 0.21, 1);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-class">.demo-dot</span><span class="selector-pseudo">::after</span> &#123;</div><div class="line">  <span class="attribute">content</span>: <span class="string">''</span>;</div><div class="line">  <span class="attribute">display</span>: block;</div><div class="line">  <span class="attribute">width</span>: <span class="number">20px</span>;</div><div class="line">  <span class="attribute">height</span>: <span class="number">20px</span>;</div><div class="line">  <span class="attribute">border-radius</span>: <span class="number">20px</span>;</div><div class="line">  <span class="attribute">background-color</span>: <span class="number">#fff</span>;</div><div class="line">  <span class="attribute">-webkit-animation</span>: yAxis <span class="number">2.5s</span> infinite <span class="built_in">cubic-bezier</span>(0.3, 0.27, 0.07, 1.64);</div><div class="line">  <span class="attribute">animation</span>: yAxis <span class="number">2.5s</span> infinite <span class="built_in">cubic-bezier</span>(0.3, 0.27, 0.07, 1.64);</div><div class="line">&#125;</div><div class="line"></div><div class="line">@-<span class="keyword">webkit</span>-<span class="keyword">keyframes</span> yAxis &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">-webkit-animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.02, 0.01, 0.21, 1);</div><div class="line">    <span class="attribute">animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.02, 0.01, 0.21, 1);</div><div class="line">    <span class="attribute">-webkit-transform</span>: <span class="built_in">translateY</span>(-100px);</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-100px);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">@<span class="keyword">keyframes</span> yAxis &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">-webkit-animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.02, 0.01, 0.21, 1);</div><div class="line">    <span class="attribute">animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.02, 0.01, 0.21, 1);</div><div class="line">    <span class="attribute">-webkit-transform</span>: <span class="built_in">translateY</span>(-100px);</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-100px);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">@-<span class="keyword">webkit</span>-<span class="keyword">keyframes</span> xAxis &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">-webkit-animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.3, 0.27, 0.07, 1.64);</div><div class="line">    <span class="attribute">animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.3, 0.27, 0.07, 1.64);</div><div class="line">    <span class="attribute">-webkit-transform</span>: <span class="built_in">translateX</span>(100px);</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translateX</span>(100px);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">@<span class="keyword">keyframes</span> xAxis &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">-webkit-animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.3, 0.27, 0.07, 1.64);</div><div class="line">    <span class="attribute">animation-timing-function</span>: <span class="built_in">cubic-bezier</span>(0.3, 0.27, 0.07, 1.64);</div><div class="line">    <span class="attribute">-webkit-transform</span>: <span class="built_in">translateX</span>(100px);</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translateX</span>(100px);</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>以下是文章起始处的例子：</p>
<p><a class="jsbin-embed" href="http://jsbin.com/waqofexedu/1/embed?html,css,output" target="_blank" rel="external">JS Bin on jsbin.com</a><script src="http://static.jsbin.com/js/embed.min.js?3.35.9"></script></p>
<p>你可能注意到我们在所有例子中都用了 <code>@keyframes</code> ，这纯粹是因为我们想展示黑点儿往返的两种状态。如果只想实现点 A 至点 B 的运动，使用 <code>transition</code> 属性做分层动画同样好用。</p>
<p>如果有个绝对定位的元素，通过给 <code>left</code> 和 <code>bottom</code> 属性加特效，就可以实现弧形路径运动，单个元素就可以，不需要容器元素。为什么不这么做呢：它性能稍差一些，动画的每一帧都会引起重绘。使用带伪元素的分层动画，<code>translate</code> 属性又开了硬件加速，动画效果更好，性能也更高。</p>
<blockquote>
<p>译者自己搞了个绝对定位的例子：</p>
</blockquote>
<p><a class="jsbin-embed" href="http://jsbin.com/zobomaboko/1/embed?html,css,output" target="_blank" rel="external">JS Bin on jsbin.com</a><script src="http://static.jsbin.com/js/embed.min.js?3.35.9"></script>  </p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="http://tobiasahlin.com/blog/curved-path-animations-in-css/">Moving along a curved path in CSS with layered animation</a><br>翻译：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
<p>译者注：部分代码示例在原文中可以看效果（作者写在博文里面了…），我偷懒把它做成Gif图了。</p>
</blockquote>
<p>CSS 的 animations （动画） 和 transitions（变换）擅于实现从点 A 到点 B 的直线运动，运动轨迹是直线路径。给一个元素添加了 <code>animation</code> 或者 <code>transition</code> 以后，无论你如何调整<a href="https://developer.mozilla.org/en/docs/Web/CSS/timing-function">贝塞尔曲线</a>，都无法让它沿着弧形路径运动。你可以通过自定义 timing function 属性，做出弹动的效果，但是它沿着 X 和 Y 轴相对移动的值永远是相同的。</p>
<p>与其使用 JavaScript 实现外观自然的运动，不如尝试用这种简单的方式：分层动画，绕过已有的限制。通过使用两个或多个元素实现动画效果，我们可以更加细粒度地控制某个元素的路径，沿着 X 轴运动使用一种 timing function ，沿着 Y 轴运动使用另一种 timing function 。</p>
<h1 id="问题所在">问题所在</h1><p><img src="/image/layered-anim/exp1.gif" alt="附图1"></p>
<p>当我们深入探讨解决方案之前，看看到底问题在哪。CSS <code>animations</code> 和 <code>transitions</code> 限制我们只能沿直线路径运动。元素总是沿着点 A 到点 B 的最短路径运动，如果我们另辟蹊径，告诉 CSS 沿着“更好的路径”，而不是“最短路径”运动呢？</p>
<p>用 CSS （开启硬件加速）实现两点之间的运动，最直截了当的方式是使用 <code>transform</code> 的 <code>translate</code> 在一定时间内移动某个元素。这就产生了直线运动。在 <code>@keyframes</code> 中，我们打算在 (0,0) 和 (100,-100) 间来回运动，见上图例子：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">@<span class="keyword">keyframes</span> straightLine &#123;</div><div class="line">  50% &#123;</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">translate3D</span>(100px, -100px, 0);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-class">.dot</span> &#123;</div><div class="line">  <span class="attribute">animation</span>: straightLine <span class="number">2.5s</span> infinite linear;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这些看起来并不难懂，但我们稍等片刻，思考一下我们需要的解决方案，拆分开来的动画，视觉上长什么样子呢。</p>
<p><code>0%</code> 时，元素从 (0,0) 出发，<code>50%</code> 时，我们用了 <code>translate3D(100px, -100px, 0)</code> 把它移动到 (100,-100)，然后原路返回。换句话说，我们把元素向右移动了 <code>100px</code>，向上移动了 <code>100px</code>，两个方向联合作用使元素沿着一个角度运动。</p>]]>
    
    </summary>
    
      <category term="animation" scheme="http://jinlong.github.io/tags/animation/"/>
    
      <category term="CSS" scheme="http://jinlong.github.io/categories/CSS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[复选框的 CSS 魔法]]></title>
    <link href="http://jinlong.github.io/2016/01/14/checkbox-trickery-with-css/"/>
    <id>http://jinlong.github.io/2016/01/14/checkbox-trickery-with-css/</id>
    <published>2016-01-14T13:54:07.000Z</published>
    <updated>2016-01-14T14:45:14.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="http://codersblock.com/blog/checkbox-trickery-with-css/" target="_blank" rel="external">Checkbox Trickery with CSS</a><br>翻译：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<p>Checkbox 复选框相当好用，加对 CSS 魔法有奇效。此文旨在展示一些利用 checkbox 实现的有创意的东西，并且文中的例子<strong><em>没用 JavaScript</em></strong> 哟。</p>
<h1 id="基本配方">基本配方</h1><p>从 HTML 着手。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"toggle"</span> <span class="attr">type</span>=<span class="string">"checkbox"</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"toggle"</span>&gt;</span></div></pre></td></tr></table></figure>
<p>此处无技巧可言。<code>&lt;label&gt;</code> 的 <code>for</code> 属性匹配 <code>&lt;input&gt;</code> 的 <code>id</code> 属性，因此点击 <code>&lt;label&gt;</code> 可以控制 <code>&lt;input&gt;</code> 复选框。这点尤其重要，因为下一步将隐藏 <code>&lt;input&gt;</code>。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">input</span> &#123;</div><div class="line">    <span class="attribute">position</span>: absolute;</div><div class="line">    <span class="attribute">left</span>: -<span class="number">9999px</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>为什么不用 <code>display: none</code> ？因为屏幕阅读机和键盘 Tab 会忽略它。此方法让 <code>&lt;input&gt;</code> 保持在文档流中，但是让它离屏隐藏（超出屏幕可见范围达到隐藏）。</p>
<p>隐藏 <code>&lt;input&gt;</code> 以后，我们更容易大展身手。我们仍需传达选中/未选两种状态，但是可以通过 <code>&lt;label&gt;</code> 完成。真正的派对开始啦。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> + <span class="selector-tag">label</span> &#123;</div><div class="line">    <span class="comment">/* 牛X闪闪的样式 */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>我们使用 <code>:checked</code> 伪类， 和相邻兄弟元素选择器（<code>+</code>）的组合达到目的，当复选框选中时，找到紧随其后的 <code>&lt;label&gt;</code>元素，加上想要的样式。还可以利用 <code>&lt;label&gt;</code> 中的伪元素（ <code>::before</code> 和 <code>::after</code>）实现更有创意的想法。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> + <span class="selector-tag">label</span><span class="selector-pseudo">::before</span> &#123;</div><div class="line">    <span class="comment">/* 指示器的样式 */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>来，看看实际效果吧。例子用到了以上提及的基本配方，把一个普普通通的复选框改造得当人眼前一亮。<br><a id="more"></a></p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="RPZZPe" data-default-tab="result" data-user="lonekorean" class="codepen">See the Pen <a href="http://codepen.io/lonekorean/pen/RPZZPe/" target="_blank" rel="external">Checkbox Trickery: Simple Toggle</a> by Will Boyd (<a href="http://codepen.io/lonekorean" target="_blank" rel="external">@lonekorean</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>&lt;form&gt;</code> 中的复选框的值仍然可以被提交。我们只改变了外观，并没有影响功能。 </p>
<h1 id="隐藏/显示内容">隐藏/显示内容</h1><p>目前为止，我们都是给 <code>&lt;label&gt;</code> 加样式，我们可以更进一步。这个例子会根据用户的选择，动态地隐藏/显示表单的部分内容。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="NqvvGK" data-default-tab="result" data-user="lonekorean" class="codepen">See the Pen <a href="http://codepen.io/lonekorean/pen/NqvvGK/" target="_blank" rel="external">Checkbox Trickery: Form Disclosure</a> by Will Boyd (<a href="http://codepen.io/lonekorean" target="_blank" rel="external">@lonekorean</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>:checked</code> 伪类对单选按钮同样奏效，考虑到这一点，“How did you hear about us?”这块的 HTML 用到了单选按钮。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"how-friend"</span> <span class="attr">name</span>=<span class="string">"how"</span> <span class="attr">type</span>=<span class="string">"radio"</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"how-friend"</span> <span class="attr">class</span>=<span class="string">"side-label"</span>&gt;</span>From a friend<span class="tag">&lt;/<span class="name">label</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"how-internet"</span> <span class="attr">name</span>=<span class="string">"how"</span> <span class="attr">type</span>=<span class="string">"radio"</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"how-internet"</span> <span class="attr">class</span>=<span class="string">"side-label"</span>&gt;</span>Somewhere on the internet<span class="tag">&lt;/<span class="name">label</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"how-other"</span> <span class="attr">name</span>=<span class="string">"how"</span> <span class="attr">type</span>=<span class="string">"radio"</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"how-other"</span> <span class="attr">class</span>=<span class="string">"side-label"</span>&gt;</span>Other...<span class="tag">&lt;/<span class="name">label</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"how-other-disclosure"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"how-other-explain"</span> <span class="attr">class</span>=<span class="string">"top-label"</span>&gt;</span>Please explain<span class="tag">&lt;/<span class="name">label</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">textarea</span> <span class="attr">id</span>=<span class="string">"how-other-explain"</span>&gt;</span><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>单选按钮指示器利用 <code>&lt;label&gt;</code> 内的 <code>::before</code> 伪元素（外部的圆环）和 <code>::after</code> 伪元素（内部的绿点）实现，当单选按钮选中 / 未选时显示 / 隐藏 <code>::after</code> 伪元素相当简单。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.side-label</span><span class="selector-pseudo">::after</span> &#123;</div><div class="line">    <span class="attribute">display</span>: none;</div><div class="line"></div><div class="line">    <span class="comment">/* 其它样式 */</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> + <span class="selector-class">.side-label</span><span class="selector-pseudo">::after</span> &#123;</div><div class="line">    <span class="attribute">display</span>: block;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><code>&lt;div&gt;</code> 一直隐藏，直到“Other…”单选按钮选中时才显示。我使用了 <code>display: none</code> 隐藏 <code>&lt;div&gt;</code>，因为这次我确实想让屏幕阅读器和键盘 Tab 在内容隐藏时忽略它。当单选按钮选中时，利用 CSS 显示 <code>&lt;div&gt;</code>。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="selector-id">#how-other</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.how-other-disclosure</span> &#123;</div><div class="line">    <span class="attribute">display</span>: block;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>之前我们一直使用相邻兄弟选择器（<code>+</code>），不过这次得用一般兄弟选择器（<code>~</code>）。它俩很相似，但是可以找到非相邻的兄弟元素，比如我们的 <code>&lt;div&gt;</code>。</p>
<h1 id="树状文件夹">树状文件夹</h1><p>我们可以活用之前例子中的技巧，实现一个树状文件夹组件，同样具备隐藏 / 显示两种功能。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="eNEEpB" data-default-tab="result" data-user="lonekorean" class="codepen">See the Pen <a href="http://codepen.io/lonekorean/pen/eNEEpB/" target="_blank" rel="external">Checkbox Trickery: Folder Tree</a> by Will Boyd (<a href="http://codepen.io/lonekorean" target="_blank" rel="external">@lonekorean</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>单个文件夹的 HTML 如下：<code>&lt;label&gt;</code> 是文件夹，两个 <code>&lt;a&gt;</code> 元素是文件夹里的文件。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"n-1"</span> <span class="attr">type</span>=<span class="string">"checkbox"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"n-1"</span>&gt;</span>Blue<span class="tag">&lt;/<span class="name">label</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"sub"</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#link"</span>&gt;</span>Mana Leak<span class="tag">&lt;/<span class="name">a</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#link"</span>&gt;</span>Time Warp<span class="tag">&lt;/<span class="name">a</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p><a href="http://fortawesome.github.io/Font-Awesome/" target="_blank" rel="external">Font Awesome</a> 图标用于表示选中（打开）和未选（关闭）状态。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">label</span><span class="selector-pseudo">::before</span>, <span class="selector-tag">a</span><span class="selector-pseudo">::before</span> &#123;</div><div class="line">    <span class="attribute">display</span>: block;</div><div class="line">    <span class="attribute">position</span>: absolute;</div><div class="line">    <span class="attribute">top</span>: <span class="number">6px</span>;</div><div class="line">    <span class="attribute">left</span>: -<span class="number">25px</span>;</div><div class="line">    <span class="attribute">font-family</span>: <span class="string">'FontAwesome'</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-tag">label</span><span class="selector-pseudo">::before</span> &#123;</div><div class="line">    <span class="attribute">content</span>: <span class="string">'\f07b'</span>; <span class="comment">/* closed folder */</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> + <span class="selector-tag">label</span><span class="selector-pseudo">::before</span> &#123;</div><div class="line">    <span class="attribute">content</span>: <span class="string">'\f07c'</span>; <span class="comment">/* open folder */</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-tag">a</span><span class="selector-pseudo">::before</span> &#123;</div><div class="line">    <span class="attribute">content</span>: <span class="string">'\f068'</span>; <span class="comment">/* dash */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>文件夹里的内容通过一般兄弟选择器（<code>~</code>）实现显示 / 隐藏。这就是 HTML 外面包裹额外 <code>&lt;div&gt;</code> 的原因，为了确保选择器能够选到元素，打开兄弟文件夹。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.sub</span> &#123;</div><div class="line">    <span class="attribute">display</span>: block;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>理所当然，文件夹可以嵌套。只需把另一个文件夹的 HTML 放入 <code>&lt;div class=&quot;sub&quot;&gt;</code> 即可。点击 “Multicolor”看看效果吧。</p>
<p>最后，我们聊一下重置按钮。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"reset"</span> <span class="attr">value</span>=<span class="string">"Collapse All"</span>&gt;</span></div></pre></td></tr></table></figure>
<p>表单的重置按钮很少有人用，不过这里巧用了一下。点击重置按钮，所有复选框恢复成初始的未选中状态，关闭了所有文件夹。有意思吧。</p>
<h1 id="Split_List（分隔列表）">Split List（分隔列表）</h1><p>这个例子依据已做或未做，把列表选项分成两部分。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="xGLLwX" data-default-tab="result" data-user="lonekorean" class="codepen">See the Pen <a href="http://codepen.io/lonekorean/pen/xGLLwX/" target="_blank" rel="external">Checkbox Trickery: To-Do List</a> by Will Boyd (<a href="http://codepen.io/lonekorean" target="_blank" rel="external">@lonekorean</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>HTML 像这样：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"items"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"item1"</span> <span class="attr">type</span>=<span class="string">"checkbox"</span> <span class="attr">checked</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"item1"</span>&gt;</span>Create a to-do list<span class="tag">&lt;/<span class="name">label</span>&gt;</span></div><div class="line">    </div><div class="line">    <span class="comment">&lt;!-- more items --&gt;</span></div><div class="line">    </div><div class="line">    <span class="tag">&lt;<span class="name">h2</span> <span class="attr">class</span>=<span class="string">"done"</span> <span class="attr">aria-hidden</span>=<span class="string">"true"</span>&gt;</span>Done<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">h2</span> <span class="attr">class</span>=<span class="string">"undone"</span> <span class="attr">aria-hidden</span>=<span class="string">"true"</span>&gt;</span>Not Done<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>分隔列表是由 CSS flexbox 实现的，这是关键的 CSS。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.items</span> &#123;</div><div class="line">    <span class="attribute">display</span>: flex;</div><div class="line">    <span class="attribute">flex-direction</span>: column;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-class">.done</span> &#123;</div><div class="line">    <span class="attribute">order</span>: <span class="number">1</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> + <span class="selector-tag">label</span> &#123;</div><div class="line">    <span class="attribute">order</span>: <span class="number">2</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-class">.undone</span> &#123;</div><div class="line">    <span class="attribute">order</span>: <span class="number">3</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-tag">label</span> &#123;</div><div class="line">    <span class="attribute">order</span>: <span class="number">4</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>CSS flexbox 可以使用 <code>order</code> 属性重排元素。当复选框选中的时候，<code>&lt;label&gt;</code> 的 order 值由 <code>4</code> 变为 <code>2</code>，列表选项就从 “Not Done” <code>&lt;h2&gt;</code> 下面移到了 “Done” <code>&lt;h2&gt;</code> 下面。</p>
<p>不幸的是，键盘导航和<a href="http://sprungmarker.de/wp-content/uploads/css-a11y-group/css-a11y-flexbox.html" target="_blank" rel="external">许多屏幕阅读器</a> 会遵循元素在 DOM 中的顺序，即使它们被 CSS flexbox 做了视觉上的重排。这就导致 “Done”和“Not Done”的标题对于屏幕阅读器无用，这便是我给它们加了 <code>aria-hidden=&quot;true&quot;</code> 的原因 —— 被忽略总比引起混淆强。此外，通过键盘和屏幕阅读器完全可以控制分隔列表，正确显示列表项的状态（选中 / 未选）。</p>
<p>如果你对“Done”和“Not Done”其后的计数实现感到好奇，它们用到了 CSS counters。想深入学习的话，可以看<a href="http://codersblock.com/blog/fun-times-with-css-counters/" target="_blank" rel="external">这篇文章</a>。</p>
<h1 id="分组筛选">分组筛选</h1><p>压轴的例子展示了如何根据筛选条件，高亮显示交叉区域的数据。</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="YXxxyj" data-default-tab="result" data-user="lonekorean" class="codepen">See the Pen <a href="http://codepen.io/lonekorean/pen/YXxxyj/" target="_blank" rel="external">Checkbox Trickery: Group Filter</a> by Will Boyd (<a href="http://codepen.io/lonekorean" target="_blank" rel="external">@lonekorean</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>这是简短的 HTML 。注意一下 <code>data-teams</code> 属性是由空格分隔的列表，它们每一项恰恰与单选按钮的 <code>id</code> 属性一致。我就是这么把角色跟队伍匹配起来的。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"original"</span> <span class="attr">type</span>=<span class="string">"radio"</span> <span class="attr">name</span>=<span class="string">"team"</span> <span class="attr">checked</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"original"</span>&gt;</span>Original X-Men<span class="tag">&lt;/<span class="name">label</span>&gt;</span></div><div class="line"></div><div class="line"><span class="comment">&lt;!-- 省略更多团队 --&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;<span class="name">br</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"characters"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">li</span> <span class="attr">id</span>=<span class="string">"angel"</span> <span class="attr">data-teams</span>=<span class="string">"original force factor hellfire"</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">h2</span>&gt;</span>Angel<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">"ct-angel.png"</span> <span class="attr">alt</span>=<span class="string">""</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line">    </div><div class="line">    <span class="comment">&lt;!-- 省略更多角色 --&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></div></pre></td></tr></table></figure>
<p>关于可访问性，我使用了空的 <code>alt</code> 属性，因为角色的名字已显示在 <code>&lt;h2&gt;</code> 标签中了 —— 每个名字读两次也没有必要。此外，我没有真正的隐藏 <code>&lt;img&gt;</code> 元素（只是收缩和渐隐），屏幕阅读器可以轻易的跳过未高亮的角色。我仅仅需要隐藏 <code>&lt;h2&gt;</code> 。</p>
<p>当选择团队的时候，高亮对应的角色，CSS 是这么实现的。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="selector-id">#original</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="original"]</span> <span class="selector-tag">h2</span>,</div><div class="line"><span class="selector-id">#force</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="force"]</span> <span class="selector-tag">h2</span>,</div><div class="line"><span class="selector-id">#factor</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="factor"]</span> <span class="selector-tag">h2</span>,</div><div class="line"><span class="selector-id">#hellfire</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="hellfire"]</span> <span class="selector-tag">h2</span> &#123;</div><div class="line">    <span class="comment">/* styles to show character name */</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="selector-id">#original</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="original"]</span> <span class="selector-tag">img</span>,</div><div class="line"><span class="selector-id">#force</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="force"]</span> <span class="selector-tag">img</span>,</div><div class="line"><span class="selector-id">#factor</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="factor"]</span> <span class="selector-tag">img</span>,</div><div class="line"><span class="selector-id">#hellfire</span><span class="selector-pseudo">:checked</span> ~ <span class="selector-class">.characters</span> <span class="selector-attr">[data-teams~="hellfire"]</span> <span class="selector-tag">img</span> &#123;</div><div class="line">    <span class="comment">/* styles to show character avatar */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>我清楚这些选择器看起来有点吓人，不过也不赖。我们来仔细分析下例子的第一行（译者注：以 CSS 中最长的选择器开始算）。当 <code>id</code> 为 ‘original’ 的元素被选中时，找到角色元素里面 <code>data-teams</code> 属性包含 ‘original’ 的元素，然后找到它里面的 <code>&lt;h2&gt;</code> 。2-4行的 ‘force’, ‘factor’, 和 ‘hellfire’ 重复上一步。8-11行继续重复上一步，只不过把 <code>&lt;h2&gt;</code> 换成了 <code>&lt;img&gt;</code> 。</p>
<h1 id="综述">综述</h1><p>我希望你跟我一样从这些例子中找到了一些乐趣。通过复选框去实现一些好玩的东西，对我而言非常有趣。我并没有排斥在适当的时候使用 JavaScript ，但是没用它也可以实现这么多东西，也是蛮开心的。谢谢阅读！</p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="http://codersblock.com/blog/checkbox-trickery-with-css/">Checkbox Trickery with CSS</a><br>翻译：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
</blockquote>
<p>Checkbox 复选框相当好用，加对 CSS 魔法有奇效。此文旨在展示一些利用 checkbox 实现的有创意的东西，并且文中的例子<strong><em>没用 JavaScript</em></strong> 哟。</p>
<h1 id="基本配方">基本配方</h1><p>从 HTML 着手。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">id</span>=<span class="string">"toggle"</span> <span class="attr">type</span>=<span class="string">"checkbox"</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"toggle"</span>&gt;</span></div></pre></td></tr></table></figure>
<p>此处无技巧可言。<code>&lt;label&gt;</code> 的 <code>for</code> 属性匹配 <code>&lt;input&gt;</code> 的 <code>id</code> 属性，因此点击 <code>&lt;label&gt;</code> 可以控制 <code>&lt;input&gt;</code> 复选框。这点尤其重要，因为下一步将隐藏 <code>&lt;input&gt;</code>。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">input</span> &#123;</div><div class="line">    <span class="attribute">position</span>: absolute;</div><div class="line">    <span class="attribute">left</span>: -<span class="number">9999px</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>为什么不用 <code>display: none</code> ？因为屏幕阅读机和键盘 Tab 会忽略它。此方法让 <code>&lt;input&gt;</code> 保持在文档流中，但是让它离屏隐藏（超出屏幕可见范围达到隐藏）。</p>
<p>隐藏 <code>&lt;input&gt;</code> 以后，我们更容易大展身手。我们仍需传达选中/未选两种状态，但是可以通过 <code>&lt;label&gt;</code> 完成。真正的派对开始啦。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> + <span class="selector-tag">label</span> &#123;</div><div class="line">    <span class="comment">/* 牛X闪闪的样式 */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>我们使用 <code>:checked</code> 伪类， 和相邻兄弟元素选择器（<code>+</code>）的组合达到目的，当复选框选中时，找到紧随其后的 <code>&lt;label&gt;</code>元素，加上想要的样式。还可以利用 <code>&lt;label&gt;</code> 中的伪元素（ <code>::before</code> 和 <code>::after</code>）实现更有创意的想法。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">input</span><span class="selector-pseudo">:checked</span> + <span class="selector-tag">label</span><span class="selector-pseudo">::before</span> &#123;</div><div class="line">    <span class="comment">/* 指示器的样式 */</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>来，看看实际效果吧。例子用到了以上提及的基本配方，把一个普普通通的复选框改造得当人眼前一亮。<br>]]>
    
    </summary>
    
      <category term="CSS" scheme="http://jinlong.github.io/tags/CSS/"/>
    
      <category term="CSS" scheme="http://jinlong.github.io/categories/CSS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[JavaScript 如何实现后台计划任务]]></title>
    <link href="http://jinlong.github.io/2015/12/14/how-to-schedule-background-tasks-in-javascript/"/>
    <id>http://jinlong.github.io/2015/12/14/how-to-schedule-background-tasks-in-javascript/</id>
    <published>2015-12-14T15:26:53.000Z</published>
    <updated>2016-01-14T13:56:50.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="http://www.sitepoint.com/how-to-schedule-background-tasks-in-javascript/" target="_blank" rel="external">How to Schedule Background Tasks in JavaScript</a><br>翻译：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<p>即使忘了 JavaScript 的一切知识，也不会忘记：它是阻塞的。</p>
<p>想象一下，你的浏览器里住着一个魔法小精灵，负责浏览器的正常运转。不论渲染 HTML，响应菜单命令，屏幕渲染，处理鼠标点击，或者执行 JavaScript 函数，所有事情都归一个小精灵处理。它哪忙得过来，一次只能处理一件事情。如果同时丢给它一堆任务，它会列一个长长的待办列表，按顺序完成它们。</p>
<p>人们常常希望初始化组件和事件处理的 JavaScript 可以尽快被执行。可是，有些不太重要的后台任务不会直接影响用户体验，比如：</p>
<ul>
<li>记录统计数据</li>
<li>发送数据到社交网络（或添加‘分享’按钮）</li>
<li>预加载内容</li>
<li>预处理或预渲染 HTML</li>
</ul>
<p>他们对时序要求不严格，但是为了让页面仍然响应，直到用户滚动页面或者与内容交互时才被执行。</p>
<p>选择之一是 <a href="http://www.sitepoint.com/javascript-threading-html5-web-workers/" target="_blank" rel="external">Web Workers</a> ，它可以在独立的线程同时执行代码。用于预加载和预处理再好不过，但是你没有权限直接访问或更新 DOM。你可以在自己的代码中避开这点，但是无法保证第三方脚本比如 Google Analytics 永远不需要这个。<br><a id="more"></a><br>另一个选择是 <code>setTimeout</code> ，比如 <code>setTimeout(doSomething, 1);</code> 。一旦其它的立即执行任务执行完毕，浏览器将执行 <code>doSomething()</code> 函数。实际上，它被放到了待办列表的底部。不幸的是，函数将被调用，而不顾处理需求。</p>
<h1 id="requestIdleCallback">requestIdleCallback</h1><p><a href="http://www.w3.org/TR/requestidlecallback/" target="_blank" rel="external">requestIdleCallback</a> 是新API，当浏览器稍作喘息的时候，用来执行不太重要的后台计划任务。 难免让人想起 <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame" target="_blank" rel="external">requestAnimationFrame</a>，在下次重绘之前，执行函数更新动画。 想了解更多戳这里：<a href="http://www.sitepoint.com/simple-animations-using-requestanimationframe/" target="_blank" rel="external">使用 requestAnimationFrame 做简单的动画</a>。</p>
<p> <code>requestIdleCallback</code> 特性监测：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (<span class="string">'requestIdleCallback'</span> <span class="keyword">in</span> <span class="built_in">window</span>) &#123;</div><div class="line">  <span class="comment">// requestIdleCallback supported</span></div><div class="line">  requestIdleCallback(backgroundTask);</div><div class="line">&#125;</div><div class="line"><span class="keyword">else</span> &#123;</div><div class="line">  <span class="comment">// no support - do something else</span></div><div class="line">  setTimeout(backgroundTask1, <span class="number">1</span>);</div><div class="line">  setTimeout(backgroundTask2, <span class="number">1</span>);</div><div class="line">  setTimeout(backgroundTask3, <span class="number">1</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>也可以指定配置参数对象，比如 timeout，</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">requestIdleCallback(backgroundTask, &#123; <span class="attr">timeout</span>: <span class="number">3000</span>; &#125;);</div></pre></td></tr></table></figure>
<p>确保函数在3秒之内调用，不管浏览器是否空闲。</p>
<p><code>deadline</code> 对象传入以下参数时，<code>requestIdleCallback</code> 仅执行一次回调：</p>
<ul>
<li><code>didTimeout</code> —— 如果可选的 timeout 触发，则设置为 true</li>
<li><code>timeRemaining()</code> —— 函数返回执行任务剩余的毫秒数<br><code>timeRemaining()</code> 最多分配50ms用于任务的执行，超过这个限制，也不会停止任务，但是，最好重新调用 <code>requestIdleCallback</code> 安排进一步的处理。</li>
</ul>
<p>我们来创建一个简单的例子，让几个任务按序执行。任务的函数引用储存在数组中：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//待执行的函数数组</span></div><div class="line"><span class="keyword">var</span> task = [</div><div class="line">    background1,</div><div class="line">    background2,</div><div class="line">    background3</div><div class="line">];</div><div class="line"></div><div class="line"><span class="keyword">if</span> (<span class="string">'requestIdleCallback'</span> <span class="keyword">in</span> <span class="built_in">window</span>) &#123;</div><div class="line">  <span class="comment">//支持 requestIdleCallback</span></div><div class="line">  requestIdleCallback(backgroundTask);</div><div class="line">&#125;</div><div class="line"><span class="keyword">else</span> &#123;</div><div class="line">  <span class="comment">//不支持 —— 立刻执行所有任务</span></div><div class="line">  <span class="keyword">while</span> (task.length) &#123;</div><div class="line">    setTimeout(task.shift(), <span class="number">1</span>);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">//requestIdleCallback 回调函数</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">backgroundTask</span>(<span class="params">deadline</span>) </span>&#123;</div><div class="line"></div><div class="line">  <span class="comment">//如果存在，执行下一个任务</span></div><div class="line">  <span class="keyword">while</span> (deadline.timeRemaining() &gt; <span class="number">0</span> &amp;&amp; task.length &gt; <span class="number">0</span>) &#123;</div><div class="line">    task.shift()();</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="comment">//需要的话，安排进一步任务</span></div><div class="line">  <span class="keyword">if</span> (task.length &gt; <span class="number">0</span>) &#123;</div><div class="line">    requestIdleCallback(backgroundTask);</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="一次_requestIdleCallback_之间不应该做什么？">一次 requestIdleCallback 之间不应该做什么？</h1><p>Paul Lewis 在<a href="https://developers.google.com/web/updates/2015/08/using-requestidlecallback?hl=en" target="_blank" rel="external">他的文章</a>中提到，一次 requestIdleCallback 执行的任务应该切成小块。它不适用于不可预知时间的情况（比如操作 DOM，使用 requestAnimationFrame 回调更好些）。resolving（或者 rejecting）Promises 时也要谨慎，即使没有更多的剩余时间，空闲回调完成之后，回调函数也将立即执行。</p>
<h1 id="requestIdleCallback_浏览器支持情况">requestIdleCallback 浏览器支持情况</h1><p><code>requestIdleCallback</code> 是试验性特性，规范仍不稳定，碰到 API 变更时不足为奇。Chrome 47 已支持… 2015年结束前应该可用了。Opera 应该会紧跟其后。Microsoft 和 Mozilla 都在考虑 API 是否应该支持 Promises 。Apple 像往常一样不鸟。</p>
<p>Paul Lewis（上文提到的）写了一个简单的 <a href="https://gist.github.com/paullewis/55efe5d6f05434a96c36" target="_blank" rel="external">requestIdleCallback shim</a> ，它可以模拟浏览器的空闲监测行为，但不是一个 polyfill（<a href="http://www.cnblogs.com/ziyunfei/archive/2012/09/17/2688829.html" target="_blank" rel="external">shim 和 polyfill 的区别</a>）。</p>
<p>requestIdleCallback shim代码如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/*!</span></div><div class="line"> * Copyright 2015 Google Inc. All rights reserved.</div><div class="line"> *</div><div class="line"> * Licensed under the Apache License, Version 2.0 (the "License");</div><div class="line"> * you may not use this file except in compliance with the License.</div><div class="line"> * You may obtain a copy of the License at</div><div class="line"> *</div><div class="line"> * http://www.apache.org/licenses/LICENSE-2.0</div><div class="line"> *</div><div class="line"> * Unless required by applicable law or agreed to in writing, software</div><div class="line"> * distributed under the License is distributed on an "AS IS" BASIS,</div><div class="line"> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express</div><div class="line"> * or implied. See the License for the specific language governing</div><div class="line"> * permissions and limitations under the License.</div><div class="line"> */</div><div class="line"> </div><div class="line"><span class="comment">/*</span></div><div class="line"> * @see https://developers.google.com/web/updates/2015/08/using-requestidlecallback</div><div class="line"> */</div><div class="line"><span class="built_in">window</span>.requestIdleCallback = <span class="built_in">window</span>.requestIdleCallback ||</div><div class="line">  <span class="function"><span class="keyword">function</span> (<span class="params">cb</span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> start = <span class="built_in">Date</span>.now();</div><div class="line">    <span class="keyword">return</span> setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</div><div class="line">      cb(&#123; </div><div class="line">        <span class="attr">didTimeout</span>: <span class="literal">false</span>,</div><div class="line">        <span class="attr">timeRemaining</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</div><div class="line">          <span class="keyword">return</span> <span class="built_in">Math</span>.max(<span class="number">0</span>, <span class="number">50</span> - (<span class="built_in">Date</span>.now() - start));</div><div class="line">        &#125;</div><div class="line">      &#125;);</div><div class="line">    &#125;, <span class="number">1</span>);</div><div class="line">  &#125;</div><div class="line"></div><div class="line"><span class="built_in">window</span>.cancelIdleCallback = <span class="built_in">window</span>.cancelIdleCallback ||</div><div class="line">  <span class="function"><span class="keyword">function</span> (<span class="params">id</span>) </span>&#123;</div><div class="line">    clearTimeout(id);</div><div class="line">  &#125;</div></pre></td></tr></table></figure>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="http://www.sitepoint.com/how-to-schedule-background-tasks-in-javascript/">How to Schedule Background Tasks in JavaScript</a><br>翻译：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
</blockquote>
<p>即使忘了 JavaScript 的一切知识，也不会忘记：它是阻塞的。</p>
<p>想象一下，你的浏览器里住着一个魔法小精灵，负责浏览器的正常运转。不论渲染 HTML，响应菜单命令，屏幕渲染，处理鼠标点击，或者执行 JavaScript 函数，所有事情都归一个小精灵处理。它哪忙得过来，一次只能处理一件事情。如果同时丢给它一堆任务，它会列一个长长的待办列表，按顺序完成它们。</p>
<p>人们常常希望初始化组件和事件处理的 JavaScript 可以尽快被执行。可是，有些不太重要的后台任务不会直接影响用户体验，比如：</p>
<ul>
<li>记录统计数据</li>
<li>发送数据到社交网络（或添加‘分享’按钮）</li>
<li>预加载内容</li>
<li>预处理或预渲染 HTML</li>
</ul>
<p>他们对时序要求不严格，但是为了让页面仍然响应，直到用户滚动页面或者与内容交互时才被执行。</p>
<p>选择之一是 <a href="http://www.sitepoint.com/javascript-threading-html5-web-workers/">Web Workers</a> ，它可以在独立的线程同时执行代码。用于预加载和预处理再好不过，但是你没有权限直接访问或更新 DOM。你可以在自己的代码中避开这点，但是无法保证第三方脚本比如 Google Analytics 永远不需要这个。<br>]]>
    
    </summary>
    
      <category term="requestIdleCallback" scheme="http://jinlong.github.io/tags/requestIdleCallback/"/>
    
      <category term="setTimeout" scheme="http://jinlong.github.io/tags/setTimeout/"/>
    
      <category term="web workers" scheme="http://jinlong.github.io/tags/web-workers/"/>
    
      <category term="Javascript" scheme="http://jinlong.github.io/categories/Javascript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[JavaScript 否定谓语函数]]></title>
    <link href="http://jinlong.github.io/2015/12/14/negating-predicate-functions-in-javaScript/"/>
    <id>http://jinlong.github.io/2015/12/14/negating-predicate-functions-in-javaScript/</id>
    <published>2015-12-14T14:51:37.000Z</published>
    <updated>2015-12-14T15:23:04.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="http://adripofjavascript.com/blog/drips/negating-predicate-functions-in-javascript.html" target="_blank" rel="external">Negating Predicate Functions in JavaScript</a><br>翻译：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a>  </p>
</blockquote>
<p>如果你从没听过这个概念，那么至少用过谓语函数。谓语本质上是一个函数，根据它的参数，可以判断返回结果是 <code>true</code> 或者 <code>false</code>。一般都以“isX”命名，比如 <code>isEven</code> 或者 <code>isNumber</code>。</p>
<p>假如我们有个程序，需要处理漫画书里的英雄和坏蛋，以下面的简单对象为例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> superman = &#123;</div><div class="line">    <span class="attr">name</span>: <span class="string">"Superman"</span>,</div><div class="line">    <span class="attr">strength</span>: <span class="string">"Super"</span>,</div><div class="line">    <span class="attr">heroism</span>: <span class="literal">true</span></div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>程序中难免会用到一些谓语，像这些：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isSuperStrong</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.strength === <span class="string">"Super"</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNotSuperStrong</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.strength !== <span class="string">"Super"</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isHeroic</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.heroism === <span class="literal">true</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNotHeroic</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.heroism !== <span class="literal">true</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotSuperStrong(superman));</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotHeroic(superman));</div></pre></td></tr></table></figure>
<a id="more"></a>
<p>如你所见，代码有些冗余。问题不是代码太长，而是核心逻辑定义了两次（每一对谓语，“is”和“isNot”）。逻辑重复意味着逻辑改变时，可能只更新了其中一处谓语，导致错误发生。</p>
<p>如何解决呢？我们首先想到的是这样改：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isSuperStrong</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.strength === <span class="string">"Super"</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNotSuperStrong</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> !isSuperStrong(character);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isHeroic</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.heroism === <span class="literal">true</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNotHeroic</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> !isHeroic(character);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotSuperStrong(superman));</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotHeroic(superman));</div></pre></td></tr></table></figure>
<p>的确有进步，但是仍有冗余。“isNot”谓语只不过颠倒了“is”谓语的功能。</p>
<p>我们何不抽象成一个更清晰，更易维护的函数呢？</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">negate</span> (<span class="params">predicateFunc</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</div><div class="line">        <span class="keyword">return</span> !predicateFunc.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</div><div class="line">    &#125;;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><code>negate</code> 函数接收一个谓语函数作为参数，返回一个函数用来对之前谓语的功能取反。</p>
<p>（如果对 <code>apply</code> 不了解，可以读读这篇《<a href="http://adripofjavascript.com/blog/drips/invoking-javascript-functions-with-call-and-apply" target="_blank" rel="external">Invoking JavaScript Functions With ‘call’ and ‘apply’</a>》）</p>
<p>使用 <code>negate</code> 函数解决我们先前的问题吧。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isSuperStrong</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.strength === <span class="string">"Super"</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">var</span> isNotSuperStrong = negate(isSuperStrong);</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isHeroic</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.heroism === <span class="literal">true</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">var</span> isNotHeroic = negate(isHeroic);</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotSuperStrong(superman));</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotHeroic(superman));</div></pre></td></tr></table></figure>
<p>程序如期运行，但是我们把核心逻辑放到了一处，从“is”谓语也很容易推导出“isNot”谓语。</p>
<p>这点小小的重构看起来微不足道，但是应用于复杂系统的许多分散函数时，<code>negate</code> 函数可以使程序更易维护。</p>
<p><a href="https://github.com/jashkenas/underscore/blob/a315e9f4473005a8310540b16d565519a9556106/underscore.js#L785" target="_blank" rel="external">Underscore</a> 和 <a href="https://github.com/lodash/lodash/blob/6a839967b4beeaf4be0601d75ff4272a18cb5bec/lodash.js#L5259" target="_blank" rel="external">Lo-Dash</a> 都已经填加了 <code>negate</code> 函数。</p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="http://adripofjavascript.com/blog/drips/negating-predicate-functions-in-javascript.html">Negating Predicate Functions in JavaScript</a><br>翻译：<a href="http://weibo.com/newwave">涂鸦码龙</a>  </p>
</blockquote>
<p>如果你从没听过这个概念，那么至少用过谓语函数。谓语本质上是一个函数，根据它的参数，可以判断返回结果是 <code>true</code> 或者 <code>false</code>。一般都以“isX”命名，比如 <code>isEven</code> 或者 <code>isNumber</code>。</p>
<p>假如我们有个程序，需要处理漫画书里的英雄和坏蛋，以下面的简单对象为例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> superman = &#123;</div><div class="line">    <span class="attr">name</span>: <span class="string">"Superman"</span>,</div><div class="line">    <span class="attr">strength</span>: <span class="string">"Super"</span>,</div><div class="line">    <span class="attr">heroism</span>: <span class="literal">true</span></div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>程序中难免会用到一些谓语，像这些：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isSuperStrong</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.strength === <span class="string">"Super"</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNotSuperStrong</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.strength !== <span class="string">"Super"</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isHeroic</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.heroism === <span class="literal">true</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNotHeroic</span> (<span class="params">character</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> character.heroism !== <span class="literal">true</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotSuperStrong(superman));</div><div class="line"></div><div class="line"><span class="comment">// Outputs: false</span></div><div class="line"><span class="built_in">console</span>.log(isNotHeroic(superman));</div></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="Javascript" scheme="http://jinlong.github.io/tags/Javascript/"/>
    
      <category term="Javascript" scheme="http://jinlong.github.io/categories/Javascript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[如何同步 Github fork 出来的分支]]></title>
    <link href="http://jinlong.github.io/2015/10/12/syncing-a-fork/"/>
    <id>http://jinlong.github.io/2015/10/12/syncing-a-fork/</id>
    <published>2015-10-12T05:00:18.000Z</published>
    <updated>2015-10-29T05:18:02.000Z</updated>
    <content type="html"><![CDATA[<p>原先一直有个疑惑， Github fork 出来的项目，我已经做了部分修改，由于某些原因，无法提交 Pull Request，可是想把原项目的最近更新代码合并进来怎么办？google 了一下才茅塞顿开，年纪大了，这里记录一下吧。</p>
<p>两种方式：</p>
<ol>
<li>项目 fetch 到本地，通过命令行的方式 merge</li>
<li>懒人方法，只用  Github ，不用命令行</li>
</ol>
<a id="more"></a>
<h1 id="项目_fetch_到本地，通过命令行的方式_merge">项目 fetch 到本地，通过命令行的方式 merge</h1><p>提示：跟上游仓库同步代码之前，必须配置过 remote，<a href="https://help.github.com/articles/configuring-a-remote-for-a-fork/" target="_blank" rel="external">指向上游仓库</a> 。</p>
<figure class="highlight vim"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git remote <span class="built_in">add</span> upstream http<span class="variable">s:</span>//github.<span class="keyword">com</span>/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git</div></pre></td></tr></table></figure>
<ol>
<li>打开命令行工具</li>
<li>切换当前工作路径至你的本地工程</li>
<li><p>从上游仓库获取到分支，及相关的提交信息，它们将被保存在本地的 <code>upstream/master</code> 分支</p>
<figure class="highlight vala"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">git fetch upstream</div><div class="line"><span class="meta"># remote: Counting objects: 75, done.</span></div><div class="line"><span class="meta"># remote: Compressing objects: 100% (53/53), done.</span></div><div class="line"><span class="meta"># remote: Total 62 (delta 27), reused 44 (delta 9)</span></div><div class="line"><span class="meta"># Unpacking objects: 100% (62/62), done.</span></div><div class="line"><span class="meta"># From https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY</span></div><div class="line"><span class="meta">#  * [new branch]      master     -&gt; upstream/master</span></div></pre></td></tr></table></figure>
</li>
<li><p>切换到本地的 <code>master</code> 分支</p>
<figure class="highlight crmsh"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">git checkout <span class="keyword">master</span></div><div class="line"><span class="title"># Switched</span> to branch '<span class="literal">master</span>'</div></pre></td></tr></table></figure>
</li>
<li><p>把 <code>upstream/master</code> 分支合并到本地的 <code>master</code> 分支，本地的 <code>master</code> 分支便跟上游仓库保持同步了，并且没有丢失你本地的修改。</p>
<figure class="highlight vala"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">git merge upstream/master</div><div class="line"><span class="meta"># Updating a422352..5fdff0f</span></div><div class="line"><span class="meta"># Fast-forward</span></div><div class="line"><span class="meta">#  README                    |    9 -------</span></div><div class="line"><span class="meta">#  README.md                 |    7 ++++++</span></div><div class="line"><span class="meta">#  2 files changed, 7 insertions(+), 9 deletions(-)</span></div><div class="line"><span class="meta">#  delete mode 100644 README</span></div><div class="line"><span class="meta">#  create mode 100644 README.md</span></div></pre></td></tr></table></figure>
</li>
</ol>
<p>提示：同步后的代码仅仅是保存在本地仓库，记得 <code>push</code> 到 Github 哟。</p>
<h1 id="懒人方法，只用_github_，不用命令行">懒人方法，只用 github ，不用命令行</h1><p>盗几张<a href="http://www.zhihu.com/question/20393785" target="_blank" rel="external">知乎的图</a>，见图知意。</p>
<p><img src="/image/sync-a-fork/1.jpg" alt="步骤1"><br><img src="/image/sync-a-fork/2.jpg" alt="步骤2"><br><img src="/image/sync-a-fork/3.jpg" alt="步骤3"><br><img src="/image/sync-a-fork/4.jpg" alt="步骤4"><br>这一页往下面拉:<br><img src="/image/sync-a-fork/5.jpg" alt="步骤5"></p>
<h1 id="参考资料：">参考资料：</h1><ul>
<li>《<a href="http://www.zhihu.com/question/20393785" target="_blank" rel="external">Github 上怎样把新 commits 使用在自己的 fork 上？</a>》</li>
<li>《<a href="https://www.peterdavehello.org/2014/02/update_forked_repository/" target="_blank" rel="external">更新從Github上fork出來的repository (或是同步兩個不同server端的repository)</a>》</li>
<li>《<a href="https://help.github.com/articles/syncing-a-fork/" target="_blank" rel="external">Syncing a fork</a>》</li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<p>原先一直有个疑惑， Github fork 出来的项目，我已经做了部分修改，由于某些原因，无法提交 Pull Request，可是想把原项目的最近更新代码合并进来怎么办？google 了一下才茅塞顿开，年纪大了，这里记录一下吧。</p>
<p>两种方式：</p>
<ol>
<li>项目 fetch 到本地，通过命令行的方式 merge</li>
<li>懒人方法，只用  Github ，不用命令行</li>
</ol>]]>
    
    </summary>
    
      <category term="git" scheme="http://jinlong.github.io/tags/git/"/>
    
      <category term="github" scheme="http://jinlong.github.io/tags/github/"/>
    
      <category term="github" scheme="http://jinlong.github.io/categories/github/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[是什么导致重排（layout / reflow）？]]></title>
    <link href="http://jinlong.github.io/2015/09/30/what-forces-layout-reflow/"/>
    <id>http://jinlong.github.io/2015/09/30/what-forces-layout-reflow/</id>
    <published>2015-09-30T04:23:38.000Z</published>
    <updated>2015-10-29T04:52:38.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：<a href="https://gist.github.com/paulirish/5d52fb081b3570c81e3a" target="_blank" rel="external">What forces layout / reflow</a><br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
<p>注：本文只摘取了自己认为重要的知识点，并没有逐字逐句翻译</p>
</blockquote>
<p>当在 JavaScript 中调用（requested/called）以下所有属性或方法时，浏览器将会同步地计算样式和布局。重排（也有叫 reflow 或 <a href="http://www.kellegous.com/j/2013/01/26/layout-performance/" target="_blank" rel="external">layout thrashing</a> 的），通常是性能瓶颈。</p>
<h1 id="元素">元素</h1><h2 id="盒子计算">盒子计算</h2><ul>
<li>elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent</li>
<li>elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight</li>
<li>elem.getClientRects(), elem.getBoundingClientRect()</li>
</ul>
<h2 id="滚动相关">滚动相关</h2><ul>
<li>elem.scrollBy(), elem.scrollTo()</li>
<li>elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()</li>
<li>elem.scrollWidth, elem.scrollHeight</li>
<li>elem.scrollLeft, elem.scrollTop，设置它们的值，同样会影响</li>
</ul>
<h2 id="获得焦点">获得焦点</h2><ul>
<li>elem.focus() 可以引起两次重排（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/dom/Element.cpp&amp;q=updateLayoutIgnorePendingStylesheets%20-f:out%20-f:test&amp;sq=package:chromium&amp;l=2369&amp;ct=rc&amp;cd=4&amp;dr=C" target="_blank" rel="external">源码在此</a>）</li>
</ul>
<a id="more"></a>
<h2 id="还有…">还有…</h2><ul>
<li>elem.computedRole, elem.computedName</li>
<li>elem.innerText（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/dom/Element.cpp&amp;q=updateLayoutIgnorePendingStylesheets%20-f:out%20-f:test&amp;sq=package:chromium&amp;l=2626&amp;ct=rc&amp;cd=4&amp;dr=C" target="_blank" rel="external">源码</a>）</li>
</ul>
<h1 id="getComputedStyle">getComputedStyle</h1><p><code>window.getComputedStyle()</code> 通常会引起样式重新计算（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/dom/Document.cpp&amp;sq=package:chromium&amp;type=cs&amp;l=1860&amp;q=updateLayoutTreeForNodeIfNeeded" target="_blank" rel="external">源码</a>）</p>
<p>如果以下任何一种情况存在，<code>window.getComputedStyle()</code> 将会引起重排：</p>
<ol>
<li>元素在 shadow tree 中</li>
<li>使用了 media queries （viewport相关的一种），特别是以下某一属性：（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/css/MediaQueryExp.cpp&amp;sq=package:chromium&amp;type=cs&amp;l=163&amp;q=MediaQueryExp::isViewportDependent" target="_blank" rel="external">源码</a>）<ul>
<li>min-width, min-height, max-width, max-height, width, height</li>
<li>aspect-ratio, min-aspect-ratio, max-aspect-ratio</li>
<li>device-pixel-ratio, resolution, orientation</li>
</ul>
</li>
<li>获取以下的某一种属性：（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp&amp;sq=package:chromium&amp;l=457&amp;dr=C&amp;q=isLayoutDependent" target="_blank" rel="external">源码</a>）<ul>
<li>height, width</li>
<li>top, right, bottom, left</li>
<li>margin [-top, -right, -bottom, -left, 或简写] ，仅当 margin 是固定值。</li>
<li>padding [-top, -right, -bottom, -left,  或简写] ，仅当 padding 是固定值。</li>
<li>transform, transform-origin, perspective-origin</li>
<li>translate, rotate, scale</li>
<li>webkit-filter, backdrop-filter</li>
<li>motion-path, motion-offset, motion-rotation</li>
<li>x, y, rx, ry</li>
</ul>
</li>
</ol>
<h1 id="window">window</h1><ul>
<li>window.scrollX, window.scrollY</li>
<li>window.innerHeight, window.innerWidth</li>
<li>window.getMatchedCSSRules() 仅重新计算样式</li>
</ul>
<h1 id="表单">表单</h1><ul>
<li>inputElem.focus()</li>
<li>inputElem.select(), textareaElem.select()（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/html/HTMLTextFormControlElement.cpp&amp;q=updateLayoutIgnorePendingStylesheets%20-f:out%20-f:test&amp;sq=package:chromium&amp;l=192&amp;dr=C" target="_blank" rel="external">源码</a>）</li>
</ul>
<h1 id="鼠标事件">鼠标事件</h1><ul>
<li>mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/events/MouseRelatedEvent.cpp&amp;q=f:mouserelatedevent%20computeRelativePosition&amp;sq=package:chromium&amp;type=cs&amp;l=132" target="_blank" rel="external">源码</a>）</li>
</ul>
<h1 id="document">document</h1><ul>
<li>doc.scrollingElement 仅重新计算样式</li>
</ul>
<h1 id="Range">Range</h1><ul>
<li>range.getClientRects(), range.getBoundingClientRect()</li>
</ul>
<h1 id="SVG">SVG</h1><p>相当多；没有详尽的清单，但是 <a href="http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html" target="_blank" rel="external">Tony Gentilcore’s 2011 Layout Triggering List</a> 指出了一些。</p>
<h1 id="contenteditable">contenteditable</h1><p>数不胜数，…包含复制图片到剪切板（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/editing/Editor.cpp&amp;sq=package:chromium&amp;l=420&amp;dr=C&amp;rcl=1442532378" target="_blank" rel="external">源码</a>）</p>
<h1 id="附录">附录</h1><ul>
<li>Reflow 仅在文档流已经改变，设定无效的样式或布局时，带来一定消耗。通常情况下，是由于 DOM 已经改变（class 名已修改，节点已新增/已移除，甚至添加一个伪类，像 :focus）。</li>
<li>如果引起重排，样式首先必须重新计算。因此重排会触发两种操作。它的消耗非常依赖于内容/形势，但通常两种操作成本相似。</li>
<li>我们该如何避免呢？长话短说：<ul>
<li>for 循环里触发重排 &amp; 改变 DOM 性能最低，尽量避免。</li>
<li>使用 DevTools Timeline 看看问题在哪。</li>
<li>合并读写 DOM 操作（通过 [FastDOM])(<a href="https://github.com/wilsonpage/fastdom" target="_blank" rel="external">https://github.com/wilsonpage/fastdom</a>) 或 虚拟 DOM 实现）</li>
</ul>
</li>
</ul>
<h1 id="啃_Chromium_源码：">啃 Chromium 源码：</h1><ul>
<li>触发重排（和重新计算样式）：<a href="https://code.google.com/p/chromium/codesearch#search/&amp;q=updateLayoutIgnorePendingStylesheets%20-f:out%20-f:test&amp;sq=package:chromium&amp;type=cs" target="_blank" rel="external">updateLayoutIgnorePendingStylesheets - Chromium Code Search</a> </li>
<li>触发样式重算：<a href="https://code.google.com/p/chromium/codesearch#search/&amp;q=updateLayoutTree%20-f:out&amp;p=1&amp;sq=package:chromium&amp;type=cs" target="_blank" rel="external">updateLayoutTree - Chromium Code Search</a> </li>
</ul>
<h1 id="CSS_Triggers">CSS Triggers</h1><p><a href="http://csstriggers.com/" target="_blank" rel="external">CSS Triggers</a> 是非常棒的参考资源。设置/改变给出的 CSS 值，在浏览器的生命周期会出现什么结果，会触发哪些操作都有展示。</p>
<h1 id="更多资料">更多资料</h1><ul>
<li><a href="https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing?hl=en" target="_blank" rel="external">Avoiding layout thrashing — Web Fundamentals</a> </li>
<li><a href="https://mattandre.ws/2014/05/really-fixing-layout-thrashing/" target="_blank" rel="external">Fixing Layout thrashing in the real world | Matt Andrews</a> </li>
<li><a href="https://developer.chrome.com/devtools/docs/demos/too-much-layout" target="_blank" rel="external">Timeline demo: Diagnosing forced synchronous layouts - Google Chrome</a> </li>
<li><a href="http://wilsonpage.co.uk/preventing-layout-thrashing/" target="_blank" rel="external">Preventing ‘layout thrashing’ | Wilson Page</a> </li>
<li><a href="https://github.com/wilsonpage/fastdom" target="_blank" rel="external">wilsonpage/fastdom</a> </li>
<li><a href="http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/" target="_blank" rel="external">Rendering: repaint, reflow/relayout, restyle / Stoyan</a> </li>
<li><a href="http://blog.fogcreek.com/we-spent-a-week-making-trello-boards-load-extremely-fast-heres-how-we-did-it/" target="_blank" rel="external">We spent a week making Trello boards load extremely fast. Here’s how we did it. - Fog Creek Blog</a> </li>
<li><a href="https://developers.google.com/speed/articles/reflow?hl=en" target="_blank" rel="external">Minimizing browser reflow  |  PageSpeed Insights  |  Google Developers</a> </li>
<li><a href="https://developers.google.com/speed/articles/reflow?hl=en" target="_blank" rel="external">Optimizing Web Content in UIWebViews and Websites on iOS</a> </li>
<li><a href="http://www.html5rocks.com/en/tutorials/speed/layers/" target="_blank" rel="external">Accelerated Rendering in Chrome</a> </li>
<li><a href="https://www.igvita.com/slides/2012/web-performance-for-the-curious/" target="_blank" rel="external">web performance for the curious</a> </li>
<li><a href="http://jankfree.org/" target="_blank" rel="external">Jank Free</a> </li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：<a href="https://gist.github.com/paulirish/5d52fb081b3570c81e3a">What forces layout / reflow</a><br>笔记：<a href="http://weibo.com/newwave">涂鸦码龙</a></p>
<p>注：本文只摘取了自己认为重要的知识点，并没有逐字逐句翻译</p>
</blockquote>
<p>当在 JavaScript 中调用（requested/called）以下所有属性或方法时，浏览器将会同步地计算样式和布局。重排（也有叫 reflow 或 <a href="http://www.kellegous.com/j/2013/01/26/layout-performance/">layout thrashing</a> 的），通常是性能瓶颈。</p>
<h1 id="元素">元素</h1><h2 id="盒子计算">盒子计算</h2><ul>
<li>elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent</li>
<li>elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight</li>
<li>elem.getClientRects(), elem.getBoundingClientRect()</li>
</ul>
<h2 id="滚动相关">滚动相关</h2><ul>
<li>elem.scrollBy(), elem.scrollTo()</li>
<li>elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()</li>
<li>elem.scrollWidth, elem.scrollHeight</li>
<li>elem.scrollLeft, elem.scrollTop，设置它们的值，同样会影响</li>
</ul>
<h2 id="获得焦点">获得焦点</h2><ul>
<li>elem.focus() 可以引起两次重排（<a href="https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/dom/Element.cpp&amp;q=updateLayoutIgnorePendingStylesheets%20-f:out%20-f:test&amp;sq=package:chromium&amp;l=2369&amp;ct=rc&amp;cd=4&amp;dr=C">源码在此</a>）</li>
</ul>]]>
    
    </summary>
    
      <category term="layout" scheme="http://jinlong.github.io/tags/layout/"/>
    
      <category term="reflow" scheme="http://jinlong.github.io/tags/reflow/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[JavaScript API 设计原则]]></title>
    <link href="http://jinlong.github.io/2015/08/31/secrets-of-awesome-javascript-api-design/"/>
    <id>http://jinlong.github.io/2015/08/31/secrets-of-awesome-javascript-api-design/</id>
    <published>2015-08-31T13:45:36.000Z</published>
    <updated>2015-10-29T03:08:20.000Z</updated>
    <content type="html"><![CDATA[<p>前段时间组织优化我们的原生模块 API（iOS、Android 模块封装成 JavaScript 接口），于是学习了几篇 JavaScript API 设计的文章，尽管是旧文，但受益匪浅，这里记录一下。</p>
<hr>
<p>好的 API 设计：在自描述的同时，达到抽象的目标。</p>
<p>设计良好的 API ，开发者可以快速上手，没必要经常抱着手册和文档，也没必要频繁光顾技术支持社区。</p>
<h1 id="流畅的接口">流畅的接口</h1><p><strong><a href="https://en.wikipedia.org/wiki/Method_chaining" target="_blank" rel="external">方法链</a>：流畅易读，更易理解</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//常见的 API 调用方式：改变一些颜色，添加事件监听</span></div><div class="line"><span class="keyword">var</span> elem = <span class="built_in">document</span>.getElementById(<span class="string">"foobar"</span>);</div><div class="line">elem.style.background = <span class="string">"red"</span>;</div><div class="line">elem.style.color = <span class="string">"green"</span>;</div><div class="line">elem.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</div><div class="line">  alert(<span class="string">"hello world!"</span>);</div><div class="line">&#125;, <span class="literal">true</span>);</div><div class="line"></div><div class="line"><span class="comment">//（设想的）方法链 API</span></div><div class="line">DOMHelper.getElementById(<span class="string">'foobar'</span>)</div><div class="line">  .setStyle(<span class="string">"background"</span>, <span class="string">"red"</span>)</div><div class="line">  .setStyle(<span class="string">"color"</span>, <span class="string">"green"</span>)</div><div class="line">  .addEvent(<span class="string">"click"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</div><div class="line">    alert(<span class="string">"hello world"</span>);</div><div class="line">  &#125;);</div></pre></td></tr></table></figure>
<a id="more"></a>
<p><strong>设置和获取操作，可以合二为一；方法越多，文档可能越难写</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> $elem = jQuery(<span class="string">"#foobar"</span>);</div><div class="line"></div><div class="line"><span class="comment">//setter</span></div><div class="line">$elem.setCss(<span class="string">"background"</span>, <span class="string">"green"</span>);</div><div class="line"><span class="comment">//getter</span></div><div class="line">$elem.getCss(<span class="string">"color"</span>) === <span class="string">"red"</span>;</div><div class="line"></div><div class="line"><span class="comment">//getter, setter 合二为一</span></div><div class="line">$elem.css(<span class="string">"background"</span>, <span class="string">"green"</span>);</div><div class="line">$elem.css(<span class="string">"color"</span>) === <span class="string">"red"</span>;</div></pre></td></tr></table></figure>
<h1 id="一致性">一致性</h1><p>相关的接口保持一致的风格，一整套 API 如果传递一种熟悉和舒适的感觉，会大大减轻开发者对新工具的适应性。   </p>
<p>命名这点事：既要短，又要自描述，最重要的是保持一致性</p>
<blockquote>
<p>“There are only two hard problems in computer science: cache-invalidation and naming things.”<br>“在计算机科学界只有两件头疼的事：缓存失效和命名问题”<br>— Phil Karlton</p>
</blockquote>
<p>选择一个你喜欢的措辞，然后持续使用。选择一种风格，然后保持这种风格。</p>
<h1 id="处理参数">处理参数</h1><p>需要考虑大家如何使用你提供的方法，是否会重复调用？为何会重复调用？你的 API 如何帮助开发者减少重复的调用？<br>接收map映射参数，回调或者序列化的属性名，不仅让你的 API 更干净，而且使用起来更舒服、高效。</p>
<p>jQuery 的 <code>css()</code> 方法可以给 DOM 元素设置样式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">jQuery(<span class="string">"#some-selector"</span>)</div><div class="line">  .css(<span class="string">"background"</span>, <span class="string">"red"</span>)</div><div class="line">  .css(<span class="string">"color"</span>, <span class="string">"white"</span>)</div><div class="line">  .css(<span class="string">"font-weight"</span>, <span class="string">"bold"</span>)</div><div class="line">  .css(<span class="string">"padding"</span>, <span class="number">10</span>);</div></pre></td></tr></table></figure>
<p>这个方法可以接受一个 JSON 对象：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">jQuery(<span class="string">"#some-selector"</span>).css(&#123;</div><div class="line">  <span class="string">"background"</span> : <span class="string">"red"</span>,</div><div class="line">  <span class="string">"color"</span> : <span class="string">"white"</span>,</div><div class="line">  <span class="string">"font-weight"</span> : <span class="string">"bold"</span>,</div><div class="line">  <span class="string">"padding"</span> : <span class="number">10</span></div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">//通过传一个 map 映射绑定事件</span></div><div class="line">jQuery(<span class="string">"#some-selector"</span>).on(&#123;</div><div class="line">  <span class="string">"click"</span> : myClickHandler,</div><div class="line">  <span class="string">"keyup"</span> : myKeyupHandler,</div><div class="line">  <span class="string">"change"</span> : myChangeHandler</div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">//为多个事件绑定同一个处理函数</span></div><div class="line">jQuery(<span class="string">"#some-selector"</span>).on(<span class="string">"click keyup change"</span>, myEventHandler);</div></pre></td></tr></table></figure>
<h2 id="处理类型">处理类型</h2><p>定义方法的时候，需要决定它可以接收什么样的参数。我们不清楚人们如何使用我们的代码，但可以更有远见，考虑支持哪些参数类型。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//原来的代码</span></div><div class="line">DateInterval.prototype.days = <span class="function"><span class="keyword">function</span>(<span class="params">start, end</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> <span class="built_in">Math</span>.floor((end - start) / <span class="number">86400000</span>);</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="comment">//修改后的代码</span></div><div class="line">DateInterval.prototype.days = <span class="function"><span class="keyword">function</span>(<span class="params">start, end</span>) </span>&#123;</div><div class="line">  <span class="keyword">if</span> (!(start <span class="keyword">instanceof</span> <span class="built_in">Date</span>)) &#123;</div><div class="line">    start = <span class="keyword">new</span> <span class="built_in">Date</span>(start);</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">if</span> (!(end <span class="keyword">instanceof</span> <span class="built_in">Date</span>)) &#123;</div><div class="line">    end = <span class="keyword">new</span> <span class="built_in">Date</span>(end);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> <span class="built_in">Math</span>.floor((end.getTime() - start.getTime()) / <span class="number">86400000</span>);</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>加了短短的6行代码，我们的方法强大到可以接收 <code>Date</code> 对象，数字的时间戳，甚至像 <code>Sat Sep 08 2012 15:34:35 GMT+0200 (CEST)</code> 这样的字符串</p>
<p>如果你需要确保传入的参数类型（字符串，数字，布尔），可以这样转换：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">castaway</span>(<span class="params">some_string, some_integer, some_boolean</span>) </span>&#123;</div><div class="line">  some_string += <span class="string">""</span>;</div><div class="line">  some_integer += <span class="number">0</span>; <span class="comment">// parseInt(some_integer, 10) 更安全些</span></div><div class="line">  some_boolean = !!some_boolean;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="处理_undefined">处理 undefined</h2><p>为了使你的 API 更健壮，需要鉴别是否真正的 <code>undefined</code> 值被传递进来，可以检查 <code>arguments</code> 对象：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">testUndefined</span>(<span class="params">expecting, someArgument</span>) </span>&#123;</div><div class="line">  <span class="keyword">if</span> (someArgument === <span class="literal">undefined</span>) &#123;</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">"someArgument 是 undefined"</span>);</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">if</span> (<span class="built_in">arguments</span>.length &gt; <span class="number">1</span>) &#123;</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">"然而它实际是传进来的"</span>);</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">testUndefined(<span class="string">"foo"</span>);</div><div class="line"><span class="comment">// 结果: someArgument 是 undefined</span></div><div class="line">testUndefined(<span class="string">"foo"</span>, <span class="literal">undefined</span>);</div><div class="line"><span class="comment">// 结果:  someArgument 是 undefined , 然而它实际是传进来的</span></div></pre></td></tr></table></figure>
<h2 id="给参数命名">给参数命名</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">event.initMouseEvent(</div><div class="line">  <span class="string">"click"</span>, <span class="literal">true</span>, <span class="literal">true</span>, <span class="built_in">window</span>,</div><div class="line">  <span class="number">123</span>, <span class="number">101</span>, <span class="number">202</span>, <span class="number">101</span>, <span class="number">202</span>,</div><div class="line">  <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>,</div><div class="line">  <span class="number">1</span>, <span class="literal">null</span>);</div></pre></td></tr></table></figure>
<p><a href="https://developer.mozilla.org/en-US/docs/DOM/event.initMouseEvent" target="_blank" rel="external">Event.initMouseEvent</a> 这个方法简直丧心病狂，不看文档的话，谁能说出每个参数是什么意思？ </p>
<p>给每个参数起个名字，赋个默认值，可好</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">event.initMouseEvent(</div><div class="line">  type=<span class="string">"click"</span>,</div><div class="line">  canBubble=<span class="literal">true</span>,</div><div class="line">  cancelable=<span class="literal">true</span>,</div><div class="line">  view=<span class="built_in">window</span>,</div><div class="line">  detail=<span class="number">123</span>,</div><div class="line">  screenX=<span class="number">101</span>,</div><div class="line">  screenY=<span class="number">202</span>,</div><div class="line">  clientX=<span class="number">101</span>,</div><div class="line">  clientY=<span class="number">202</span>,</div><div class="line">  ctrlKey=<span class="literal">true</span>,</div><div class="line">  altKey=<span class="literal">false</span>,</div><div class="line">  shiftKey=<span class="literal">false</span>,</div><div class="line">  metaKey=<span class="literal">false</span>,</div><div class="line">  button=<span class="number">1</span>,</div><div class="line">  relatedTarget=<span class="literal">null</span>);</div></pre></td></tr></table></figure>
<p>ES6, 或者 Harmony 就有 <a href="http://wiki.ecmascript.org/doku.php?id=harmony:parameter_default_values" target="_blank" rel="external">默认参数值</a> 和 <a href="http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters" target="_blank" rel="external">rest 参数</a> 了。</p>
<h2 id="参数接收_JSON_对象">参数接收 JSON 对象</h2><p>与其接收一堆参数，不如接收一个 JSON 对象：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">nightmare</span>(<span class="params">accepts, async, beforeSend, cache, complete, <span class="regexp">/* 等28个参数 */</span></span>) </span>&#123;</div><div class="line">  <span class="keyword">if</span> (accepts === <span class="string">"text"</span>) &#123;</div><div class="line">    <span class="comment">// 准备接收纯文本</span></div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">dream</span>(<span class="params">options</span>) </span>&#123;</div><div class="line">  options = options || &#123;&#125;;</div><div class="line">  <span class="keyword">if</span> (options.accepts === <span class="string">"text"</span>) &#123;</div><div class="line">    <span class="comment">// 准备接收纯文本</span></div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>调用起来也更简单了：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">nightmare(<span class="string">"text"</span>, <span class="literal">true</span>, <span class="literal">undefined</span>, <span class="literal">false</span>, <span class="literal">undefined</span>, <span class="comment">/* 等28个参数 */</span>);</div><div class="line"></div><div class="line">dream(&#123;</div><div class="line">  <span class="attr">accepts</span>: <span class="string">"text"</span>,</div><div class="line">  <span class="attr">async</span>: <span class="literal">true</span>,</div><div class="line">  <span class="attr">cache</span>: <span class="literal">false</span></div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<h2 id="参数默认值">参数默认值</h2><p>参数最好有默认值，通过 <a href="http://api.jquery.com/jQuery.extend/  , _.extend(" target="_blank" rel="external">jQuery.extend()</a> <a href="http://underscorejs.org/#extend" target="_blank" rel="external">http://underscorejs.org/#extend</a>) 和 Protoype 的 <a href="http://api.prototypejs.org/language/Object/extend/" target="_blank" rel="external">Object.extend</a> ，可以覆盖预设的默认值。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> default_options = &#123;</div><div class="line">  <span class="attr">accepts</span>: <span class="string">"text"</span>,</div><div class="line">  <span class="attr">async</span>: <span class="literal">true</span>,</div><div class="line">  <span class="attr">beforeSend</span>: <span class="literal">null</span>,</div><div class="line">  <span class="attr">cache</span>: <span class="literal">false</span>,</div><div class="line">  <span class="attr">complete</span>: <span class="literal">null</span>,</div><div class="line">  <span class="comment">// …</span></div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">dream</span>(<span class="params">options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> o = jQuery.extend(&#123;&#125;, default_options, options || &#123;&#125;);</div><div class="line">  <span class="built_in">console</span>.log(o.accepts);</div><div class="line">&#125;</div><div class="line"></div><div class="line">dream(&#123; <span class="attr">async</span>: <span class="literal">false</span> &#125;);</div><div class="line"><span class="comment">// prints: "text"</span></div></pre></td></tr></table></figure>
<h1 id="扩展性">扩展性</h1><h2 id="回调（callbacks）">回调（callbacks）</h2><p>通过回调， API 用户可以覆盖你的某一部分代码。把一些需要自定义的功能开放成可配置的回调函数，允许 API 用户轻松覆盖你的默认代码。</p>
<p>API 接口一旦接收回调，确保在文档中加以说明，并提供代码示例。</p>
<h2 id="事件（events）">事件（events）</h2><p>事件接口最好见名知意，可以自由选择事件名字，避免与<a href="https://developer.mozilla.org/en-US/docs/DOM/DOM_event_reference" target="_blank" rel="external">原生事件</a> 重名。</p>
<h1 id="处理错误">处理错误</h1><p>不是所有的错误都对开发者调试代码有用：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// jQuery 允许这么写</span></div><div class="line">$(<span class="built_in">document</span>.body).on(<span class="string">'click'</span>, &#123;&#125;);</div><div class="line"></div><div class="line"><span class="comment">// 点击时报错</span></div><div class="line"><span class="comment">//   TypeError: ((p.event.special[l.origType] || &#123;&#125;).handle || l.handler).apply is not a function</span></div><div class="line"><span class="comment">//   in jQuery.min.js on Line 3</span></div></pre></td></tr></table></figure>
<p>这样的错误调试起来很痛苦，不要浪费开发者的时间，直接告诉他们犯了什么错：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (<span class="built_in">Object</span>.prototype.toString.call(callback) !== <span class="string">'[object Function]'</span>) &#123; <span class="comment">// 看备注</span></div><div class="line">  <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"callback is not a function!"</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<blockquote>
<p>备注：<code>typeof callback === &quot;function&quot;</code> 在老的浏览器上会有问题，<code>object</code> 会当成个 <code>function</code> 。</p>
</blockquote>
<h1 id="可预测性">可预测性</h1><p>好的 API 具有可预测性，开发者可以根据例子推断它的用法。</p>
<p><a href="http://modernizr.com/docs/#howitworks" target="_blank" rel="external">Modernizr’s 特性检测</a> 是个例子：</p>
<p>a) 它使用的属性名完全与 HTML5、CSS 概念和 API 相匹配</p>
<p>b) 每一个单独的检测一致地返回 true 或 false 值</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 所有这些属性都返回 'true' 或 'false'</span></div><div class="line">Modernizr.geolocation</div><div class="line">Modernizr.localstorage</div><div class="line">Modernizr.webworkers</div><div class="line">Modernizr.canvas</div><div class="line">Modernizr.borderradius</div><div class="line">Modernizr.boxshadow</div><div class="line">Modernizr.flexbox</div></pre></td></tr></table></figure>
<p>依赖于开发者已熟悉的概念也可以达到可预测的目的。</p>
<p><a href="http://api.jquery.com/category/selectors/" target="_blank" rel="external">jQuery’s 选择器语法</a> 就是一个显著的例子，CSS1-CSS3 的选择器可直接用于它的 DOM 选择器引擎。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$(<span class="string">"#grid"</span>) <span class="comment">// Selects by ID</span></div><div class="line">$(<span class="string">"ul.nav &gt; li"</span>) <span class="comment">// All LIs for the UL with class "nav"</span></div><div class="line">$(<span class="string">"ul li:nth-child(2)"</span>) <span class="comment">// Second item in each list</span></div></pre></td></tr></table></figure>
<h1 id="比例协调">比例协调</h1><p>好的 API 并不一定是小的 API，API 的体积大小要跟它的功能相称。</p>
<p>比如 <a href="http://momentjs.com/" target="_blank" rel="external">Moment.js</a> ，著名的日期解析和格式化的库，可以称之为均衡，它的 API 既简洁又功能明确。</p>
<p>像 Moment.js 这样特定功能的库，确保 API 的专注和小巧非常重要。</p>
<h1 id="编写_API_文档">编写 API 文档</h1><p>软件开发最艰难的任务之一是写文档，实际上每个人都恨写文档，怨声载道的是没有一个好用的文档工具。</p>
<p>以下是一些文档自动生成工具：</p>
<ul>
<li><a href="http://yui.github.com/yuidoc/" target="_blank" rel="external">YUIDoc</a> (requires Node.js, npm)</li>
<li><a href="https://github.com/p120ph37/node-jsdoc-toolkit" target="_blank" rel="external">JsDoc Toolkit</a> (requires Node.js, npm)</li>
<li><a href="https://github.com/cbou/markdox" target="_blank" rel="external">Markdox</a> (requires Node.js, npm)</li>
<li><a href="https://github.com/visionmedia/dox" target="_blank" rel="external">Dox</a> (requires Node.js, npm)</li>
<li><a href="http://jashkenas.github.com/docco/" target="_blank" rel="external">Docco</a> (requires Node.js, Python, CoffeeScript)</li>
<li><a href="https://github.com/senchalabs/jsduck" target="_blank" rel="external">JSDuck</a> (reqires Ruby, gem)</li>
<li><a href="https://github.com/jsdoc3/jsdoc" target="_blank" rel="external">JSDoc 3</a> (requires Java)</li>
</ul>
<p><strong>最重要的是：确保文档跟代码同步更新。</strong></p>
<blockquote>
<p>参考资料：</p>
<ul>
<li><a href="http://reeze.cn/2014/02/07/what-makes-a-good-api/" target="_blank" rel="external">好的 API 设计</a></li>
<li><a href="http://www.smashingmagazine.com/2012/10/09/designing-javascript-apis-usability/" target="_blank" rel="external">Designing Better JavaScript APIs</a></li>
<li><a href="http://webstandardssherpa.com/reviews/secrets-of-awesome-javascript-api-design/" target="_blank" rel="external">Secrets of Awesome JavaScript API Design</a></li>
</ul>
</blockquote>
]]></content>
    <summary type="html">
    <![CDATA[<p>前段时间组织优化我们的原生模块 API（iOS、Android 模块封装成 JavaScript 接口），于是学习了几篇 JavaScript API 设计的文章，尽管是旧文，但受益匪浅，这里记录一下。</p>
<hr>
<p>好的 API 设计：在自描述的同时，达到抽象的目标。</p>
<p>设计良好的 API ，开发者可以快速上手，没必要经常抱着手册和文档，也没必要频繁光顾技术支持社区。</p>
<h1 id="流畅的接口">流畅的接口</h1><p><strong><a href="https://en.wikipedia.org/wiki/Method_chaining">方法链</a>：流畅易读，更易理解</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//常见的 API 调用方式：改变一些颜色，添加事件监听</span></div><div class="line"><span class="keyword">var</span> elem = <span class="built_in">document</span>.getElementById(<span class="string">"foobar"</span>);</div><div class="line">elem.style.background = <span class="string">"red"</span>;</div><div class="line">elem.style.color = <span class="string">"green"</span>;</div><div class="line">elem.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</div><div class="line">  alert(<span class="string">"hello world!"</span>);</div><div class="line">&#125;, <span class="literal">true</span>);</div><div class="line"></div><div class="line"><span class="comment">//（设想的）方法链 API</span></div><div class="line">DOMHelper.getElementById(<span class="string">'foobar'</span>)</div><div class="line">  .setStyle(<span class="string">"background"</span>, <span class="string">"red"</span>)</div><div class="line">  .setStyle(<span class="string">"color"</span>, <span class="string">"green"</span>)</div><div class="line">  .addEvent(<span class="string">"click"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</div><div class="line">    alert(<span class="string">"hello world"</span>);</div><div class="line">  &#125;);</div></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/tags/JavaScript/"/>
    
      <category term="API Design" scheme="http://jinlong.github.io/tags/API-Design/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Retina屏的移动设备如何实现真正1px的线？]]></title>
    <link href="http://jinlong.github.io/2015/05/24/css-retina-hairlines/"/>
    <id>http://jinlong.github.io/2015/05/24/css-retina-hairlines/</id>
    <published>2015-05-24T10:02:35.000Z</published>
    <updated>2015-10-29T03:07:54.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>前些日子总被人问起 iOS Retina 屏，设置 1px 边框，实际显示 2px，如何解决？<br>原来一直没在意，源于自己根本不是像素眼……<br>今天仔细瞅了瞅原生实现的边框和CSS设置的边框，确实差距不小……</p>
</blockquote>
<p>上图是原生实现，下图是 CSS 边框，手机上对比更加明显<br><img src="/image/css-retina-1px/native.jpg"><img src="/image/css-retina-1px/css.jpg"></p>
<p>然后，如何解决呢？搜遍整个谷歌，发现好多人早已开始研究解决方案了。到底有哪些方案，到底好不好用呢？试过才知道，把我试过的结论记录一下。</p>
<a id="more"></a>
<h1 id="有说用_0-5px_解决的">有说用 0.5px 解决的</h1><p>在2014年的 WWDC，“设计响应的Web体验” 一讲中，Ted O’Connor 讲到关于“retina<br> hairlines”（retina 极细的线）：在retina屏上仅仅显示1物理像素的边框，开发者应该如何处理呢。</p>
<p>他们曾介绍到 iOS 8 和 OS X Yosemite 即将支持 0.5px 的边框：</p>
<p><img src="/image/css-retina-1px/hairline.jpg"></p>
<p>额的神呐！so easy! 果真如此吗？</p>
<p>这样还不够（WWDC幻灯片通常是“唬人”的），但是相差不多。</p>
<p>问题是 retina 屏的浏览器可能不认识0.5px的边框，将会把它解释成0px，没有边框。包括 iOS 7 和 之前版本，OS X Mavericks 及以前版本，还有 Android 设备。</p>
<p><strong>解决方案</strong></p>
<p>解决方案是通过 JavaScript 检测浏览器能否处理0.5px的边框，如果可以，给<code>&lt;html&gt;</code>元素添加个<code>class</code>。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (<span class="built_in">window</span>.devicePixelRatio &amp;&amp; devicePixelRatio &gt;= <span class="number">2</span>) &#123;</div><div class="line">  <span class="keyword">var</span> testElem = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>);</div><div class="line">  testElem.style.border = <span class="string">'.5px solid transparent'</span>;</div><div class="line">  <span class="built_in">document</span>.body.appendChild(testElem);</div><div class="line">  <span class="keyword">if</span> (testElem.offsetHeight == <span class="number">1</span>)</div><div class="line">  &#123;</div><div class="line">    <span class="built_in">document</span>.querySelector(<span class="string">'html'</span>).classList.add(<span class="string">'hairlines'</span>);</div><div class="line">  &#125;</div><div class="line">  <span class="built_in">document</span>.body.removeChild(testElem);</div><div class="line">&#125;</div><div class="line"><span class="comment">// 脚本应该放在&lt;body&gt;内， 如果在&lt;head&gt;里面运行，需要包装 $(document).ready(function() &#123;   &#125;)</span></div></pre></td></tr></table></figure>
<p>然后，极细的边框样式就容易了：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">div</span> &#123;</div><div class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#bbb</span>;</div><div class="line">&#125;</div><div class="line"> </div><div class="line"><span class="selector-class">.hairlines</span> <span class="selector-tag">div</span> &#123;</div><div class="line">  <span class="attribute">border-width</span>: <span class="number">0.5px</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><strong>看起来是个解决方案，可是我要兼容安卓设备，和 iOS 8 以下设备怎么办？这个思路行不通。</strong></p>
<h1 id="还有用图片实现_border_的">还有用图片实现 border 的</h1><p>6x6 的 一张图片</p>
<p><img src="/image/css-retina-1px/border.png"></p>
<p>可以用 gif，png，或 base64 图片</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.border</span>&#123;</div><div class="line">    <span class="attribute">border-width</span>: <span class="number">1px</span>;</div><div class="line">    <span class="attribute">border-image</span>: <span class="built_in">url</span>(border.gif) <span class="number">2</span> repeat;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><strong>缺点是改边框颜色时要改图片，不是很方便。</strong></p>
<h1 id="还有用多背景渐变实现的">还有用多背景渐变实现的</h1><p>设置1px的渐变背景，50%有颜色，50%透明</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.border</span> &#123;</div><div class="line">    <span class="attribute">background</span>:</div><div class="line">    <span class="built_in">linear-gradient</span>(180deg, black, black 50%, transparent 50%) top    left  / <span class="number">100%</span> <span class="number">1px</span> no-repeat,</div><div class="line">    <span class="built_in">linear-gradient</span>(90deg,  black, black 50%, transparent 50%) top    right / <span class="number">1px</span> <span class="number">100%</span> no-repeat,</div><div class="line">    <span class="built_in">linear-gradient</span>(0,      black, black 50%, transparent 50%) bottom right / <span class="number">100%</span> <span class="number">1px</span> no-repeat,</div><div class="line">    <span class="built_in">linear-gradient</span>(-90deg, black, black 50%, transparent 50%) bottom left  / <span class="number">1px</span> <span class="number">100%</span> no-repeat;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><strong>多写了不少代码，圆角也没法实现，也是不太好用。</strong></p>
<h1 id="还有用_box-shadow_模拟边框的">还有用 box-shadow 模拟边框的</h1><figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.hairlines</span> <span class="selector-tag">li</span> &#123;</div><div class="line">    <span class="attribute">border</span>: none;</div><div class="line">    <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">1px</span> <span class="number">1px</span> -<span class="number">1px</span> <span class="built_in">rgba</span>(0, 0, 0, 0.5);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><strong>试了下不太好用，颜色不好处理，有阴影出现。</strong></p>
<h1 id="淘宝M站是通过_viewport_+_rem_实现的"><a href="http://m.taobao.com/" target="_blank" rel="external">淘宝M站</a>是通过 viewport + rem 实现的</h1><p>在<code>devicePixelRatio = 2</code> 时，输出viewport</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no"</span>&gt;</span></div></pre></td></tr></table></figure>
<p>在<code>devicePixelRatio = 3</code> 时，输出viewport</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no"</span>&gt;</span></div></pre></td></tr></table></figure>
<p>同时通过设置对应viewport的rem基准值，这种方式就可以像以前一样轻松愉快的写1px了。</p>
<p><strong>个人感觉为了做个 border 多准备两套样式，还得动态改变 viewport ，有点费劲。</strong></p>
<h1 id="最后找到一个还算好用的方法：伪类_+_transform">最后找到一个还算好用的方法：伪类  + transform</h1><p>原理是把原先元素的 border 去掉，然后利用 <code>:before</code> 或者 <code>:after</code> 重做 border ，并 transform 的 scale 缩小一半，原先的元素相对定位，新做的 border 绝对定位</p>
<ul>
<li>单条 border</li>
</ul>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.hairlines</span> <span class="selector-tag">li</span>&#123;</div><div class="line">    <span class="attribute">position</span>: relative;</div><div class="line">    <span class="attribute">border</span>:none;</div><div class="line">&#125;</div><div class="line"><span class="selector-class">.hairlines</span> <span class="selector-tag">li</span><span class="selector-pseudo">:after</span>&#123;</div><div class="line">    <span class="attribute">content</span>: <span class="string">''</span>;</div><div class="line">    <span class="attribute">position</span>: absolute;</div><div class="line">    <span class="attribute">left</span>: <span class="number">0</span>;</div><div class="line">    <span class="attribute">background</span>: <span class="number">#000</span>;</div><div class="line">    <span class="attribute">width</span>: <span class="number">100%</span>;</div><div class="line">    <span class="attribute">height</span>: <span class="number">1px</span>;</div><div class="line">    <span class="attribute">-webkit-transform</span>: <span class="built_in">scaleY</span>(0.5);</div><div class="line">            <span class="attribute">transform</span>: <span class="built_in">scaleY</span>(0.5);</div><div class="line">    <span class="attribute">-webkit-transform-origin</span>: <span class="number">0</span> <span class="number">0</span>;</div><div class="line">            <span class="attribute">transform-origin</span>: <span class="number">0</span> <span class="number">0</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>四条 border</li>
</ul>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.hairlines</span> <span class="selector-tag">li</span>&#123;</div><div class="line">    <span class="attribute">position</span>: relative;</div><div class="line">    <span class="attribute">margin-bottom</span>: <span class="number">20px</span>;</div><div class="line">    <span class="attribute">border</span>:none;</div><div class="line">&#125;</div><div class="line"><span class="selector-class">.hairlines</span> <span class="selector-tag">li</span><span class="selector-pseudo">:after</span>&#123;</div><div class="line">    <span class="attribute">content</span>: <span class="string">''</span>;</div><div class="line">    <span class="attribute">position</span>: absolute;</div><div class="line">    <span class="attribute">top</span>: <span class="number">0</span>;</div><div class="line">    <span class="attribute">left</span>: <span class="number">0</span>;</div><div class="line">    <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#000</span>;</div><div class="line">    <span class="attribute">-webkit-box-sizing</span>: border-box;</div><div class="line">    <span class="attribute">box-sizing</span>: border-box;</div><div class="line">    <span class="attribute">width</span>: <span class="number">200%</span>;</div><div class="line">    <span class="attribute">height</span>: <span class="number">200%</span>;</div><div class="line">    <span class="attribute">-webkit-transform</span>: <span class="built_in">scale</span>(0.5);</div><div class="line">    <span class="attribute">transform</span>: <span class="built_in">scale</span>(0.5);</div><div class="line">    <span class="attribute">-webkit-transform-origin</span>: left top;</div><div class="line">    <span class="attribute">transform-origin</span>: left top;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>样式使用的时候，也要结合 JS 代码，判断是否 Retina 屏</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span>(<span class="built_in">window</span>.devicePixelRatio &amp;&amp; devicePixelRatio &gt;= <span class="number">2</span>)&#123;</div><div class="line">    <span class="built_in">document</span>.querySelector(<span class="string">'ul'</span>).className = <span class="string">'hairlines'</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>可以支持圆角，唯一的一点小缺陷是 <code>&lt;td&gt;</code> 用不了。</p>
<blockquote>
<p>参考资料：</p>
<ul>
<li><a href="http://dieulot.net/css-retina-hairline" target="_blank" rel="external">CSS retina hairline, the easy way.</a></li>
<li><a href="http://www.ghugo.com/css-retina-hairline/" target="_blank" rel="external">再谈mobile web retina 下 1px 边框解决方案</a></li>
<li><a href="https://51bits.com/writing/half-point-css-borders-in-ios/" target="_blank" rel="external">Half-Point CSS Borders in iOS</a></li>
<li><a href="http://n12v.com/css-retina-and-physical-pixels/" target="_blank" rel="external">CSS, Retina, and Physical Pixels</a></li>
</ul>
</blockquote>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>前些日子总被人问起 iOS Retina 屏，设置 1px 边框，实际显示 2px，如何解决？<br>原来一直没在意，源于自己根本不是像素眼……<br>今天仔细瞅了瞅原生实现的边框和CSS设置的边框，确实差距不小……</p>
</blockquote>
<p>上图是原生实现，下图是 CSS 边框，手机上对比更加明显<br><img src="/image/css-retina-1px/native.jpg" /><img src="/image/css-retina-1px/css.jpg" /></p>
<p>然后，如何解决呢？搜遍整个谷歌，发现好多人早已开始研究解决方案了。到底有哪些方案，到底好不好用呢？试过才知道，把我试过的结论记录一下。</p>]]>
    
    </summary>
    
      <category term="CSS" scheme="http://jinlong.github.io/tags/CSS/"/>
    
      <category term="Retina" scheme="http://jinlong.github.io/tags/Retina/"/>
    
      <category term="1px线" scheme="http://jinlong.github.io/tags/1px%E7%BA%BF/"/>
    
      <category term="CSS" scheme="http://jinlong.github.io/categories/CSS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[去除 JavaScript 代码的怪味]]></title>
    <link href="http://jinlong.github.io/2015/05/21/eliminate-javascript-code-smells/"/>
    <id>http://jinlong.github.io/2015/05/21/eliminate-javascript-code-smells/</id>
    <published>2015-05-21T15:06:21.000Z</published>
    <updated>2015-10-29T03:07:36.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文：《<a href="http://elijahmanor.github.io/talks/js-smells/#/" target="_blank" rel="external">ELIMINATE JAVASCRIPT CODE SMELLS</a>》<br>作者：<a href="http://elijahmanor.com" target="_blank" rel="external">@elijahmanor</a><br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码农</a></p>
</blockquote>
<h1 id="难闻的代码">难闻的代码</h1><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/* const */</span> <span class="keyword">var</span> CONSONANTS = <span class="string">'bcdfghjklmnpqrstvwxyz'</span>;</div><div class="line"><span class="comment">/* const */</span> <span class="keyword">var</span> VOWELS = <span class="string">'aeiou'</span>;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">englishToPigLatin</span>(<span class="params">english</span>) </span>&#123;</div><div class="line">  <span class="comment">/* const */</span> <span class="keyword">var</span> SYLLABLE = <span class="string">'ay'</span>;</div><div class="line">  <span class="keyword">var</span> pigLatin = <span class="string">''</span>;</div><div class="line"></div><div class="line">  <span class="keyword">if</span> (english !== <span class="literal">null</span> &amp;&amp; english.length &gt; <span class="number">0</span> &amp;&amp;</div><div class="line">    (VOWELS.indexOf(english[<span class="number">0</span>]) &gt; <span class="number">-1</span> ||</div><div class="line">    CONSONANTS.indexOf(english[<span class="number">0</span>]) &gt; <span class="number">-1</span> )) &#123;</div><div class="line">    <span class="keyword">if</span> (VOWELS.indexOf(english[<span class="number">0</span>]) &gt; <span class="number">-1</span>) &#123;</div><div class="line">      pigLatin = english + SYLLABLE;</div><div class="line">    &#125; <span class="keyword">else</span> &#123;</div><div class="line">      <span class="keyword">var</span> preConsonants = <span class="string">''</span>;</div><div class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; english.length; ++i) &#123;</div><div class="line">        <span class="keyword">if</span> (CONSONANTS.indexOf(english[i]) &gt; <span class="number">-1</span>) &#123;</div><div class="line">          preConsonants += english[i];</div><div class="line">          <span class="keyword">if</span> (preConsonants == <span class="string">'q'</span> &amp;&amp;</div><div class="line">            i+<span class="number">1</span> &lt; english.length &amp;&amp; english[i+<span class="number">1</span>] == <span class="string">'u'</span>) &#123;</div><div class="line">            preConsonants += <span class="string">'u'</span>;</div><div class="line">            i += <span class="number">2</span>;</div><div class="line">            <span class="keyword">break</span>;</div><div class="line">          &#125;</div><div class="line">        &#125; <span class="keyword">else</span> &#123; <span class="keyword">break</span>; &#125;</div><div class="line">      &#125;</div><div class="line">      pigLatin = english.substring(i) + preConsonants + SYLLABLE;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> pigLatin;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="为毛是这个味？">为毛是这个味？</h2><a id="more"></a>
<h2 id="很多原因：">很多原因：</h2><ul>
<li>声明过多</li>
<li>嵌套太深</li>
<li>复杂度太高</li>
</ul>
<h2 id="检查工具">检查工具</h2><p><strong>Lint 规则</strong></p>
<figure class="highlight vim"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">/*jshint maxstatement<span class="variable">s:15</span>, maxdepth:<span class="number">2</span>, maxcomplexity:<span class="number">5</span> */</div><div class="line">/*jshint 最多声明:<span class="number">15</span>, 最大深度:<span class="number">2</span>, 最高复杂度:<span class="number">5</span>*/</div><div class="line"></div><div class="line">/*eslint <span class="built_in">max</span>-statement<span class="variable">s:</span>[<span class="number">2</span>, <span class="number">15</span>], <span class="built_in">max</span>-depth:[<span class="number">1</span>, <span class="number">2</span>], complexity:[<span class="number">2</span>, <span class="number">5</span>] */</div></pre></td></tr></table></figure>
<p><strong>结果</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="number">7</span>:<span class="number">0</span> - <span class="built_in">Function</span> <span class="string">'englishToPigLatin'</span> has a complexity <span class="keyword">of</span> <span class="number">7.</span></div><div class="line"><span class="comment">//englishToPigLatin 方法复杂度为 7</span></div><div class="line"><span class="number">7</span>:<span class="number">0</span> - This <span class="function"><span class="keyword">function</span> <span class="title">has</span> <span class="title">too</span> <span class="title">many</span> <span class="title">statements</span> (<span class="params"><span class="number">16</span></span>). <span class="title">Maximum</span> <span class="title">allowed</span> <span class="title">is</span> 15.</span></div><div class="line">//次方法有太多声明(<span class="params"><span class="number">16</span></span>)。最大允许值为15。</div><div class="line">22:10 - <span class="title">Blocks</span> <span class="title">are</span> <span class="title">nested</span> <span class="title">too</span> <span class="title">deeply</span> (<span class="params"><span class="number">5</span></span>).</div><div class="line">//嵌套太深(<span class="params"><span class="number">5</span></span>)</div></pre></td></tr></table></figure>
<h2 id="重构">重构</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> CONSONANTS = [<span class="string">'th'</span>, <span class="string">'qu'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'f'</span>, <span class="string">'g'</span>, <span class="string">'h'</span>, <span class="string">'j'</span>, <span class="string">'k'</span>,</div><div class="line"><span class="string">'l'</span>, <span class="string">'m'</span>, <span class="string">'n'</span>, <span class="string">'p'</span>, <span class="string">'q'</span>, <span class="string">'r'</span>, <span class="string">'s'</span>, <span class="string">'t'</span>, <span class="string">'v'</span>, <span class="string">'w'</span>, <span class="string">'x'</span>, <span class="string">'y'</span>, <span class="string">'z'</span>];</div><div class="line"><span class="keyword">const</span> VOWELS = [<span class="string">'a'</span>, <span class="string">'e'</span>, <span class="string">'i'</span>, <span class="string">'o'</span>, <span class="string">'u'</span>];</div><div class="line"><span class="keyword">const</span> ENDING = <span class="string">'ay'</span>;</div><div class="line"></div><div class="line"><span class="keyword">let</span> isValid = <span class="function"><span class="params">word</span> =&gt;</span> startsWithVowel(word) || startsWithConsonant(word);</div><div class="line"><span class="keyword">let</span> startsWithVowel = <span class="function"><span class="params">word</span> =&gt;</span> !!~VOWELS.indexOf(word[<span class="number">0</span>]);</div><div class="line"><span class="keyword">let</span> startsWithConsonant = <span class="function"><span class="params">word</span> =&gt;</span> !!~CONSONANTS.indexOf(word[<span class="number">0</span>]);</div><div class="line"><span class="keyword">let</span> getConsonants = <span class="function"><span class="params">word</span> =&gt;</span> CONSONANTS.reduce(<span class="function">(<span class="params">memo, char</span>) =&gt;</span> &#123;</div><div class="line">  <span class="keyword">if</span> (word.startsWith(char)) &#123;</div><div class="line">    memo += char;</div><div class="line">    word = word.substr(char.length);</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> memo;</div><div class="line">&#125;, <span class="string">''</span>);</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">englishToPigLatin</span>(<span class="params">english=<span class="string">''</span></span>) </span>&#123;</div><div class="line">   <span class="keyword">if</span> (isValid(english)) &#123;</div><div class="line">      <span class="keyword">if</span> (startsWithVowel(english)) &#123;</div><div class="line">        english += ENDING;</div><div class="line">      &#125; <span class="keyword">else</span> &#123;</div><div class="line">        <span class="keyword">let</span> letters = getConsonants(english);</div><div class="line">        english = <span class="string">`<span class="subst">$&#123;english.substr(letters.length)&#125;</span><span class="subst">$&#123;letters&#125;</span><span class="subst">$&#123;ENDING&#125;</span>`</span>;</div><div class="line">      &#125;</div><div class="line">   &#125;</div><div class="line">   <span class="keyword">return</span> english;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><strong>重构后统计</strong></p>
<ul>
<li>max-statements（最多声明）: 16 → 6</li>
<li>max-depth（最大嵌套）: 5 → 2</li>
<li>complexity（复杂度）: 7 → 3</li>
<li>max-len（最多行数）: 65 → 73</li>
<li>max-params（最多参数）: 1 → 2</li>
<li>max-nested-callbacks（最多嵌套回调）: 0 → 1</li>
</ul>
<h2 id="资源">资源</h2><p>jshint - <a href="jshint - http://jshint.com/">http://jshint.com/</a><br>eslint - <a href="http://eslint.org/" target="_blank" rel="external">http://eslint.org/</a><br>jscomplexity - <a href="http://jscomplexity.org/" target="_blank" rel="external">http://jscomplexity.org/</a><br>escomplex - <a href="https://github.com/philbooth/escomplex" target="_blank" rel="external">https://github.com/philbooth/escomplex</a><br>jasmine - <a href="http://jasmine.github.io/" target="_blank" rel="external">http://jasmine.github.io/</a> </p>
<h1 id="复制粘贴代码的味道">复制粘贴代码的味道</h1><p>已有功能…</p>
<p><img src="/image/js-smell/copy-paste-code-box-crop.png"></p>
<p>已有代码，BOX.js</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// ... more code ...</span></div><div class="line"></div><div class="line"><span class="keyword">var</span> boxes = <span class="built_in">document</span>.querySelectorAll(<span class="string">'.Box'</span>);</div><div class="line"></div><div class="line">[].forEach.call(boxes, <span class="function"><span class="keyword">function</span>(<span class="params">element, index</span>) </span>&#123;</div><div class="line">  element.innerText = <span class="string">"Box: "</span> + index;</div><div class="line">  element.style.backgroundColor =</div><div class="line">    <span class="string">'#'</span> + (<span class="built_in">Math</span>.random() * <span class="number">0xFFFFFF</span> &lt;&lt; <span class="number">0</span>).toString(<span class="number">16</span>);</div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">// ... more code ...</span></div></pre></td></tr></table></figure>
<p>那么，现在想要这个功能</p>
<p><img src="/image/js-smell/copy-paste-code-circle-crop.png"></p>
<p>于是，Duang!  CIRCLE.JS 就出现了…</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// ... more code ...</span></div><div class="line"></div><div class="line"><span class="keyword">var</span> circles = <span class="built_in">document</span>.querySelectorAll(<span class="string">".Circle"</span>);</div><div class="line"></div><div class="line">[].forEach.call(circles, <span class="function"><span class="keyword">function</span>(<span class="params">element, index</span>) </span>&#123;</div><div class="line">  element.innerText = <span class="string">"Circle: "</span> + index;</div><div class="line">  element.style.color =</div><div class="line">    <span class="string">'#'</span> + (<span class="built_in">Math</span>.random() * <span class="number">0xFFFFFF</span> &lt;&lt; <span class="number">0</span>).toString(<span class="number">16</span>);</div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">// ... more code ...</span></div></pre></td></tr></table></figure>
<p>为毛是这个味？因为我们复制粘贴了！</p>
<h2 id="工具">工具</h2><p><a href="https://github.com/danielstjules/jsinspect" target="_blank" rel="external">JSINSPECT</a></p>
<p>检查复制粘贴和结构相似的代码</p>
<p>一行命令：</p>
<figure class="highlight ebnf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="attribute">jsinspect</span></div></pre></td></tr></table></figure>
<p><img src="/image/js-smell/jsinspect.png"></p>
<p><a href="https://github.com/kucherenko/jscpd" target="_blank" rel="external">JSCPD</a></p>
<p>程序源码的复制/粘贴检查器</p>
<p>(JavaScript, TypeScript, C#, Ruby, CSS, SCSS, HTML, 等等都适用…)</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">jscpd <span class="_">-f</span> **/*.js <span class="_">-l</span> 1 -t 30 --languages javascript</div></pre></td></tr></table></figure>
<p><img src="/image/js-smell/jscpd.png"></p>
<h2 id="怎么能不被发现？重构">怎么能不被发现？重构</h2><p>把随机颜色部分丢出去…</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> randomColor = <span class="function"><span class="params">()</span> =&gt;</span> <span class="string">`#<span class="subst">$&#123;(<span class="built_in">Math</span>.random() * <span class="number">0xFFFFFF</span> &lt;&lt; <span class="number">0</span>).toString(<span class="number">16</span>)&#125;</span>;</span></div><div class="line"></div><div class="line">let boxes = document.querySelectorAll(".Box");</div><div class="line">[].forEach.call(boxes, (element, index) =&gt; &#123;</div><div class="line">  element.innerText = "Box: " + index;</div><div class="line">  element.style.backgroundColor = randomColor();</div><div class="line">&#125;);</div><div class="line"></div><div class="line">let circles = document.querySelectorAll(".Circle");</div><div class="line">[].forEach.call(circles, (element, index) =&gt; &#123;</div><div class="line">  element.innerText = "Circle: " + index;</div><div class="line">  element.style.color = randomColor();</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>再重构</p>
<p>再把怪异的 [].forEach.call 部分丢出去…</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> randomColor = <span class="function"><span class="params">()</span> =&gt;</span> <span class="string">`#<span class="subst">$&#123;(<span class="built_in">Math</span>.random() * <span class="number">0xFFFFFF</span> &lt;&lt; <span class="number">0</span>).toString(<span class="number">16</span>)&#125;</span>;</span></div><div class="line"></div><div class="line">let $$ = selector =&gt; [].slice.call(document.querySelectorAll(selector || '*'));</div><div class="line"></div><div class="line">$$('.Box').forEach((element, index) =&gt; &#123;</div><div class="line">  element.innerText = "Box: " + index;</div><div class="line">  element.style.backgroundColor = randomColor();</div><div class="line">&#125;);</div><div class="line"></div><div class="line">$$(".Circle").forEach((element, index) =&gt; &#123;</div><div class="line">  element.innerText = "Circle: " + index;</div><div class="line">  element.style.color = randomColor();</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>再再重构</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> randomColor = <span class="function"><span class="params">()</span> =&gt;</span> <span class="string">`#<span class="subst">$&#123;(<span class="built_in">Math</span>.random() * <span class="number">0xFFFFFF</span> &lt;&lt; <span class="number">0</span>).toString(<span class="number">16</span>)&#125;</span>;</span></div><div class="line"></div><div class="line">let $$ = selector =&gt; [].slice.call(document.querySelectorAll(selector || '*'));</div><div class="line"></div><div class="line">let updateElement = (selector, textPrefix, styleProperty) =&gt; &#123;</div><div class="line">  $$(selector).forEach((element, index) =&gt; &#123;</div><div class="line">    element.innerText = textPrefix + ': ' + index;</div><div class="line">    element.style[styleProperty] = randomColor();</div><div class="line">  &#125;);</div><div class="line">&#125;</div><div class="line"></div><div class="line">updateElement('.Box', 'Box', 'backgroundColor');</div><div class="line"></div><div class="line">updateElement('.Circle', 'Circle', 'color');</div></pre></td></tr></table></figure>
<h2 id="资源-1">资源</h2><ul>
<li><a href="https://github.com/danielstjules" target="_blank" rel="external">jsinspect</a>  </li>
<li><a href="https://github.com/kucherenko/jscpd" target="_blank" rel="external">jscpd</a>  </li>
<li><a href="http://codepen.io/elijahmanor/pen/myQebo" target="_blank" rel="external">CodePen</a> </li>
</ul>
<h1 id="switch_味道">switch 味道</h1><h2 id="难闻的代码-1">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getArea</span>(<span class="params">shape, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> area = <span class="number">0</span>;</div><div class="line"></div><div class="line">  <span class="keyword">switch</span> (shape) &#123;</div><div class="line">    <span class="keyword">case</span> <span class="string">'Triangle'</span>:</div><div class="line">      area = <span class="number">.5</span> * options.width * options.height;</div><div class="line">      <span class="keyword">break</span>;</div><div class="line"></div><div class="line">    <span class="keyword">case</span> <span class="string">'Square'</span>:</div><div class="line">      area = <span class="built_in">Math</span>.pow(options.width, <span class="number">2</span>);</div><div class="line">      <span class="keyword">break</span>;</div><div class="line"></div><div class="line">    <span class="keyword">case</span> <span class="string">'Rectangle'</span>:</div><div class="line">      area = options.width * options.height;</div><div class="line">      <span class="keyword">break</span>;</div><div class="line"></div><div class="line">    <span class="keyword">default</span>:</div><div class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Invalid shape: '</span> + shape);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> area;</div><div class="line">&#125;</div><div class="line"></div><div class="line">getArea(<span class="string">'Triangle'</span>,  &#123; <span class="attr">width</span>: <span class="number">100</span>, <span class="attr">height</span>: <span class="number">100</span> &#125;);</div><div class="line">getArea(<span class="string">'Square'</span>,    &#123; <span class="attr">width</span>: <span class="number">100</span> &#125;);</div><div class="line">getArea(<span class="string">'Rectangle'</span>, &#123; <span class="attr">width</span>: <span class="number">100</span>, <span class="attr">height</span>: <span class="number">100</span> &#125;);</div><div class="line">getArea(<span class="string">'Bogus'</span>);</div></pre></td></tr></table></figure>
<h2 id="为毛是这个味？违背“开放/封闭原则”">为毛是这个味？违背“<a href="http://en.wikipedia.org/wiki/SOLID_(object-oriented_design" target="_blank" rel="external">开放/封闭原则</a>”</h2><p>增加个新形状</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getArea</span>(<span class="params">shape, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> area = <span class="number">0</span>;</div><div class="line"></div><div class="line">  <span class="keyword">switch</span> (shape) &#123;</div><div class="line">    <span class="keyword">case</span> <span class="string">'Triangle'</span>:</div><div class="line">      area = <span class="number">.5</span> * options.width * options.height;</div><div class="line">      <span class="keyword">break</span>;</div><div class="line"></div><div class="line">    <span class="keyword">case</span> <span class="string">'Square'</span>:</div><div class="line">      area = <span class="built_in">Math</span>.pow(options.width, <span class="number">2</span>);</div><div class="line">      <span class="keyword">break</span>;</div><div class="line"></div><div class="line">    <span class="keyword">case</span> <span class="string">'Rectangle'</span>:</div><div class="line">      area = options.width * options.height;</div><div class="line">      <span class="keyword">break</span>;</div><div class="line"></div><div class="line">    <span class="keyword">case</span> <span class="string">'Circle'</span>:</div><div class="line">      area = <span class="built_in">Math</span>.PI * <span class="built_in">Math</span>.pow(options.radius, <span class="number">2</span>);</div><div class="line">      <span class="keyword">break</span>;</div><div class="line"></div><div class="line">    <span class="keyword">default</span>:</div><div class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Invalid shape: '</span> + shape);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> area;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>加点设计模式</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">shapes</span>) </span>&#123; <span class="comment">// triangle.js</span></div><div class="line">  <span class="keyword">var</span> Triangle = shapes.Triangle = <span class="function"><span class="keyword">function</span>(<span class="params">options</span>) </span>&#123;</div><div class="line">    <span class="keyword">this</span>.width = options.width;</div><div class="line">    <span class="keyword">this</span>.height = options.height;</div><div class="line">  &#125;;</div><div class="line">  Triangle.prototype.getArea = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> <span class="number">0.5</span> * <span class="keyword">this</span>.width * <span class="keyword">this</span>.height;</div><div class="line">  &#125;;  </div><div class="line">&#125;(<span class="built_in">window</span>.shapes = <span class="built_in">window</span>.shapes || &#123;&#125;));</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getArea</span>(<span class="params">shape, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> Shape = <span class="built_in">window</span>.shapes[shape], area = <span class="number">0</span>;</div><div class="line"></div><div class="line">  <span class="keyword">if</span> (Shape &amp;&amp; <span class="keyword">typeof</span> Shape === <span class="string">'function'</span>) &#123;</div><div class="line">    area = <span class="keyword">new</span> Shape(options).getArea();</div><div class="line">  &#125; <span class="keyword">else</span> &#123;</div><div class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Invalid shape: '</span> + shape);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> area;</div><div class="line">&#125;</div><div class="line"></div><div class="line">getArea(<span class="string">'Triangle'</span>,  &#123; <span class="attr">width</span>: <span class="number">100</span>, <span class="attr">height</span>: <span class="number">100</span> &#125;);</div><div class="line">getArea(<span class="string">'Square'</span>,    &#123; <span class="attr">width</span>: <span class="number">100</span> &#125;);</div><div class="line">getArea(<span class="string">'Rectangle'</span>, &#123; <span class="attr">width</span>: <span class="number">100</span>, <span class="attr">height</span>: <span class="number">100</span> &#125;);</div><div class="line">getArea(<span class="string">'Bogus'</span>);</div></pre></td></tr></table></figure>
<p>再增加新形状时</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// circle.js</span></div><div class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">shapes</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> Circle = shapes.Circle = <span class="function"><span class="keyword">function</span>(<span class="params">options</span>) </span>&#123;</div><div class="line">    <span class="keyword">this</span>.radius = options.radius;</div><div class="line">  &#125;;</div><div class="line"></div><div class="line">  Circle.prototype.getArea = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> <span class="built_in">Math</span>.PI * <span class="built_in">Math</span>.pow(<span class="keyword">this</span>.radius, <span class="number">2</span>);</div><div class="line">  &#125;;</div><div class="line"></div><div class="line">  Circle.prototype.getCircumference = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> <span class="number">2</span> * <span class="built_in">Math</span>.PI * <span class="keyword">this</span>.radius;</div><div class="line">  &#125;;</div><div class="line">&#125;(<span class="built_in">window</span>.shapes = <span class="built_in">window</span>.shapes || &#123;&#125;));</div></pre></td></tr></table></figure>
<h2 id="还有其它的味道吗？神奇的字符串">还有其它的味道吗？神奇的字符串</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getArea</span>(<span class="params">shape, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> area = <span class="number">0</span>;</div><div class="line"></div><div class="line">  <span class="keyword">switch</span> (shape) &#123;</div><div class="line">    <span class="keyword">case</span> <span class="string">'Triangle'</span>:</div><div class="line">      area = <span class="number">.5</span> * options.width * options.height;</div><div class="line">      <span class="keyword">break</span>;</div><div class="line">    <span class="comment">/* ... more code ... */</span></div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> area;</div><div class="line">&#125;</div><div class="line"></div><div class="line">getArea(<span class="string">'Triangle'</span>, &#123; <span class="attr">width</span>: <span class="number">100</span>, <span class="attr">height</span>: <span class="number">100</span> &#125;);</div></pre></td></tr></table></figure>
<p>神奇的字符串重构为对象类型</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> shapeType = &#123;</div><div class="line">  <span class="attr">triangle</span>: <span class="string">'Triangle'</span></div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getArea</span>(<span class="params">shape, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> area = <span class="number">0</span>;</div><div class="line">  <span class="keyword">switch</span> (shape) &#123;</div><div class="line">    <span class="keyword">case</span> shapeType.triangle:</div><div class="line">      area = <span class="number">.5</span> * options.width * options.height;</div><div class="line">      <span class="keyword">break</span>;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> area;</div><div class="line">&#125;</div><div class="line"></div><div class="line">getArea(shapeType.triangle, &#123; <span class="attr">width</span>: <span class="number">100</span>, <span class="attr">height</span>: <span class="number">100</span> &#125;);</div></pre></td></tr></table></figure>
<p>神奇字符重构为 CONST &amp; SYMBOLS</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> shapeType = &#123;</div><div class="line">  <span class="attr">triangle</span>: <span class="keyword">new</span> <span class="built_in">Symbol</span>()</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getArea</span>(<span class="params">shape, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> area = <span class="number">0</span>;</div><div class="line">  <span class="keyword">switch</span> (shape) &#123;</div><div class="line">    <span class="keyword">case</span> shapeType.triangle:</div><div class="line">      area = <span class="number">.5</span> * options.width * options.height;</div><div class="line">      <span class="keyword">break</span>;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> area;</div><div class="line">&#125;</div><div class="line"></div><div class="line">getArea(shapeType.triangle, &#123; <span class="attr">width</span>: <span class="number">100</span>, <span class="attr">height</span>: <span class="number">100</span> &#125;);</div></pre></td></tr></table></figure>
<h2 id="工具！？！">工具！？！</h2><p>木有 :(</p>
<p><a href="http://bit.ly/eslint-plugin-smells" target="_blank" rel="external">ESLINT-PLUGIN-SMELLS</a><br>用于 JavaScript Smells（味道） 的 ESLint 规则</p>
<p>规则</p>
<ul>
<li>no-switch - 不允许使用  switch  声明</li>
<li>no-complex-switch-case -  不允许使用复杂的  switch  声明</li>
</ul>
<h2 id="资源-2">资源</h2><ul>
<li><a href="http://codepen.io/elijahmanor/pen/OPazmm" target="_blank" rel="external">CodePen</a>  </li>
<li><a href="http://addyosmani.com/resources/essentialjsdesignpatterns/book/" target="_blank" rel="external">Addy Osmani’s JavaScript Design Patterns eBook</a>  </li>
<li><a href="http://eslint.org/" target="_blank" rel="external">ESLint</a>  </li>
<li><a href="https://github.com/elijahmanor/eslint-plugin-smells" target="_blank" rel="external">eslint-plugin-smells</a>  </li>
<li><a href="http://www.2ality.com/2015/02/es6-scoping.html" target="_blank" rel="external">ES6 Scoping</a>  </li>
<li><a href="http://www.2ality.com/2014/12/es6-symbols.html" target="_blank" rel="external">ES6 Symbols</a>  </li>
<li><a href="http://babeljs.io/docs/learn-es6" target="_blank" rel="external">Learn ES6</a></li>
</ul>
<h1 id="this_深渊的味道">this 深渊的味道</h1><h2 id="难闻的代码-2">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.teeth = [&#123; <span class="attr">clean</span>: <span class="literal">false</span> &#125;, &#123; <span class="attr">clean</span>: <span class="literal">false</span> &#125;, &#123; <span class="attr">clean</span>: <span class="literal">false</span> &#125;];</div><div class="line">&#125;;</div><div class="line"></div><div class="line">Person.prototype.brush = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> that = <span class="keyword">this</span>;</div><div class="line"></div><div class="line">  <span class="keyword">this</span>.teeth.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">tooth</span>) </span>&#123;</div><div class="line">    that.clean(tooth);</div><div class="line">  &#125;);</div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'brushed'</span>);</div><div class="line">&#125;;</div><div class="line"></div><div class="line">Person.prototype.clean = <span class="function"><span class="keyword">function</span>(<span class="params">tooth</span>) </span>&#123;</div><div class="line">  tooth.clean = <span class="literal">true</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person();</div><div class="line">person.brush();</div><div class="line"><span class="built_in">console</span>.log(person.teeth);</div></pre></td></tr></table></figure>
<h2 id="为什么是这个味？that_还是_self_还是_selfie">为什么是这个味？that 还是 self 还是 selfie</h2><p>替换方案<br>1) bind</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">Person.prototype.brush = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.teeth.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">tooth</span>) </span>&#123;</div><div class="line">    <span class="keyword">this</span>.clean(tooth);</div><div class="line">  &#125;.bind(<span class="keyword">this</span>));</div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'brushed'</span>);</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>替换方案<br>2) forEach 的第二个参数</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">Person.prototype.brush = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.teeth.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">tooth</span>) </span>&#123;</div><div class="line">    <span class="keyword">this</span>.clean(tooth);</div><div class="line">  &#125;, <span class="keyword">this</span>);</div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'brushed'</span>);</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>替换方案<br>3) ECMAScript 2015 (ES6)</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">Person.prototype.brush = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.teeth.forEach(<span class="function"><span class="params">tooth</span> =&gt;</span> &#123;</div><div class="line">    <span class="keyword">this</span>.clean(tooth);</div><div class="line">  &#125;);</div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'brushed'</span>);</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>4a) 函数式编程</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Person.prototype.brush = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.teeth.forEach(<span class="keyword">this</span>.clean);</div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'brushed'</span>);</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>4b) 函数式编程</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Person.prototype.brush = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.teeth.forEach(<span class="keyword">this</span>.clean.bind(<span class="keyword">this</span>));</div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'brushed'</span>);</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<h2 id="工具-1">工具</h2><p>ESLint</p>
<ul>
<li><a href="https://github.com/elijahmanor/eslint-plugin-smells" target="_blank" rel="external">no-this-assign</a> (eslint-plugin-smells) </li>
<li><a href="http://eslint.org/docs/rules/consistent-this" target="_blank" rel="external">consistent-this</a> </li>
<li><a href="http://eslint.org/docs/rules/no-extra-bind" target="_blank" rel="external">no-extra-bind</a></li>
</ul>
<h1 id="字符串连接的味道">字符串连接的味道</h1><h2 id="难闻的代码-3">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> build = <span class="function"><span class="keyword">function</span>(<span class="params">id, href</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> $( <span class="string">"&lt;div id='tab'&gt;&lt;a href='"</span> + href + <span class="string">"' id='"</span>+ id + <span class="string">"'&gt;&lt;/div&gt;"</span> );</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="为毛是这个味？因为字符串连接">为毛是这个味？因为字符串连接</h2><p>替换方案<br>@thomasfuchs <a href="http://mir.aculo.us/2011/03/09/little-helpers-a-tweet-sized-javascript-templating-engine/" target="_blank" rel="external">推文上的 JavaScript 模板引擎</a> </p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">t</span>(<span class="params">s, d</span>) </span>&#123;</div><div class="line">  <span class="keyword">for</span> (<span class="keyword">var</span> p <span class="keyword">in</span> d)</div><div class="line">    s = s.replace(<span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">'&#123;'</span> + p + <span class="string">'&#125;'</span>, <span class="string">'g'</span>), d[p]);</div><div class="line">  <span class="keyword">return</span> s;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">var</span> build = <span class="function"><span class="keyword">function</span>(<span class="params">id, href</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> options = &#123;</div><div class="line">    <span class="attr">id</span>: id</div><div class="line">    href: href</div><div class="line">  &#125;;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> t(<span class="string">'&lt;div id="tab"&gt;&lt;a href="&#123;href&#125;" id="&#123;id&#125;"&gt;&lt;/div&gt;'</span>, options);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>替换方案<br>2) ECMAScript 2015 (ES6) 模板字符串</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> build = <span class="function">(<span class="params">id, href</span>) =&gt;</span></div><div class="line">  <span class="string">`&lt;div id="tab"&gt;&lt;a href="<span class="subst">$&#123;href&#125;</span>" id="<span class="subst">$&#123;id&#125;</span>"&gt;&lt;/div&gt;`</span>;</div></pre></td></tr></table></figure>
<p>替换方案<br>3) ECMAScript 2015 (ES6)  模板字符串  (多行)</p>
<p>替换方案<br>4) 其它小型库或大型库/框架</p>
<ul>
<li>Lowdash 或 Underscore</li>
<li>Angular</li>
<li>React</li>
<li>Ember</li>
<li>等等…</li>
</ul>
<h2 id="工具-2">工具</h2><p>ESLINT-PLUGIN-SMELLS<br><a href="http://bit.ly/eslint-plugin-smells" target="_blank" rel="external">no-complex-string-concat</a> </p>
<h2 id="资源-3">资源</h2><p><a href="http://mir.aculo.us/2011/03/09/little-helpers-a-tweet-sized-javascript-templating-engine/" target="_blank" rel="external">Tweet Sized JavaScript Templating Engine</a> by @thomasfuchs<br>Learn ECMAScript 2015 (ES6) - <a href="http://babeljs.io/docs/learn-es6/" target="_blank" rel="external">http://babeljs.io/docs/learn-es6/</a></p>
<h1 id="jQuery_调查">jQuery 调查</h1><h2 id="难闻的代码-4">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">$(<span class="built_in">document</span>).ready(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  $(<span class="string">'.Component'</span>)</div><div class="line">    .find(<span class="string">'button'</span>)</div><div class="line">      .addClass(<span class="string">'Component-button--action'</span>)</div><div class="line">      .click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; alert(<span class="string">'HEY!'</span>); &#125;)</div><div class="line">    .end()</div><div class="line">    .mouseenter(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; $(<span class="keyword">this</span>).addClass(<span class="string">'Component--over'</span>); &#125;)</div><div class="line">    .mouseleave(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; $(<span class="keyword">this</span>).removeClass(<span class="string">'Component--over'</span>); &#125;)</div><div class="line">    .addClass(<span class="string">'initialized'</span>);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<h2 id="为毛是这个味？丧心病狂的链式调用">为毛是这个味？丧心病狂的链式调用</h2><p>愉快地重构吧</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Event Delegation before DOM Ready</span></div><div class="line">$(<span class="built_in">document</span>).on(<span class="string">'mouseenter mouseleave'</span>, <span class="string">'.Component'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  $(<span class="keyword">this</span>).toggleClass(<span class="string">'Component--over'</span>, e.type === <span class="string">'mouseenter'</span>);  </div><div class="line">&#125;);</div><div class="line"></div><div class="line">$(<span class="built_in">document</span>).on(<span class="string">'click'</span>, <span class="string">'.Component'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  alert(<span class="string">'HEY!'</span>);</div><div class="line">&#125;);</div><div class="line"></div><div class="line">$(<span class="built_in">document</span>).ready(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  $(<span class="string">'.Component button'</span>).addClass(<span class="string">'Component-button--action'</span>);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>最终 Demo</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="pvQQZw" data-default-tab="result" data-user="elijahmanor" class="codepen">See the Pen <a href="http://codepen.io/elijahmanor/pen/pvQQZw/" target="_blank" rel="external">pvQQZw</a> by Elijah Manor (<a href="http://codepen.io/elijahmanor" target="_blank" rel="external">@elijahmanor</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>

<h2 id="工具-3">工具</h2><p>ESLINT-PLUGIN-SMELLS</p>
<ul>
<li><a href="http://bit.ly/eslint-plugin-smells" target="_blank" rel="external">no-complex-chaining</a> </li>
</ul>
<h1 id="难以琢磨的计时器">难以琢磨的计时器</h1><h2 id="难闻的代码-5">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">setInterval(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'start setInterval'</span>);</div><div class="line">  someLongProcess(getRandomInt(<span class="number">2000</span>, <span class="number">4000</span>));</div><div class="line">&#125;, <span class="number">3000</span>);</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">someLongProcess</span>(<span class="params">duration</span>) </span>&#123;</div><div class="line">  setTimeout(</div><div class="line">    <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; <span class="built_in">console</span>.log(<span class="string">'long process: '</span> + duration); &#125;,</div><div class="line">    duration</div><div class="line">  );  </div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getRandomInt</span>(<span class="params">min, max</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * (max - min + <span class="number">1</span>)) + min;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="为毛这个味？无法同步的计时器">为毛这个味？无法同步的计时器</h2><p>Demo: setInterval</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="bNQmzP" data-default-tab="result" data-user="elijahmanor" class="codepen">See the Pen <a href="http://codepen.io/elijahmanor/pen/bNQmzP/" target="_blank" rel="external">bNQmzP</a> by Elijah Manor (<a href="http://codepen.io/elijahmanor" target="_blank" rel="external">@elijahmanor</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>用 setTimeout 保证顺序</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'start setTimeout'</span>)</div><div class="line">  someLongProcess(getRandomInt(<span class="number">2000</span>, <span class="number">4000</span>), <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    setTimeout(timer, <span class="number">3000</span>);</div><div class="line">  &#125;);</div><div class="line">&#125;, <span class="number">3000</span>);</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">someLongProcess</span>(<span class="params">duration, callback</span>) </span>&#123;</div><div class="line">  setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">'long process: '</span> + duration);</div><div class="line">    callback();</div><div class="line">  &#125;, duration);  </div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">/* getRandomInt(min, max) &#123;&#125; */</span></div></pre></td></tr></table></figure>
<p>Demo: setTimeout </p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="raQQay" data-default-tab="result" data-user="elijahmanor" class="codepen">See the Pen <a href="http://codepen.io/elijahmanor/pen/raQQay/" target="_blank" rel="external">raQQay</a> by Elijah Manor (<a href="http://codepen.io/elijahmanor" target="_blank" rel="external">@elijahmanor</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>

<h1 id="重复定义">重复定义</h1><h2 id="难闻的代码-6">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">data = <span class="keyword">this</span>.appendAnalyticsData(data);</div><div class="line">data = <span class="keyword">this</span>.appendSubmissionData(data);</div><div class="line">data = <span class="keyword">this</span>.appendAdditionalInputs(data);</div><div class="line">data = <span class="keyword">this</span>.pruneObject(data);</div></pre></td></tr></table></figure>
<p>替换方案<br>1) 嵌套调用函数</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">data = <span class="keyword">this</span>.pruneObject(</div><div class="line">  <span class="keyword">this</span>.appendAdditionalInputs(</div><div class="line">    <span class="keyword">this</span>.appendSubmissionData(</div><div class="line">      <span class="keyword">this</span>.appendAnalyticsData(data)</div><div class="line">    )</div><div class="line">  )</div><div class="line">);</div></pre></td></tr></table></figure>
<p>2) forEach<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> funcs = [</div><div class="line">  <span class="keyword">this</span>.appendAnalyticsData,</div><div class="line">  <span class="keyword">this</span>.appendSubmissionData,</div><div class="line">  <span class="keyword">this</span>.appendAdditionalInputs,</div><div class="line">  <span class="keyword">this</span>.pruneObject</div><div class="line">];</div><div class="line"></div><div class="line">funcs.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">func</span>) </span>&#123;</div><div class="line">  data = func(data);</div><div class="line">&#125;);</div></pre></td></tr></table></figure></p>
<p>3) reduce<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> funcs = [</div><div class="line">  <span class="keyword">this</span>.appendAnalyticsData,</div><div class="line">  <span class="keyword">this</span>.appendSubmissionData,</div><div class="line">  <span class="keyword">this</span>.appendAdditionalInputs,</div><div class="line">  <span class="keyword">this</span>.pruneObject</div><div class="line">];</div><div class="line"></div><div class="line">data = funcs.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">memo, func</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> func(memo);</div><div class="line">&#125;, data);</div></pre></td></tr></table></figure></p>
<p>4) <a href="https://lodash.com/docs#flow" target="_blank" rel="external">flow</a><br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">data = _.flow(</div><div class="line">  <span class="keyword">this</span>.appendAnalyticsData,</div><div class="line">  <span class="keyword">this</span>.appendSubmissionData,</div><div class="line">  <span class="keyword">this</span>.appendAdditionalInputs,</div><div class="line">  <span class="keyword">this</span>.pruneObject</div><div class="line">)(data);</div></pre></td></tr></table></figure></p>
<h2 id="工具-4">工具</h2><p>ESLINT-PLUGIN-SMELLS</p>
<ul>
<li><a href="http://bit.ly/eslint-plugin-smells" target="_blank" rel="external">no-reassign</a> </li>
</ul>
<h2 id="资源-4">资源</h2><ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach" target="_blank" rel="external">forEach</a>  </li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce" target="_blank" rel="external">reduce</a>  </li>
<li><a href="https://lodash.com/docs#flow" target="_blank" rel="external">_.flow</a> </li>
</ul>
<h1 id="过度耦合">过度耦合</h1><h2 id="难闻的代码-7">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">ShoppingCart</span>(<span class="params"></span>) </span>&#123; <span class="keyword">this</span>.items = []; &#125;</div><div class="line">ShoppingCart.prototype.addItem = <span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.items.push(item);</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Product</span>(<span class="params">name</span>) </span>&#123; <span class="keyword">this</span>.name = name; &#125;</div><div class="line">Product.prototype.addToCart = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  shoppingCart.addItem(<span class="keyword">this</span>);</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="keyword">var</span> shoppingCart = <span class="keyword">new</span> ShoppingCart();</div><div class="line"><span class="keyword">var</span> product = <span class="keyword">new</span> Product(<span class="string">'Socks'</span>);</div><div class="line">product.addToCart();</div><div class="line"><span class="built_in">console</span>.log(shoppingCart.items);</div></pre></td></tr></table></figure>
<h2 id="为毛是这个味？紧密耦合的依赖关系">为毛是这个味？紧密耦合的依赖关系</h2><p>如何改善！？！</p>
<ol>
<li>依赖注入</li>
<li><p>消息订阅</p>
</li>
<li><p>依赖注入 </p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">ShoppingCart</span>(<span class="params"></span>) </span>&#123; <span class="keyword">this</span>.items = []; &#125;</div><div class="line">ShoppingCart.prototype.addItem = <span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.items.push(item);</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Product</span>(<span class="params">name, shoppingCart</span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.name = name;</div><div class="line">  <span class="keyword">this</span>.shoppingCart = shoppingCart;</div><div class="line">&#125;</div><div class="line">Product.prototype.addToCart = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.shoppingCart.addItem(<span class="keyword">this</span>);</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="keyword">var</span> shoppingCart = <span class="keyword">new</span> ShoppingCart();</div><div class="line"><span class="keyword">var</span> product = <span class="keyword">new</span> Product(<span class="string">'Socks'</span>, shoppingCart);</div><div class="line">product.addToCart();</div><div class="line"><span class="built_in">console</span>.log(shoppingCart.items);</div></pre></td></tr></table></figure>
</li>
<li><p>消息订阅 </p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> channel = postal.channel();</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">ShoppingCart</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.items = [];</div><div class="line">  channel.subscribe(<span class="string">'shoppingcart.add'</span>, <span class="keyword">this</span>.addItem);</div><div class="line">&#125;</div><div class="line">ShoppingCart.prototype.addItem = <span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</div><div class="line">  <span class="keyword">this</span>.items.push(item);</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Product</span>(<span class="params">name</span>) </span>&#123; <span class="keyword">this</span>.name = name; &#125;</div><div class="line">Product.prototype.addToCart = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  channel.publish(<span class="string">'shoppingcart.add'</span>, <span class="keyword">this</span>);</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="keyword">var</span> shoppingCart = <span class="keyword">new</span> ShoppingCart();</div><div class="line"><span class="keyword">var</span> product = <span class="keyword">new</span> Product(<span class="string">'Socks'</span>);</div><div class="line">product.addToCart();</div><div class="line"><span class="built_in">console</span>.log(shoppingCart.items);</div></pre></td></tr></table></figure>
</li>
</ol>
<p>资源</p>
<ul>
<li><a href="https://github.com/postaljs/postal.js" target="_blank" rel="external">postal</a> by @ifandelse</li>
</ul>
<h1 id="连续不断的交互">连续不断的交互</h1><h2 id="难闻的代码-8">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> search = <span class="built_in">document</span>.querySelector(<span class="string">'.Autocomplete'</span>);</div><div class="line"></div><div class="line">search.addEventListener(<span class="string">'input'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  <span class="comment">// Make Ajax call for autocomplete</span></div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(e.target.value);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>Demo: 根本停不下来</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="LEXBdX" data-default-tab="result" data-user="elijahmanor" class="codepen">See the Pen <a href="http://codepen.io/elijahmanor/pen/LEXBdX/" target="_blank" rel="external">LEXBdX</a> by Elijah Manor (<a href="http://codepen.io/elijahmanor" target="_blank" rel="external">@elijahmanor</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>解决方案：节流阀<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> search = <span class="built_in">document</span>.querySelector(<span class="string">'.Autocomplete'</span>);</div><div class="line"></div><div class="line">search.addEventListener(<span class="string">'input'</span>, _.throttle(<span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  <span class="comment">// Make Ajax call for autocomplete</span></div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(e.target.value);</div><div class="line">&#125;, <span class="number">500</span>));</div></pre></td></tr></table></figure></p>
<p>Demo: </p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="azQjGj" data-default-tab="result" data-user="elijahmanor" class="codepen">See the Pen <a href="http://codepen.io/elijahmanor/pen/azQjGj/" target="_blank" rel="external">azQjGj</a> by Elijah Manor (<a href="http://codepen.io/elijahmanor" target="_blank" rel="external">@elijahmanor</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>解决方案：DEBOUNCE<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> search = <span class="built_in">document</span>.querySelector(<span class="string">'.Autocomplete'</span>);</div><div class="line"></div><div class="line">search.addEventListener(<span class="string">'input'</span>, _.debounce(<span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  <span class="comment">// Make Ajax call for autocomplete</span></div><div class="line"></div><div class="line">  <span class="built_in">console</span>.log(e.target.value);</div><div class="line">&#125;, <span class="number">500</span>));</div></pre></td></tr></table></figure></p>
<p>Demo</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="bNQjje" data-default-tab="result" data-user="elijahmanor" class="codepen">See the Pen <a href="http://codepen.io/elijahmanor/pen/bNQjje/" target="_blank" rel="external">bNQjje</a> by Elijah Manor (<a href="http://codepen.io/elijahmanor" target="_blank" rel="external">@elijahmanor</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>资源</p>
<ul>
<li><a href="https://lodash.com/" target="_blank" rel="external">lodash</a>  </li>
<li><a href="https://lodash.com/docs#throttle" target="_blank" rel="external">throttle</a> </li>
<li><a href="https://lodash.com/docs#debounce" target="_blank" rel="external">debounce</a></li>
</ul>
<h1 id="匿名函数">匿名函数</h1><h2 id="难闻的代码-9">难闻的代码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> search = <span class="built_in">document</span>.querySelector(<span class="string">'.Autocomplete'</span>);</div><div class="line"></div><div class="line">search.addEventListener(<span class="string">'input'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(e.target.value);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>给函数命名的原因：</p>
<ol>
<li>堆栈追踪</li>
<li>去关联</li>
<li><p>代码复用</p>
</li>
<li><p>堆栈追踪</p>
</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> search = <span class="built_in">document</span>.querySelector(<span class="string">'.Autocomplete'</span>);</div><div class="line"></div><div class="line">search.addEventListener(<span class="string">'input'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(e.target.value);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p><img src="/image/js-smell/06-1-a.png"></p>
<p>修改后<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> search = <span class="built_in">document</span>.querySelector(<span class="string">'.Autocomplete'</span>);</div><div class="line"></div><div class="line">search.addEventListener(<span class="string">'input'</span>, <span class="function"><span class="keyword">function</span> <span class="title">matches</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(e.target.value);</div><div class="line">&#125;);</div></pre></td></tr></table></figure></p>
<p><img src="/image/js-smell/06-1-b.png"></p>
<ol>
<li>去关联</li>
</ol>
<p>单次事件绑定<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">document</span>.querySelector(<span class="string">'button'</span>)</div><div class="line">  .addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> <span class="title">handler</span>(<span class="params"></span>) </span>&#123;</div><div class="line">      alert(<span class="string">'Ka-boom!'</span>);</div><div class="line">    <span class="keyword">this</span>.removeEventListener(<span class="string">'click'</span>, handler);</div><div class="line">  &#125;);</div></pre></td></tr></table></figure></p>
<p>Demo</p>
<p></p><p data-height="268" data-theme-id="0" data-slug-hash="PwxBxP" data-default-tab="result" data-user="elijahmanor" class="codepen">See the Pen <a href="http://codepen.io/elijahmanor/pen/PwxBxP/" target="_blank" rel="external">PwxBxP</a> by Elijah Manor (<a href="http://codepen.io/elijahmanor" target="_blank" rel="external">@elijahmanor</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>

<ol>
<li>代码复用</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> kaboom = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; alert(<span class="string">'Ka-boom'</span>); &#125;;</div><div class="line"></div><div class="line"><span class="built_in">document</span>.querySelector(<span class="string">'button'</span>).addEventListener(<span class="string">'click'</span>, kaboom);</div><div class="line"></div><div class="line"><span class="built_in">document</span>.querySelector(<span class="string">'#egg'</span>).addEventListener(<span class="string">'mouseenter'</span>, kaboom);</div></pre></td></tr></table></figure>
<h2 id="资源-5">资源</h2><ul>
<li><a href="https://developer.chrome.com/devtools" target="_blank" rel="external">Chrome DevTools</a> </li>
</ul>
<h1 id="结尾">结尾</h1><p>更多的 ESLint 规则</p>
<ul>
<li><a href="https://www.npmjs.com/package/eslint-plugin-react" target="_blank" rel="external">eslint-plugin-react</a>  </li>
<li><a href="https://www.npmjs.com/package/eslint-plugin-angular" target="_blank" rel="external">eslint-plugin-angular</a> </li>
<li><a href="https://www.npmjs.com/package/eslint-plugin-ember" target="_blank" rel="external">eslint-plugin-ember</a>  </li>
<li><a href="https://www.npmjs.com/package/eslint-plugin-backbone" target="_blank" rel="external">eslint-plugin-backbone</a> </li>
</ul>
<h2 id="资源-6">资源</h2><p>NPM 搜索  <a href="https://www.npmjs.com/search?q=eslint-plugin" target="_blank" rel="external">eslint-plugin</a>  </p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文：《<a href="http://elijahmanor.github.io/talks/js-smells/#/">ELIMINATE JAVASCRIPT CODE SMELLS</a>》<br>作者：<a href="http://elijahmanor.com">@elijahmanor</a><br>笔记：<a href="http://weibo.com/newwave">涂鸦码农</a></p>
</blockquote>
<h1 id="难闻的代码">难闻的代码</h1><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/* const */</span> <span class="keyword">var</span> CONSONANTS = <span class="string">'bcdfghjklmnpqrstvwxyz'</span>;</div><div class="line"><span class="comment">/* const */</span> <span class="keyword">var</span> VOWELS = <span class="string">'aeiou'</span>;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">englishToPigLatin</span>(<span class="params">english</span>) </span>&#123;</div><div class="line">  <span class="comment">/* const */</span> <span class="keyword">var</span> SYLLABLE = <span class="string">'ay'</span>;</div><div class="line">  <span class="keyword">var</span> pigLatin = <span class="string">''</span>;</div><div class="line"></div><div class="line">  <span class="keyword">if</span> (english !== <span class="literal">null</span> &amp;&amp; english.length &gt; <span class="number">0</span> &amp;&amp;</div><div class="line">    (VOWELS.indexOf(english[<span class="number">0</span>]) &gt; <span class="number">-1</span> ||</div><div class="line">    CONSONANTS.indexOf(english[<span class="number">0</span>]) &gt; <span class="number">-1</span> )) &#123;</div><div class="line">    <span class="keyword">if</span> (VOWELS.indexOf(english[<span class="number">0</span>]) &gt; <span class="number">-1</span>) &#123;</div><div class="line">      pigLatin = english + SYLLABLE;</div><div class="line">    &#125; <span class="keyword">else</span> &#123;</div><div class="line">      <span class="keyword">var</span> preConsonants = <span class="string">''</span>;</div><div class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; english.length; ++i) &#123;</div><div class="line">        <span class="keyword">if</span> (CONSONANTS.indexOf(english[i]) &gt; <span class="number">-1</span>) &#123;</div><div class="line">          preConsonants += english[i];</div><div class="line">          <span class="keyword">if</span> (preConsonants == <span class="string">'q'</span> &amp;&amp;</div><div class="line">            i+<span class="number">1</span> &lt; english.length &amp;&amp; english[i+<span class="number">1</span>] == <span class="string">'u'</span>) &#123;</div><div class="line">            preConsonants += <span class="string">'u'</span>;</div><div class="line">            i += <span class="number">2</span>;</div><div class="line">            <span class="keyword">break</span>;</div><div class="line">          &#125;</div><div class="line">        &#125; <span class="keyword">else</span> &#123; <span class="keyword">break</span>; &#125;</div><div class="line">      &#125;</div><div class="line">      pigLatin = english.substring(i) + preConsonants + SYLLABLE;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> pigLatin;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="为毛是这个味？">为毛是这个味？</h2>]]>
    
    </summary>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/tags/JavaScript/"/>
    
      <category term="ES6" scheme="http://jinlong.github.io/tags/ES6/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[JavaScript 错误以及如何修复]]></title>
    <link href="http://jinlong.github.io/2015/02/01/javascript-errors-and-how-to-fix-them/"/>
    <id>http://jinlong.github.io/2015/02/01/javascript-errors-and-how-to-fix-them/</id>
    <published>2015-02-01T14:09:27.000Z</published>
    <updated>2015-10-29T03:00:24.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>原文《<a href="http://davidwalsh.name/fix-javascript-errors" target="_blank" rel="external">JavaScript Errors and How to Fix Them</a>》<br>作者：Jani Hartikainen<br>翻译：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码农</a></p>
</blockquote>
<p>JavaScript 调试是一场噩梦：首先给出的错误非常难以理解，其次给出的行号不总有帮助。有个查找错误含义，及修复措施的列表，是不是很有用？</p>
<p>以下是奇怪的 JavaScript 错误列表。同样的错误，不同的浏览器会给出不同的消息，因此有一些不同的例子。</p>
<h3 id="如何读懂错误？">如何读懂错误？</h3><p>首先，让我们快速看下错误信息的结构。理解结构有助于理解错误，如果遇到列表之外的错误会减少麻烦。</p>
<p>Chrome 中典型的错误像这样：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Uncaught <span class="built_in">TypeError</span>: <span class="literal">undefined</span> is not a <span class="function"><span class="keyword">function</span></span></div></pre></td></tr></table></figure>
<p>错误的结构如下：</p>
<ol>
<li><p><strong>Uncaught TypeError：</strong>这部分信息通常不是很有用。<code>Uncaught</code> 表示错误没有被 <code>catch</code> 语句捕获，<code>TypeError</code> 是错误的名字。</p>
</li>
<li><p><strong>undefined is not a function:</strong> 这部分信息，你必须逐字阅读。比如这里表示代码尝试使用 <code>undefined</code> ，把它当做一个函数。</p>
</li>
</ol>
<p>其它基于 webkit 的浏览器，比如 Safari ，给出的错误格式跟 Chrome 很类似。Firefox 也类似，但是不总包含第一部分，最新版本的 IE 也给出比 Chrome 简单的错误 - 但是在这里，简单并不总代表好。</p>
<p>以下是真正的错误。</p>
<h3 id="Uncaught_TypeError:_undefined_is_not_a_function">Uncaught TypeError: undefined is not a function</h3><p><strong>相关错误：</strong>number is not a function, object is not a function, string is not a function, Unhandled Error: ‘foo’ is not a function, Function Expected</p>
<p>当尝试调用一个像方法的值时，这个值并不是一个方法。比如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> foo = <span class="literal">undefined</span>;</div><div class="line">foo();</div></pre></td></tr></table></figure>
<a id="more"></a>
<p>如果你尝试调用一个对象的方法时，你输错了名字，这个典型的错误很容易发生。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> x = <span class="built_in">document</span>.getElementByID(<span class="string">'foo'</span>);</div></pre></td></tr></table></figure>
<p>由于对象的属性不存在，默认是 <code>undefined</code> ，以上代码将导致这个错误。</p>
<p>尝试调用一个像方法的数字，“number is not a function” 错误出现。</p>
<p><strong>如何修复错误：</strong>确保方法名正确。这个错误的行号将指出正确的位置。</p>
<h3 id="Uncaught_ReferenceError:_Invalid_left-hand_side_in_assignment">Uncaught ReferenceError: Invalid left-hand side in assignment</h3><p><strong>相关错误：</strong>Uncaught exception: ReferenceError: Cannot assign to ‘functionCall()’, Uncaught exception: ReferenceError: Cannot assign to ‘this’</p>
<p>尝试给不能赋值的东西赋值，引起这个错误。</p>
<p>这个错误最常见的例子出现在 if 语句使用：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span>(doSomething() = <span class="string">'somevalue'</span>)</div></pre></td></tr></table></figure>
<p>此例中，程序员意外地使用了单个等号，而不是双等号。“left-hand side in assignment” 提及了等号左手边的部分，因此你可以看到以上例子，左手边包含不能赋值的东西，导致这个错误。</p>
<p><strong>如何修复错误：</strong>确保没有给函数结果赋值，或者给 <code>this</code> 关键字赋值。</p>
<h3 id="Uncaught_TypeError:_Converting_circular_structure_to_JSON">Uncaught TypeError: Converting circular structure to JSON</h3><p><strong>相关错误：</strong>Uncaught exception: TypeError: JSON.stringify: Not an acyclic Object, TypeError: cyclic object value, Circular reference in value argument not supported</p>
<p>把循环引用的对象，传给 <code>JSON.stringify</code> 总会引起错误。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = &#123; &#125;;</div><div class="line"><span class="keyword">var</span> b = &#123; <span class="attr">a</span>: a &#125;;</div><div class="line">a.b = b;</div><div class="line"><span class="built_in">JSON</span>.stringify(a);</div></pre></td></tr></table></figure>
<p>由于以上的 <code>a</code> 和 <code>b</code> 循环引用彼此，结果对象无法转换成 JSON。</p>
<p><strong>如何修复错误：</strong>移除任何想转换成 JSON 的对象中的循环引用。</p>
<h3 id="Unexpected_token_;">Unexpected token ;</h3><p><strong>相关错误：</strong>Expected ), missing ) after argument list</p>
<p>JavaScript 解释器预期的东西没有被包含。不匹配的圆括号或方括号通常引起这个错误。</p>
<p>错误信息可能有所不同 - “Unexpected token ]” 或者 “Expected {” 等。</p>
<p><strong>如何修复错误：</strong>有时错误出现的行号并不准确，因此很难修复。</p>
<ul>
<li>[ ] { } ( ) 这几个符号不配对常常导致出错。检查所有的圆括号和方括号是否配对。行号指出的不仅是问题字符。</li>
<li>Unexpected / 跟正则表达式有关。此时行号通常是正确的。</li>
<li>Unexpected ;  对象或者数组字面量里面有个 ; 通常引起这个错误，或者函数调用的参数列表里有个分号。此时的行号通常也是正确的。</li>
</ul>
<h3 id="Uncaught_SyntaxError:_Unexpected_token_ILLEGAL">Uncaught SyntaxError: Unexpected token ILLEGAL</h3><p><strong>相关错误：</strong>Unterminated String Literal, Invalid Line Terminator</p>
<p>一个字符串字面量少了结尾的引号。</p>
<p><strong>如何修复错误：</strong>确保所有的字符串都有结束的引号。</p>
<h3 id="Uncaught_TypeError:_Cannot_read_property_‘foo’_of_null,_Uncaught_TypeError:_Cannot_read_property_‘foo’_of_undefined">Uncaught TypeError: Cannot read property ‘foo’ of null, Uncaught TypeError: Cannot read property ‘foo’ of undefined</h3><p><strong>相关错误：</strong>TypeError: someVal is null, Unable to get property ‘foo’ of undefined or null reference</p>
<p>尝试读取 <code>null</code> 或者 <code>undefined</code> ，把它当成了对象。例如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> someVal = <span class="literal">null</span>;</div><div class="line"><span class="built_in">console</span>.log(someVal.foo);</div></pre></td></tr></table></figure>
<p><strong>如何修复错误：</strong>通常由于拼写错误导致。检查错误指出的行号附近使用的变量名是否正确。</p>
<h3 id="Uncaught_TypeError:_Cannot_set_property_‘foo’_of_null,_Uncaught_TypeError:_Cannot_set_property_‘foo’_of_undefined">Uncaught TypeError: Cannot set property ‘foo’ of null, Uncaught TypeError: Cannot set property ‘foo’ of undefined</h3><p><strong>相关错误：</strong>TypeError: someVal is undefined, Unable to set property ‘foo’ of undefined or null reference</p>
<p>尝试写入 <strong>null</strong> 或者 <strong>undefined</strong> ，把它当成了一个对象。例如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> someVal = <span class="literal">null</span>;</div><div class="line">someVal.foo = <span class="number">1</span>;</div></pre></td></tr></table></figure>
<p><strong>如何修复错误：</strong>也是由于拼写错误所致。检查错误指出的行号附近的变量名。</p>
<h3 id="Uncaught_RangeError:_Maximum_call_stack_size_exceeded">Uncaught RangeError: Maximum call stack size exceeded</h3><p><strong>相关错误：</strong>Related errors: Uncaught exception: RangeError: Maximum recursion depth exceeded, too much recursion, Stack overflow</p>
<p>通常由程序逻辑 bug 引起，导致函数的无限递归调用。</p>
<p><strong>如何修复错误：</strong>检查递归函数中可能导致无限循环 的 bug 。</p>
<h3 id="Uncaught_URIError:_URI_malformed">Uncaught URIError: URI malformed</h3><p><strong>相关错误：</strong>URIError: malformed URI sequence</p>
<p>无效的 decodeURIComponent 调用所致。</p>
<p><strong>如何修复错误：</strong>按照错误指出的行号，检查 <code>decodeURIComponent</code> 调用，它是正确的。</p>
<h3 id="XMLHttpRequest_cannot_load_http://some/url/-_No_‘Access-Control-Allow-Origin’_header_is_present_on_the_requested_resource">XMLHttpRequest cannot load <a href="http://some/url/" target="_blank" rel="external">http://some/url/</a>. No ‘Access-Control-Allow-Origin’ header is present on the requested resource</h3><p><strong>相关错误：</strong>Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at<br><a href="http://some/url/" target="_blank" rel="external">http://some/url/</a></p>
<p>错误肯定是使用 XMLHttpRequest 引起的。</p>
<p><strong>如何修复：</strong>确保请求的 URL 是正确的，它遵循<a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy" target="_blank" rel="external">同源策略</a> 。最好的方法是从代码中找到错误信息指出的 URL 。</p>
<h3 id="InvalidStateError:_An_attempt_was_made_to_use_an_object_that_is_not,_or_is_no_longer,_usable">InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable</h3><p><strong>相关错误：</strong>InvalidStateError, DOMException code 11</p>
<p>代码调用的方法在当前状态无法调用。通常由 <code>XMLHttpRequest</code> 引起，在方法准备完毕之前调用它会引起错误。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</div><div class="line">xhr.setRequestHeader(<span class="string">'Some-Header'</span>, <span class="string">'val'</span>);</div></pre></td></tr></table></figure>
<p>这时就会出错，因为 <code>setRequestHeader</code> 方法只能在 <code>xhr.open</code> 方法之后调用。</p>
<p><strong>如何修复：</strong>查看错误指出的行号，确保代码运行的时机正确，或者在它（例如 <strong>xhr.open</strong><br>）之前添加了不必要的调用</p>
<h3 id="结论">结论</h3><p>我看过不少无用的 JavaScript 错误，比如 PHP 中声名狼藉的异常 <code>Expected T_PAAMAYIM_NEKUDOTAYIM</code> 。抛出更熟悉的错误才更有意义。现代浏览器不再抛出完全无用的错误，才会更有帮助。</p>
<p>你看到的最令人困惑的错误是什么？在评论中分享一下呗。</p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p>原文《<a href="http://davidwalsh.name/fix-javascript-errors">JavaScript Errors and How to Fix Them</a>》<br>作者：Jani Hartikainen<br>翻译：<a href="http://weibo.com/newwave">涂鸦码农</a></p>
</blockquote>
<p>JavaScript 调试是一场噩梦：首先给出的错误非常难以理解，其次给出的行号不总有帮助。有个查找错误含义，及修复措施的列表，是不是很有用？</p>
<p>以下是奇怪的 JavaScript 错误列表。同样的错误，不同的浏览器会给出不同的消息，因此有一些不同的例子。</p>
<h3 id="如何读懂错误？">如何读懂错误？</h3><p>首先，让我们快速看下错误信息的结构。理解结构有助于理解错误，如果遇到列表之外的错误会减少麻烦。</p>
<p>Chrome 中典型的错误像这样：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Uncaught <span class="built_in">TypeError</span>: <span class="literal">undefined</span> is not a <span class="function"><span class="keyword">function</span></span></div></pre></td></tr></table></figure>
<p>错误的结构如下：</p>
<ol>
<li><p><strong>Uncaught TypeError：</strong>这部分信息通常不是很有用。<code>Uncaught</code> 表示错误没有被 <code>catch</code> 语句捕获，<code>TypeError</code> 是错误的名字。</p>
</li>
<li><p><strong>undefined is not a function:</strong> 这部分信息，你必须逐字阅读。比如这里表示代码尝试使用 <code>undefined</code> ，把它当做一个函数。</p>
</li>
</ol>
<p>其它基于 webkit 的浏览器，比如 Safari ，给出的错误格式跟 Chrome 很类似。Firefox 也类似，但是不总包含第一部分，最新版本的 IE 也给出比 Chrome 简单的错误 - 但是在这里，简单并不总代表好。</p>
<p>以下是真正的错误。</p>
<h3 id="Uncaught_TypeError:_undefined_is_not_a_function">Uncaught TypeError: undefined is not a function</h3><p><strong>相关错误：</strong>number is not a function, object is not a function, string is not a function, Unhandled Error: ‘foo’ is not a function, Function Expected</p>
<p>当尝试调用一个像方法的值时，这个值并不是一个方法。比如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> foo = <span class="literal">undefined</span>;</div><div class="line">foo();</div></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/tags/JavaScript/"/>
    
      <category term="JavaScript" scheme="http://jinlong.github.io/categories/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[JSHint 配置浅析]]></title>
    <link href="http://jinlong.github.io/2014/10/25/jshint-configuration/"/>
    <id>http://jinlong.github.io/2014/10/25/jshint-configuration/</id>
    <published>2014-10-25T03:05:14.000Z</published>
    <updated>2015-10-29T03:00:06.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p><a href="http://www.jshint.com/" target="_blank" rel="external">JSHint</a>，发现错误和潜在问题的社区驱动的工具<br><a href="http://jslinterrors.com/" target="_blank" rel="external">JSLint 错误解析</a></p>
</blockquote>
<h2 id="单独安装">单独安装</h2><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm <span class="keyword">install </span><span class="keyword">jshint </span>-g</div></pre></td></tr></table></figure>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ jshint myfile<span class="selector-class">.js</span></div><div class="line">myfile<span class="selector-class">.js</span>: line <span class="number">10</span>, col <span class="number">39</span>, Octal literals are not allowed <span class="keyword">in</span> strict mode.</div><div class="line"></div><div class="line"><span class="number">1</span> error</div></pre></td></tr></table></figure>
<h2 id="编辑器和IDE插件">编辑器和IDE插件</h2><p><a href="https://github.com/victorporof/Sublime-JSHint" target="_blank" rel="external">Sublime-JSHint</a> </p>
<h3 id="安装">安装</h3><p><code>Ctrl+Shift+P</code> 或 <code>Cmd+Shift+P</code><br>输入 <code>install</code>，选择 <code>Package Control: Install Package</code><br>输入 <code>js gutter</code>，选择 <code>JSHint Gutter</code></p>
<h3 id="使用方法：">使用方法：</h3><p>Tools -&gt; Command Palette (<code>Ctrl+Shift+P</code> 或者 <code>Cmd+Shift+P</code>) 然后输入 <code>jshint</code></p>
<p>– 或者 –</p>
<p><code>Ctrl+Shift+J</code> (或者 Mac 使用 <code>Cmd+Shift+J</code>)</p>
<p>– 或者 –</p>
<p>当前文件右键选择 JSHint -&gt; Lint Code</p>
<p>– 或者 –</p>
<p>打开 JavaScript 文件，菜单 View -&gt; Show Console，然后输入 <code>view.run_command(&quot;jshint&quot;</code>)</p>
<a id="more"></a>
<h3 id="编辑，加载或保存时自动检查">编辑，加载或保存时自动检查</h3><p>右键 -&gt; JSHint -&gt; Set Plugin Options</p>
<p>三项设置为 <code>true</code><br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    <span class="string">"lint_on_edit"</span>: <span class="literal">true</span>,</div><div class="line">    <span class="string">"lint_on_load"</span>: <span class="literal">true</span>,</div><div class="line">    <span class="string">"lint_on_save"</span>: <span class="literal">true</span></div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<h2 id="三种配置方式：">三种配置方式：</h2><p>通过 <code>--config</code> 标记手动配置</p>
<p>使用 <strong>.jshintrc</strong> 文件</p>
<p>配置放到项目的 <strong>package.json</strong> 文件里面， <strong>jshintConfig</strong> 下面</p>
<p><a href="http://www.jshint.com/docs/options/" target="_blank" rel="external">自定义 <strong>.jshintrc</strong> 配置文件</a></p>
<h2 id="JSHint_设置">JSHint 设置</h2><h3 id="强制选项">强制选项</h3><p>禁用位运算符，位运算符在 JavaScript 中使用较少，经常是把 &amp;&amp; 错输成 &amp;<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">bitwise:</span> <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>循环或者条件语句必须使用花括号包围<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">curly:</span> <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>强制使用三等号<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">eqeqeq:</span> <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>兼容低级浏览器 IE 6/7/8/9<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">es3:</span> <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>禁止重写原生对象的原型，比如 <code>Array</code>，<code>Date</code><br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">freeze:</span> <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>代码缩进<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">indent:</span> <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>禁止定义之前使用变量，忽略 <code>function</code> 函数声明<br><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">latedef:</span> <span class="string">"nofunc"</span></div></pre></td></tr></table></figure></p>
<p>构造器函数首字母大写<br><figure class="highlight haxe"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">new</span><span class="type">cap</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>禁止使用 <code>arguments.caller</code> 和 <code>arguments.callee</code>，未来会被弃用， ECMAScript 5 禁止使用 <code>arguments.callee</code><br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">noarg:</span><span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>为 <code>true</code> 时，禁止单引号和双引号混用<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"quotmark"</span>: <span class="literal">false</span></div></pre></td></tr></table></figure></p>
<p>变量未定义<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"undef"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>变量未使用<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"unused"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>严格模式<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">strict:</span><span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>最多参数个数<br><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">maxparams:</span> <span class="number">4</span></div></pre></td></tr></table></figure></p>
<p>最大嵌套深度<br><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">maxdepth:</span> <span class="number">4</span></div></pre></td></tr></table></figure></p>
<p>复杂度检测<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">maxcomplexity:</span><span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>最大行数<br><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="symbol">maxlen:</span> <span class="number">600</span></div></pre></td></tr></table></figure></p>
<h3 id="宽松选项">宽松选项</h3><p>控制“缺少分号”的警告<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="string">"asi"</span>: <span class="literal">true</span></div><div class="line"><span class="string">"boss"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>忽略 <code>debugger</code><br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"debug"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>控制 <code>eval</code> 使用警告<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"evil"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>检查一行代码最后声明后面的分号是否遗漏<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"lastsemic"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>检查不安全的折行，忽略逗号在最前面的编程风格<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"laxcomma"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>检查循环内嵌套 <code>function</code><br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"loopfunc"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>检查多行字符串<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"multistr"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>检查无效的 <code>typeof</code> 操作符值<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"notypeof"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p><code>person[&#39;name&#39;]</code> vs. <code>person.name</code><br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"sub"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p><code>new function () { ... }</code> 和 <code>new Object</code>;<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"supernew"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>在非构造器函数中使用 <code>this</code><br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"validthis"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<h3 id="环境">环境</h3><p>预定义一些全局变量</p>
<p>预定义全局变量 <code>document</code>，<code>navigator</code>，<code>FileReader</code> 等<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"browser"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>定义用于调试的全局变量：<code>console</code>，<code>alert</code><br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="string">"devel"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
<p>定义全局变量<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="string">"jquery"</span>: <span class="literal">true</span>,</div><div class="line">  <span class="string">"node"</span>: <span class="literal">true</span></div></pre></td></tr></table></figure></p>
]]></content>
    <summary type="html">
    <![CDATA[<blockquote>
<p><a href="http://www.jshint.com/">JSHint</a>，发现错误和潜在问题的社区驱动的工具<br><a href="http://jslinterrors.com/">JSLint 错误解析</a></p>
</blockquote>
<h2 id="单独安装">单独安装</h2><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm <span class="keyword">install </span><span class="keyword">jshint </span>-g</div></pre></td></tr></table></figure>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ jshint myfile<span class="selector-class">.js</span></div><div class="line">myfile<span class="selector-class">.js</span>: line <span class="number">10</span>, col <span class="number">39</span>, Octal literals are not allowed <span class="keyword">in</span> strict mode.</div><div class="line"></div><div class="line"><span class="number">1</span> error</div></pre></td></tr></table></figure>
<h2 id="编辑器和IDE插件">编辑器和IDE插件</h2><p><a href="https://github.com/victorporof/Sublime-JSHint">Sublime-JSHint</a> </p>
<h3 id="安装">安装</h3><p><code>Ctrl+Shift+P</code> 或 <code>Cmd+Shift+P</code><br>输入 <code>install</code>，选择 <code>Package Control: Install Package</code><br>输入 <code>js gutter</code>，选择 <code>JSHint Gutter</code></p>
<h3 id="使用方法：">使用方法：</h3><p>Tools -&gt; Command Palette (<code>Ctrl+Shift+P</code> 或者 <code>Cmd+Shift+P</code>) 然后输入 <code>jshint</code></p>
<p>– 或者 –</p>
<p><code>Ctrl+Shift+J</code> (或者 Mac 使用 <code>Cmd+Shift+J</code>)</p>
<p>– 或者 –</p>
<p>当前文件右键选择 JSHint -&gt; Lint Code</p>
<p>– 或者 –</p>
<p>打开 JavaScript 文件，菜单 View -&gt; Show Console，然后输入 <code>view.run_command(&quot;jshint&quot;</code>)</p>]]>
    
    </summary>
    
      <category term="JSHint" scheme="http://jinlong.github.io/tags/JSHint/"/>
    
      <category term="JSHint" scheme="http://jinlong.github.io/categories/JSHint/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Handlebars 文档笔记]]></title>
    <link href="http://jinlong.github.io/2014/10/19/handlebars-docs/"/>
    <id>http://jinlong.github.io/2014/10/19/handlebars-docs/</id>
    <published>2014-10-19T14:30:55.000Z</published>
    <updated>2015-10-29T02:59:48.000Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>官方文档：<a href="http://handlebarsjs.com/" target="_blank" rel="external">http://handlebarsjs.com/</a><br>笔记：<a href="http://weibo.com/newwave" target="_blank" rel="external">涂鸦码龙</a></p>
</blockquote>
<p>Handlebars 兼容 <a href="https://github.com/janl/mustache.js" target="_blank" rel="external">Mustache 模板</a>。</p>
<p>对比了几个 Node.js 常用模板，什么 EJS 、Jade 等等，还是感觉 Handlebars 比较顺手，模板只做数据展示，前端逻辑的东西通过 helper 实现，HTML 中没有掺杂太多 JS 的东西，看起来整洁一些。</p>
<p>Express 中引入 Handlebars 模板的话，需要引入<a href="https://github.com/donpark/hbs" target="_blank" rel="external">hbs 模块</a></p>
<hr>
<p><strong>handlebars 表达式</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>&amp;#123;&amp;#123;title&#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div></pre></td></tr></table></figure>
<p>在上下文中找 title 属性，获取它的值</p>
<p><strong>点分割表达式</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>&amp;#123;&amp;#123;article.title&#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div></pre></td></tr></table></figure>
<p>当前上下文找 article 属性，再找它的 title 属性</p>
<p><em>标识符可以是除了以下字符以外的 unicode 字符</em><br>Whitespace ! “ # % &amp; ‘ ( ) * + , . / ; &lt; = &gt; @ [ \ ] ^ ` { | } ~</p>
<a id="more"></a>
<p><strong>不合法的标识符用 “[]” 包装</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;#each articles.[10].[#comments]&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>&amp;#123;&amp;#123;subject&#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></div><div class="line">    &amp;#123;&amp;#123;body&#125;&#125;</div><div class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line">&amp;#123;&amp;#123;/each&#125;&#125;</div></pre></td></tr></table></figure>
<p><strong>不转义</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;&amp;#123;foo&#125;&#125;&#125;</div></pre></td></tr></table></figure>
<p><strong>Helpers</strong></p>
<p>0或多个参数，用空格分割，每个参数是个 handlebars 表达式</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;&amp;#123;link story&#125;&#125;&#125;</div></pre></td></tr></table></figure>
<p><em>link</em> 是 helper 名字，<em>story</em> 是 helper 参数。</p>
<p><strong>注册 helper</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'link'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">object</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Handlebars.SafeString(</div><div class="line">    <span class="string">"&lt;a href='"</span> + object.url + <span class="string">"'&gt;"</span> + object.text + <span class="string">"&lt;/a&gt;"</span></div><div class="line">  );</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>helper 返回 HTML ，不想被转义，用 <code>Handlebars.SafeString()</code>。</p>
<p>helper 把接收的上下文作为 <code>this</code> 上下文</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">ul</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;#each items&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>&amp;#123;&amp;#123;agree_button&#125;&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;/each&#125;&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></div></pre></td></tr></table></figure>
<p><strong>上下文和 helper：</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> context = &#123;</div><div class="line">  <span class="attr">items</span>: [</div><div class="line">    &#123;<span class="attr">name</span>: <span class="string">"Handlebars"</span>, <span class="attr">emotion</span>: <span class="string">"love"</span>&#125;,</div><div class="line">    &#123;<span class="attr">name</span>: <span class="string">"Mustache"</span>, <span class="attr">emotion</span>: <span class="string">"enjoy"</span>&#125;,</div><div class="line">    &#123;<span class="attr">name</span>: <span class="string">"Ember"</span>, <span class="attr">emotion</span>: <span class="string">"want to learn"</span>&#125;</div><div class="line">  ]</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'agree_button'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Handlebars.SafeString(</div><div class="line">    <span class="string">"&lt;button&gt;I agree. I "</span> + <span class="keyword">this</span>.emotion + <span class="string">" "</span> + <span class="keyword">this</span>.name + <span class="string">"&lt;/button&gt;"</span></div><div class="line">  );</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>输出结果：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">ul</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span><span class="tag">&lt;<span class="name">button</span>&gt;</span>I agree. I love Handlebars<span class="tag">&lt;/<span class="name">button</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span><span class="tag">&lt;<span class="name">button</span>&gt;</span>I agree. I enjoy Mustache<span class="tag">&lt;/<span class="name">button</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span><span class="tag">&lt;<span class="name">button</span>&gt;</span>I agree. I want to learn Ember<span class="tag">&lt;/<span class="name">button</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></div></pre></td></tr></table></figure>
<p>也可以直接传字符串参数</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;&amp;#123;link "See more..." story.url&#125;&#125;&#125;</div></pre></td></tr></table></figure>
<p>等价于</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;&amp;#123;link story.text story.url&#125;&#125;&#125;</div></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'link'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">text, url</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Handlebars.SafeString(</div><div class="line">    <span class="string">"&lt;a href='"</span> + url + <span class="string">"'&gt;"</span> + text + <span class="string">"&lt;/a&gt;"</span></div><div class="line">  );</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>helper 最后一个参数也可以接收可选的键值对序列（文档提到的 <strong>hash</strong> 参数）</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;&amp;#123;link "See more..." href=story.url class="story"&#125;&#125;&#125;</div></pre></td></tr></table></figure>
<p><strong>hash</strong> 参数的 <code>key</code> 必须是简单的标识符，<code>value</code> 是 Handlebars 表达式， <code>value</code> 可以是简单的标识符，路径，或者字符串。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'link'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">text, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> attrs = [];</div><div class="line"></div><div class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> prop <span class="keyword">in</span> options.hash) &#123;</div><div class="line">    attrs.push(prop + <span class="string">'="'</span> + options.hash[prop] + <span class="string">'"'</span>);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Handlebars.SafeString(</div><div class="line">    <span class="string">"&lt;a "</span> + attrs.join(<span class="string">" "</span>) + <span class="string">"&gt;"</span> + text + <span class="string">"&lt;/a&gt;"</span></div><div class="line">  );</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<h1 id="基础_Blocks">基础 Blocks</h1><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>&amp;#123;&amp;#123;title&#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"body"</span>&gt;</span></div><div class="line">    &amp;#123;&amp;#123;#noop&#125;&#125;&amp;#123;&amp;#123;body&#125;&#125;&amp;#123;&amp;#123;/noop&#125;&#125;</div><div class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'noop'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">options</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> options.fn(<span class="keyword">this</span>);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>noop helper 实际跟没有 helper 类似，只是传递上下文，返回字符串。Handlebars 把当前的上下文作为 <code>this</code> 。</p>
<h1 id="内建_helper">内建 helper</h1><h2 id="with_helper">with helper</h2><p>根据模板传递的上下文解析模板</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>&amp;#123;&amp;#123;title&#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;#with story&#125;&#125;</div><div class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"intro"</span>&gt;</span>&amp;#123;&amp;#123;&#123;intro&#125;&#125;&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"body"</span>&gt;</span>&amp;#123;&amp;#123;&#123;body&#125;&#125;&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;/with&#125;&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>当 JSON 对象包含嵌套属性时，不必再三重复父属性的名字。比如以下数据：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  <span class="attr">title</span>: <span class="string">"First Post"</span>,</div><div class="line">  <span class="attr">story</span>: &#123;</div><div class="line">    <span class="attr">intro</span>: <span class="string">"Before the jump"</span>,</div><div class="line">    <span class="attr">body</span>: <span class="string">"After the jump"</span></div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>helper 接收参数，参数为 JSON 属性的 上下文。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'with'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">context, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> options.fn(context);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<h2 id="简单迭代器_each_helper">简单迭代器 each helper</h2><p>Handlebars 内建了　<strong>each</strong>　迭代器</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"comments"</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;#each comments&#125;&#125;</div><div class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"comment"</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">h2</span>&gt;</span>&amp;#123;&amp;#123;subject&#125;&#125;<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></div><div class="line">      &amp;#123;&amp;#123;&#123;body&#125;&#125;&#125;</div><div class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;/each&#125;&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>实现原理如下：　把 comments 数组的每一个元素作为上下文解析模板</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'each'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">context, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">var</span> ret = <span class="string">""</span>;</div><div class="line"></div><div class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>, j=context.length; i&lt;j; i++) &#123;</div><div class="line">    ret = ret + options.fn(context[i]);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> ret;</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>可以用 <code>this</code> 引用迭代元素</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"people_list"</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;#each people&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>&amp;#123;&amp;#123;this&#125;&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;/each&#125;&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></div></pre></td></tr></table></figure>
<p>上下文：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  <span class="attr">people</span>: [</div><div class="line">    <span class="string">"Yehuda Katz"</span>,</div><div class="line">    <span class="string">"Alan Johnson"</span>,</div><div class="line">    <span class="string">"Charles Jolley"</span></div><div class="line">  ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>结果：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"people_list"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>Yehuda Katz<span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>Alan Johnson<span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>Charles Jolley<span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></div></pre></td></tr></table></figure>
<p>当某一项为空时，可以用<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;else&#125;&#125;</div></pre></td></tr></table></figure></p>
<p> 表达式</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;#each paragraphs&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>&amp;#123;&amp;#123;this&#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">&amp;#123;&amp;#123;else&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">p</span> <span class="attr">class</span>=<span class="string">"empty"</span>&gt;</span>No content<span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">&amp;#123;&amp;#123;/each&#125;&#125;</div></pre></td></tr></table></figure>
<p>通过<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;@index&#125;&#125;</div></pre></td></tr></table></figure></p>
<p> 可以引用当前的循环索引</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;#each array&#125;&#125;</div><div class="line">  &amp;#123;&amp;#123;@index&#125;&#125;: &amp;#123;&amp;#123;this&#125;&#125;</div><div class="line">&amp;#123;&amp;#123;/each&#125;&#125;</div></pre></td></tr></table></figure>
<p>用<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;@key&#125;&#125;</div><div class="line">``` </div><div class="line">引用当前的键名：</div><div class="line"></div><div class="line">```html</div><div class="line">&amp;#123;&amp;#123;#each object&#125;&#125;</div><div class="line">  &amp;#123;&amp;#123;@key&#125;&#125;: &amp;#123;&amp;#123;this&#125;&#125;</div><div class="line">&amp;#123;&amp;#123;/each&#125;&#125;</div></pre></td></tr></table></figure></p>
<p>数组迭代的第一步和最后一步用 <code>@first</code> 和 <code>@last</code> 变量表示， 对象迭代时仅 <code>@first</code> 可用。</p>
<h2 id="条件语句_if_helper">条件语句 if helper</h2><p>如果条件参数返回 <code>false</code>, <code>undefined</code>, <code>null</code>, <code>&quot;&quot;</code> 或 <code>[]</code>（非真的值）时，Handlebars 将不渲染该块</p>
<p>Handlebars 内建了 <code>if</code> 和 <code>unless</code> 语句</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;#if isActive&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">"star.gif"</span> <span class="attr">alt</span>=<span class="string">"Active"</span>&gt;</span></div><div class="line">&amp;#123;&amp;#123;/if&#125;&#125;</div></pre></td></tr></table></figure>
<p>实现原理：根据传入的条件参数，判断是否解析模板</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'if'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">conditional, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">if</span>(conditional) &#123;</div><div class="line">    <span class="keyword">return</span> options.fn(<span class="keyword">this</span>);</div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>Handlebars 还提供了 <code>else</code> 语句</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;#if isActive&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">"star.gif"</span> <span class="attr">alt</span>=<span class="string">"Active"</span>&gt;</span></div><div class="line">&amp;#123;&amp;#123;else&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">"cry.gif"</span> <span class="attr">alt</span>=<span class="string">"Inactive"</span>&gt;</span></div><div class="line">&amp;#123;&amp;#123;/if&#125;&#125;</div></pre></td></tr></table></figure>
<h2 id="unless_helper">unless helper</h2><p><code>unless</code> 跟 <code>if</code> 正好相反，如果表达式返回 false ，模板将被渲染。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;#unless license&#125;&#125;</div><div class="line">  <span class="tag">&lt;<span class="name">h3</span> <span class="attr">class</span>=<span class="string">"warning"</span>&gt;</span>WARNING: This entry does not have a license!<span class="tag">&lt;/<span class="name">h3</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;/unless&#125;&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>当 license 返回 <code>false</code>，Handlebars 将渲染 warning 。</p>
<h2 id="log_helper">log helper</h2><p>记录上下文状态</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;log "Look at me!"&#125;&#125;</div></pre></td></tr></table></figure>
<h1 id="JavaScript_编译模板">JavaScript 编译模板</h1><p>模板可以包含在特殊的 <code>&lt;script&gt;</code> 里：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">id</span>=<span class="string">"entry-template"</span> <span class="attr">type</span>=<span class="string">"text/x-handlebars-template"</span>&gt;</span><span class="undefined"></span></div><div class="line">  template content</div><div class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div></pre></td></tr></table></figure>
<p>然后用 <code>Handlebars.compile</code> 编译模板</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> source   = $(<span class="string">"#entry-template"</span>).html();</div><div class="line"><span class="keyword">var</span> template = Handlebars.compile(source);</div></pre></td></tr></table></figure>
<p>获取编译后的 HTML 模板，用 JSON 数据填充模板</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> context = &#123;<span class="attr">title</span>: <span class="string">"My New Post"</span>, <span class="attr">body</span>: <span class="string">"This is my first post!"</span>&#125;</div><div class="line"><span class="keyword">var</span> html    = template(context);</div></pre></td></tr></table></figure>
<p>最终结果：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>My New Post<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"body"</span>&gt;</span></div><div class="line">    This is my first post!</div><div class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<h2 id="HTML_转义">HTML 转义</h2><p>不想转义用 </p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;&amp;#123;</div></pre></td></tr></table></figure>
<p>模板：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>&amp;#123;&amp;#123;title&#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"body"</span>&gt;</span></div><div class="line">    &amp;#123;&amp;#123;&#123;body&#125;&#125;&#125;</div><div class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>上下文数据：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  <span class="attr">title</span>: <span class="string">"All about &lt;p&gt; Tags"</span>,</div><div class="line">  <span class="attr">body</span>: <span class="string">"&lt;p&gt;This is a post about &amp;lt;p&amp;gt; tags&lt;/p&gt;"</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>最终结果：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>All About &amp;lt;p&amp;gt; Tags<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"body"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>This is a post about &amp;lt;p&amp;gt; tags<span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p> <code>Handlebars.SafeString</code> 方法不做转义，通常返回 <code>new Handlebars.SafeString(result)</code>。此种情形，你可能想手动转义参数：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerHelper(<span class="string">'link'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">text, url</span>) </span>&#123;</div><div class="line">  text = Handlebars.Utils.escapeExpression(text);</div><div class="line">  url  = Handlebars.Utils.escapeExpression(url);</div><div class="line"></div><div class="line">  <span class="keyword">var</span> result = <span class="string">'&lt;a href="'</span> + url + <span class="string">'"&gt;'</span> + text + <span class="string">'&lt;/a&gt;'</span>;</div><div class="line"></div><div class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Handlebars.SafeString(result);</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>模板注释<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;! &#125;&#125;</div><div class="line">``` </div><div class="line">或者 </div><div class="line"></div><div class="line">```html</div><div class="line">&amp;#123;&amp;#123;!-- --&#125;&#125;</div></pre></td></tr></table></figure></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;! only output this author names if an author exists &#125;&#125;</div><div class="line">  &amp;#123;&amp;#123;#if author&#125;&#125;</div><div class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>&amp;#123;&amp;#123;firstName&#125;&#125; &amp;#123;&amp;#123;lastName&#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;/if&#125;&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<p>模板注释不会输出，HTML 注释会输出</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"entry"</span>&gt;</span></div><div class="line">  &amp;#123;&amp;#123;! This comment will not be in the output &#125;&#125;</div><div class="line">  <span class="comment">&lt;!-- This comment will be in the output --&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure>
<h2 id="Partials_局部模板">Partials 局部模板</h2><p>用<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">&amp;#123;&amp;#123;&gt; partialName&#125;&#125;</div><div class="line">``` </div><div class="line">引入局部模板，局部模板可以使字符串，也可以是编译模板的函数。</div><div class="line"></div><div class="line">```js</div><div class="line">var source = "<span class="tag">&lt;<span class="name">ul</span>&gt;</span>&amp;#123;&amp;#123;#people&#125;&#125;<span class="tag">&lt;<span class="name">li</span>&gt;</span>&amp;#123;&amp;#123;&gt; link&#125;&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span>&amp;#123;&amp;#123;/people&#125;&#125;<span class="tag">&lt;/<span class="name">ul</span>&gt;</span>";</div></pre></td></tr></table></figure></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Handlebars.registerPartial(<span class="string">'link'</span>, <span class="string">'&lt;a href="/people/&amp;#123;&amp;#123;id&#125;&#125;"&gt;&amp;#123;&amp;#123;name&#125;&#125;&lt;/a&gt;'</span>)</div><div class="line"><span class="keyword">var</span> template = Handlebars.compile(source);</div></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> data = &#123; <span class="string">"people"</span>: [</div><div class="line">    &#123; <span class="string">"name"</span>: <span class="string">"Alan"</span>, <span class="string">"id"</span>: <span class="number">1</span> &#125;,</div><div class="line">    &#123; <span class="string">"name"</span>: <span class="string">"Yehuda"</span>, <span class="string">"id"</span>: <span class="number">2</span> &#125;</div><div class="line">  ]&#125;;</div><div class="line"></div><div class="line">template(data);</div><div class="line"></div><div class="line"><span class="comment">// Should render:</span></div><div class="line"><span class="comment">// &lt;ul&gt;</span></div><div class="line"><span class="comment">//   &lt;li&gt;&lt;a href="/people/1"&gt;Alan&lt;/a&gt;&lt;/li&gt;</span></div><div class="line"><span class="comment">//   &lt;li&gt;&lt;a href="/people/2"&gt;Yehuda&lt;/a&gt;&lt;/li&gt;</span></div><div class="line"><span class="comment">// &lt;/ul&gt;</span></div></pre></td></tr></table></figure>
<h1 id="内建工具">内建工具</h1><h2 id="转义字符串">转义字符串</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Handlebars.Utils.escapeExpression(string)</div></pre></td></tr></table></figure>
<h2 id="判断空值">判断空值</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Handlebars.Utils.isEmpty(value)</div></pre></td></tr></table></figure>
<h2 id="扩展对象">扩展对象</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Handlebars.Utils.extend(foo, &#123;<span class="attr">bar</span>: <span class="literal">true</span>&#125;)</div></pre></td></tr></table></figure>
<h2 id="转字符串">转字符串</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Handlebars.Utils.toString(obj)</div></pre></td></tr></table></figure>
<h2 id="判断数组">判断数组</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Handlebars.Utils.isArray(obj)</div></pre></td></tr></table></figure>
<h2 id="判断函数">判断函数</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Handlebars.Utils.isFunction(obj)</div></pre></td></tr></table></figure>]]></content>
    <summary type="html">
    <![CDATA[前段时间项目正好用到 Handlebars ，边研究文档边翻译了下，留个备份]]>
    
    </summary>
    
      <category term="Node.js" scheme="http://jinlong.github.io/tags/Node-js/"/>
    
      <category term="Handlebars" scheme="http://jinlong.github.io/tags/Handlebars/"/>
    
      <category term="Node.js" scheme="http://jinlong.github.io/categories/Node-js/"/>
    
  </entry>
  
</feed>
