<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>XieYang-blog</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://blog.xieyangogo.cn/"/>
  <updated>2019-06-13T06:08:02.000Z</updated>
  <id>http://blog.xieyangogo.cn/</id>
  
  <author>
    <name>Oceanxy</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>React + GraphQL + apollo-client技术栈简要介绍(基于官方文档v2.5)</title>
    <link href="http://blog.xieyangogo.cn/2019/06/12/React-GraphQL-apollo-client%E6%8A%80%E6%9C%AF%E6%A0%88%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D-%E5%9F%BA%E4%BA%8E%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3v2-5/"/>
    <id>http://blog.xieyangogo.cn/2019/06/12/React-GraphQL-apollo-client技术栈简要介绍-基于官方文档v2-5/</id>
    <published>2019-06-12T08:47:17.000Z</published>
    <updated>2019-06-13T06:08:02.000Z</updated>
    
    <content type="html"><![CDATA[<p>本文档诸多例子来源于官方文档，但也在编写过程中添加了我对这套技术栈的一些理解，如果你更喜欢看官方文档，请移步<a href="https://www.apollographql.com/" target="_blank" rel="noopener">官网</a>/<a href="https://www.apollographql.com/docs/" target="_blank" rel="noopener">官方文档</a></p><blockquote><p>为什么要使用apollo？<br>没有redux繁琐的action、reducer、dispatch……让全局管理store变得简单、直白！</p><p><strong>使用<code>redux</code>管理状态，重心是放在如何去拿数据上；而<code>apollo</code>把重心放在需要什么数据上。理解这一点非常重要！</strong></p></blockquote><p>好了，废话不多说，我们立即开始！</p><a id="more"></a><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="创建React项目"><a href="#创建React项目" class="headerlink" title="创建React项目"></a>创建React项目</h3><ol><li>你可以使用<code>create-react-app</code>快速创建一个React应用，不熟悉<a href="https://facebook.github.io/create-react-app/" target="_blank" rel="noopener">create-react-app</a>的小伙伴可以先行了解。</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">npm i create-react-app -g</span><br><span class="line"></span><br><span class="line">create-react-app react-apollo-client-demo --typescript</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> react-apollo-client-demo</span><br><span class="line"></span><br><span class="line">npm start</span><br></pre></td></tr></table></figure><ol start="2"><li>也可以在<a href="https://codesandbox.io/" target="_blank" rel="noopener">codesandbox</a>上在线搭建React项目。方便快捷！</li></ol><h3 id="搭建GraphQL服务"><a href="#搭建GraphQL服务" class="headerlink" title="搭建GraphQL服务"></a>搭建GraphQL服务</h3><ol><li>你可以在github上fork <a href="https://github.com/glennreyes/graphpack" target="_blank" rel="noopener">graphpack</a>项目，然后使用<code>github账号</code>登录<code>codesandbox</code>并导入该项目，即可零配置搭建一个在线的GraphQL服务。<br> 本文档在编写时在<code>codesandbox</code>上搭建了一个服务，可供参考：<a href="https://kdvmr.sse.codesandbox.io/" target="_blank" rel="noopener">https://kdvmr.sse.codesandbox.io/</a></li><li>也可以在本地搭建自己的GraphQL服务，因不在本文档讨论范围，所以暂不提供搭建步骤。</li></ol><h3 id="安装需要的包"><a href="#安装需要的包" class="headerlink" title="安装需要的包"></a>安装需要的包</h3><p>既然本文讲的是<code>graphql</code> + <code>react</code> + <code>apollo</code>开发React App，所以需要安装以下包来支撑，以前使用的redux、react-redux等包可以丢到一边了。</p><blockquote><p>PS：在apollo 1.0时代，<code>本地状态管理</code>功能（本文档后面作了介绍）还依赖于redux等相关技术。但现在apollo已经升级到2.0时代，已经完全抛弃了redux的依赖。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install apollo-boost react-apollo graphql --save</span><br></pre></td></tr></table></figure><p>我们来看一下这三个包的作用：</p><ul><li><a href="https://www.apollographql.com/docs/react/essentials/get-started/#whats-included" target="_blank" rel="noopener">apollo-boost</a>：包含设置Apollo Client所需的核心包。如果你需要按自己的意愿定制化项目，可以自行选择安装单独的包：<br>  — <code>apollo-client</code>：apollo客户端包<ul><li><code>apollo-cache-inmemory</code>：官方推荐的缓存包</li><li><code>apollo-link-http</code>：用于获取远程数据的包</li><li><code>apollo-link-error</code>：用于处理错误的包</li><li><code>apollo-link-state</code>：本地状态管理的包（2.5版本已集成到<code>apollo-client</code>）</li></ul></li><li><a href="https://www.apollographql.com/docs/react/api/react-apollo/" target="_blank" rel="noopener">react-apollo</a>：react的图层集成（用react的组件方式来使用apollo）</li><li><a href="https://graphql.cn/" target="_blank" rel="noopener">graphql</a>：解析GraphQL查询</li></ul><h3 id="实例化Apollo客户端"><a href="#实例化Apollo客户端" class="headerlink" title="实例化Apollo客户端"></a>实例化Apollo客户端</h3><p>需要注意一点的是<code>apollo-boost</code>和<code>apollo-client</code>都提供了<code>ApolloClient</code>，但是两者需要的参数有一点差别。具体见各自API：</p><ul><li><code>apollo-boost</code>导出的Apollo Client对象（详细<a href="https://www.apollographql.com/docs/react/essentials/get-started/#configuration-options" target="_blank" rel="noopener">API</a>）：集成官方核心功能的一个大集合对象</li><li><code>apollo-client</code>导出的Apollo Client对象（详细<a href="https://www.apollographql.com/docs/react/api/apollo-client/#apolloclient" target="_blank" rel="noopener">API</a>）：默认为App所在的<code>同一主机</code>上的<code>GraphQL端点</code>，<br>  要自定义<code>uri</code>还需引入<code>apollo-link-http</code>包。如果你使用的是<code>apollo v1.x</code>，可直接从<code>apollo-client</code>包内导出<code>createNetworkInterface</code>方法，用法请见<a href="https://github.com/apollographql/apollo-link/tree/master/packages/apollo-link-http#upgrading-from-apollo-fetch--apollo-client" target="_blank" rel="noopener">1.x迁移至2.x指南</a></li></ul><p>我们看一下使用<code>apollo-boost</code>实例化Apollo客户端：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> ApolloClient <span class="keyword">from</span> <span class="string">'apollo-boost'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">    <span class="comment">// 如果你实在找不到现成的服务端，可以使用apollo官网提供的：https://48p1r2roz4.sse.codesandbox.io或者本教程的服务：https://kdvmr.sse.codesandbox.io/</span></span><br><span class="line">    uri: <span class="string">'你的GraphQL服务链接'</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>以及使用<code>apollo-client</code>实例化Apollo客户端：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> ApolloClient <span class="keyword">from</span> <span class="string">'apollo-client'</span></span><br><span class="line"><span class="keyword">import</span> &#123; createHttpLink &#125; <span class="keyword">from</span> <span class="string">'apollo-link-http'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  link: createHttpLink(&#123; </span><br><span class="line">    uri: <span class="string">'你的GraphQL服务链接'</span> </span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="编写GraphQL查询语句"><a href="#编写GraphQL查询语句" class="headerlink" title="编写GraphQL查询语句"></a>编写GraphQL查询语句</h3><blockquote><p>如果对<code>GraphQL</code>语法不是很了解，请先移步<a href="https://juejin.im/post/5b5545a0e51d4518e3119933" target="_blank" rel="noopener">graphQL基础实践</a></p></blockquote><p>为了演示GraphQL查询，我们暂且使用普通的请求看一段代码：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; gql &#125; <span class="keyword">from</span> <span class="string">'apollo-boost'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 实例化 Apollo 客户端</span></span><br><span class="line"></span><br><span class="line">client.query(&#123;</span><br><span class="line">  query: gql<span class="string">`</span></span><br><span class="line"><span class="string">    &#123;</span></span><br><span class="line"><span class="string">      rates(currency: "CNY") &#123;</span></span><br><span class="line"><span class="string">        currency</span></span><br><span class="line"><span class="string">      &#125;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  `</span></span><br><span class="line">&#125;).then(<span class="function"><span class="params">result</span> =&gt;</span> <span class="built_in">console</span>.log(result));</span><br></pre></td></tr></table></figure><p>除了从<code>apollo-boost</code>导入<code>gql</code>，你还可以从<code>graphql-tag</code>这个包导入：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line">gql<span class="string">`...`</span></span><br></pre></td></tr></table></figure><p>显而易见，gql()的作用是把查询字符串解析成查询文档。</p><h3 id="连接Apollo客户端到React"><a href="#连接Apollo客户端到React" class="headerlink" title="连接Apollo客户端到React"></a>连接Apollo客户端到React</h3><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> &#123; ApolloProvider &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> App: React = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;ApolloProvider client=&#123;client&#125;&gt;</span><br><span class="line">      &lt;div&gt;App content&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>ApolloProvider&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> App</span><br></pre></td></tr></table></figure><blockquote><p>ApolloProvider（详细<a href="https://www.apollographql.com/docs/react/api/react-apollo/#apolloprovider" target="_blank" rel="noopener">API</a>）有一个必需参数<code>client</code>。</p></blockquote><p>和redux一样（redux使用<code>&lt;Provider/&gt;</code>组件包裹React App），react-apollo需要<code>&lt;ApolloProvider /&gt;</code>组件来包裹整个React App，以便将实例化的<code>client</code>放到上下文中，就可以在组件树的任何位置访问到它。<br>另外，还可以使用<code>withApollo</code>来包裹组件，以在组件内部获取到<code>client实例</code>（还有很多获取实例的方法，文档后面有介绍），<br>详情请参考<a href="https://www.apollographql.com/docs/react/api/react-apollo/#withapollocomponent" target="_blank" rel="noopener">withApollo()</a>。</p><h2 id="Query组件与Mutation组件"><a href="#Query组件与Mutation组件" class="headerlink" title="Query组件与Mutation组件"></a>Query组件与Mutation组件</h2><p>在graphql中，<code>query</code>操作代表<code>查询</code>，mutation操作代表增、删和改，他们对应<code>REST API</code>的GET与POST请求，但要注意在实际的请求过程中Query或许并不是<code>GET</code>请求，这里只是为了方便大家理解做的假设！</p><h3 id="获取数据——Query组件"><a href="#获取数据——Query组件" class="headerlink" title="获取数据——Query组件"></a>获取数据——<a href="https://www.apollographql.com/docs/react/essentials/queries/" target="_blank" rel="noopener">Query组件</a></h3><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Query &#125; <span class="keyword">from</span> <span class="string">"react-apollo"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; gql &#125; <span class="keyword">from</span> <span class="string">"apollo-boost"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ExchangeRates = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">  &lt;Query</span><br><span class="line">    query=&#123;gql<span class="string">`</span></span><br><span class="line"><span class="string">      &#123;</span></span><br><span class="line"><span class="string">        rates(currency: "USD") &#123;</span></span><br><span class="line"><span class="string">          currency</span></span><br><span class="line"><span class="string">          rate</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;</span></span><br><span class="line"><span class="string">    `</span>&#125;</span><br><span class="line">  &gt;</span><br><span class="line">    &#123;</span><br><span class="line">      (&#123; loading, error, data &#125;) =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (loading) <span class="keyword">return</span> &lt;p&gt;Loading...&lt;<span class="regexp">/p&gt;;</span></span><br><span class="line"><span class="regexp">        if (error) return &lt;p&gt;Error :(&lt;/</span>p&gt;;</span><br><span class="line">    </span><br><span class="line">        <span class="keyword">return</span> data.rates.map(<span class="function">(<span class="params">&#123; currency, rate &#125;</span>) =&gt;</span> (</span><br><span class="line">          &lt;div key=&#123;currency&#125;&gt;</span><br><span class="line">            &lt;p&gt;&#123;currency&#125;: &#123;rate&#125;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">          &lt;/</span>div&gt;</span><br><span class="line">        ));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &lt;<span class="regexp">/Query&gt;</span></span><br><span class="line"><span class="regexp">);</span></span><br></pre></td></tr></table></figure><p>恭喜，你刚刚创建了第一个React Query组件！🎉🎉🎉</p><p>代码解析：</p><p>可以看到，在Query组件内，有一个<code>匿名函数</code>，这个匿名函数有一些参数，最常用的有：<code>loading</code>，<code>error</code>，<code>data</code>。<br>它们分别代表组件的加载状态、组件的加载错误提示、以及组件加载到的数据。</p><blockquote><p>Query组件是从<code>react-apollo</code>导出的<code>React组件</code>，它使用<code>render prop</code>模式与UI共享GraphQL数据。（即我们可以从组件的props获取到GraphQL查询返回的数据）<br>Query组件还有很多其他props，上面就展示了一个query属性，其他的如：</p><ul><li><code>children</code>(根据查询结果显示要渲染的UI)</li><li><code>variables</code>（用来传递查询参数到gql()）</li><li><code>skip</code>（跳过这个查询，比如登录时，验证失败，我们使用skip跳过这个查询，则登录失败）</li></ul><p>更多props详见<a href="https://www.apollographql.com/docs/react/essentials/queries/#query-api-overview" target="_blank" rel="noopener">Query API</a></p></blockquote><h3 id="更新数据——Mutation组件"><a href="#更新数据——Mutation组件" class="headerlink" title="更新数据——Mutation组件"></a>更新数据——<a href="https://www.apollographql.com/docs/react/essentials/mutations/" target="_blank" rel="noopener">Mutation组件</a></h3><p>更新数据包括新增、修改和删除，这些操作统一使用Mutation组件。<br>Mutation组件和Query组件一样，使用<code>render prop</code>模式，但props有差别，<a href="https://www.apollographql.com/docs/react/essentials/mutations/#mutation-api-overview" target="_blank" rel="noopener">Mutation API</a></p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Mutation &#125; <span class="keyword">from</span> <span class="string">"react-apollo"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ADD_TODO = gql<span class="string">`</span></span><br><span class="line"><span class="string">  mutation AddTodo($type: String!) &#123;</span></span><br><span class="line"><span class="string">    addTodo(type: $type) &#123;</span></span><br><span class="line"><span class="string">      id</span></span><br><span class="line"><span class="string">      type</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> AddTodo = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> input;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;Mutation mutation=&#123;ADD_TODO&#125;&gt;</span><br><span class="line">      &#123;</span><br><span class="line">        (addTodo, &#123; data &#125;) =&gt; (</span><br><span class="line">          &lt;div&gt;</span><br><span class="line">            &lt;form</span><br><span class="line">              onSubmit=&#123;<span class="function"><span class="params">e</span> =&gt;</span> &#123;</span><br><span class="line">                e.preventDefault();</span><br><span class="line">                addTodo(&#123; variables: &#123; <span class="keyword">type</span>: input.value &#125; &#125;);</span><br><span class="line">                input.value = <span class="string">""</span>;</span><br><span class="line">              &#125;&#125;</span><br><span class="line">            &gt;</span><br><span class="line">              &lt;input</span><br><span class="line">                ref=&#123;<span class="function"><span class="params">node</span> =&gt;</span> &#123;</span><br><span class="line">                  input = node;</span><br><span class="line">                &#125;&#125;</span><br><span class="line">              /&gt;</span><br><span class="line">              &lt;button <span class="keyword">type</span>=<span class="string">"submit"</span>&gt;Add Todo&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">            &lt;/</span>form&gt;</span><br><span class="line">          &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">        )</span></span><br><span class="line"><span class="regexp">      &#125;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>Mutation&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我们来梳理一下代码：</p><ul><li>首先，创建用于<code>mutation</code>（突变）的GraphQL，mutation需要一个字符串类型的参数<code>type</code>。它将用于mutation的GraphQL语句包装在<code>gql</code>方法中，并将其传递给<code>Mutation组件</code>的<code>props</code>。</li><li><code>Mutation组件</code>内需要一个<code>匿名函数</code>作为<code>子函数</code>（也称为render prop函数），同Query组件，但参数有差异。</li><li><code>render prop函数</code>的第一个参数是<code>Mutation组件</code>内部定义的<code>mutate()</code>函数。为了提高代码可读性，这里取名为<code>addTodo</code>。<br>  也可以直接用<code>“mutate”</code>表示mutate函数，通过调用它来告诉<code>Apollo Client</code>，接下来要触发mutation（即触发提交表单的POST请求，在onSubmit事件里面可以看见addTodo函数被调用了）。</li><li><code>render prop函数</code>的第二个参数是一个对象，这个对象有多个属性，包括data（mutation的结果，POST请求的返回值）、loading（加载状态）和error（加载过程中的错误信息），同<code>Query组件</code>。</li></ul><blockquote><p><code>mutate函数</code>（也就是上面命名的<code>addTodo</code>函数）可选地接受变量，如：</p><ul><li>optimisticResponse</li><li>refetchQueries和update（这些函数就是后面用来更新缓存的）</li><li>ignoreResults：忽略mutation操作返回的结果（即忽略POST请求的返回值）</li></ul><p>你也可以将这些值作为<code>props</code>传递给<code>Mutation组件</code>。详细的介绍请移步<a href="https://www.apollographql.com/docs/react/api/react-apollo/#graphql-options-for-mutations" target="_blank" rel="noopener">mutate函数 API</a></p></blockquote><p>到这里，我们能发出客户端请求，也能得到服务器返回的结果，那接下来就着手怎么处理这些数据，然后渲染到UI上。我们看一下<code>redux</code>在这一步是怎么处理的：</p><ul><li>dispatch触发数据请求</li><li>reducer根据之前定义的action处理得到的新数据，把数据保存到store中</li><li>react-redux的connect连接store与React组件</li><li>mapStateToProps/mapDisToProps完成render prop。</li></ul><p>以上步骤，全靠一行一行的代码手动实现，我们再来看一下apollo是怎么处理的：</p><ul><li>cache.writeQuery()</li></ul><p>没错，你没看错，就是这一个API，搞定以上<code>redux</code>需要一大堆代码才能完成的数据更新！writeQuery相当于通过一种方式来告诉Apollo Client：<br><code>我们已经成功发出POST请求并得到了返回的结果了，现在把结果给你，你更新一下本地的缓存吧！</code><br>并且如果你的数据写得很规范（呃，其实它叫<code>范式化</code>，不要急，后面有介绍），甚至连这一句话都不用写，当你执行query或mutation后，UI便会自动根据新的数据更新UI！！</p><h3 id="更新缓存——mutation后内部自动query"><a href="#更新缓存——mutation后内部自动query" class="headerlink" title="更新缓存——mutation后内部自动query"></a>更新缓存——mutation后内部自动query</h3><p>有时，当执行mutation时，GraphQL服务器和Apollo缓存会变得不同步。当执行的更新取决于本地缓存中已有的数据时，会发生这种情况。例如，本地缓存了一张列表，<br>当删除列表中的一项或添加一项新的数据，当我们执行mutation后，graphql服务端和本地缓存不一致，我们需要一种方法来告诉Apollo Client去更新项目列表的查询，<br>以获取我们mutation后新的项目列表数据；又或者我们仅仅使用mutation提交一张表单，本地并没有缓存这张表单的数据，所以我们并不需要新的查询来更新本地缓存。</p><p>下面来看一段代码：</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Mutation &#125; <span class="keyword">from</span> <span class="string">"react-apollo"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ADD_TODO = gql<span class="string">`</span></span><br><span class="line"><span class="string">  mutation AddTodo($type: String!) &#123;</span></span><br><span class="line"><span class="string">    addTodo(type: $type) &#123;</span></span><br><span class="line"><span class="string">      id</span></span><br><span class="line"><span class="string">      type</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> GET_TODOS = gql<span class="string">`</span></span><br><span class="line"><span class="string">  query GetTodos &#123;</span></span><br><span class="line"><span class="string">    todos</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> AddTodo = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> input;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;Mutation</span><br><span class="line">      mutation=&#123;ADD_TODO&#125;</span><br><span class="line">      update=&#123;<span class="function">(<span class="params">cache, &#123; data: &#123; addTodo &#125; &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> &#123; todos &#125; = cache.readQuery(&#123; query: GET_TODOS &#125;);</span><br><span class="line">        cache.writeQuery(&#123;</span><br><span class="line">          query: GET_TODOS,</span><br><span class="line">          data: &#123; todos: todos.concat([addTodo]) &#125;,</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;&#125;</span><br><span class="line">    &gt;</span><br><span class="line">      &#123;<span class="function"><span class="params">addTodo</span> =&gt;</span> (</span><br><span class="line">        &lt;div&gt;</span><br><span class="line">          &lt;form</span><br><span class="line">            onSubmit=&#123;<span class="function"><span class="params">e</span> =&gt;</span> &#123;</span><br><span class="line">              e.preventDefault();</span><br><span class="line">              addTodo(&#123; variables: &#123; <span class="keyword">type</span>: input.value &#125; &#125;);</span><br><span class="line">              input.value = <span class="string">""</span>;</span><br><span class="line">            &#125;&#125;</span><br><span class="line">          &gt;</span><br><span class="line">            &lt;input</span><br><span class="line">              ref=&#123;<span class="function"><span class="params">node</span> =&gt;</span> &#123;</span><br><span class="line">                input = node;</span><br><span class="line">              &#125;&#125;</span><br><span class="line">            /&gt;</span><br><span class="line">            &lt;button <span class="keyword">type</span>=<span class="string">"submit"</span>&gt;Add Todo&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">          &lt;/</span>form&gt;</span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      )&#125;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>Mutation&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>通过这段代码可以看见，<code>update()</code>函数可以作为props传递给Mutation组件，但它也可以作为prop传递给mutate函数，即：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 借用上面的mutate（重命名为addTodo）函数来举例</span></span><br><span class="line">addTodo(&#123;</span><br><span class="line">  variables: &#123; <span class="keyword">type</span>: input.value &#125;,</span><br><span class="line">  update: <span class="function">(<span class="params">cache, data: &#123; addTodo &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><blockquote><p><strong><code>update</code>: (cache: DataProxy, mutationResult: FetchResult)</strong>：用于在发生突变（mutation）后更新缓存</p><p>参数：</p><ul><li><code>cache</code>，这个参数详细讲又可以讲几节课，所以这里只简单介绍一下，详细<a href="https://www.apollographql.com/docs/react/advanced/caching/#inmemorycache" target="_blank" rel="noopener">API</a><ul><li>cache通常是<code>InMemoryCache</code>的一个实例，在创建Apollo Client时提供给Apollo Client的构造函数（怎么创建的Apollo Client？请返回<a href="#创建一个apollo客户端">创建一个apollo客户端</a>复习一下）</li><li><code>InMemoryCache</code>来自于一个单独的包<code>apollo-cache-inmemory</code>。如果你使用<code>apollo-boost</code>，这个包已经被包含在里面了，无需重复安装。</li><li>cache有几个实用函数，例如<code>cache.readQuery</code>和<code>cache.writeQuery</code>，它们允许您使用GraphQL读取和写入缓存。</li><li>另外还有其他的方法，例如<code>cache.readFragment</code>，<code>cache.writeFragment</code>和<code>cache.writeData</code>，详细<a href="(https://www.apollographql.com/docs/react/advanced/caching/#inmemorycache">API</a>)。</li></ul></li><li><code>mutationResult</code>，一个对象，对象里面的data属性保存着执行mutation后的结果（POST请求后得到的数据），详细<a href="https://www.apollographql.com/docs/react/essentials/mutations/#render-prop-function" target="_blank" rel="noopener">API</a><ul><li>如果指定<a href="https://www.apollographql.com/docs/react/features/optimistic-ui/" target="_blank" rel="noopener">乐观响应</a>，则会更新两次<code>update</code>函数：一次是乐观结果，另一次是实际结果。</li><li>您可以使用您的变异结果来使用<code>cache.writeQuery</code>更新缓存。</li></ul></li></ul><p>对于<code>update函数</code>，当你在其内部调用<code>cache.writeQuery</code>时，更新操作会触发Apollo内部的<code>广播查询</code>（broadcastQueries），而广播查询又会触发缓存中与本次mutation相关的数据的自动更新——自动使用受影响组件的GraphQL进行查询并更新UI。<br>因此当执行mutation后，我们不必手动去执行相关组件的查询，Apollo Client在内部已经做好了所有工作，这区别于redux在dispatch后所做的一切处理数据的工作。</p><p>有时，update函数不需要为所有mutation更新缓存（比如提交了一张表单）。所以，Apollo提供单独的<code>cache.writeQuery</code>方法，来触发相关缓存的查询，以更新本地缓存。<br>所以需要注意：<code>仅仅只在update函数内部调用cache.writeQuery()才会触发广播行为</code>。在其他任何地方，cache.writeQuery只会写入缓存，并且所做的更改不会广播到视图层。<br>为了避免给代码造成混淆，推荐在未使用update函数时，使用Apollo Client实例对象<code>client</code>的<code>client.writeQuery</code>方法将数据写入缓存。</p></blockquote><p>解析代码：</p><p>由于我们需要更新显示TODOS列表的查询，因此首先使用<code>cache.readQuery</code>从缓存中读取数据。<br>然后，我们将mutation后得到的新todo与现有todo列表合并起来，并使用<code>cache.writeQuery</code>将查询到的数据写回缓存。<br>既然我们已经指定了一个<code>update函数</code>，那么一旦新的todo从服务器返回，我们的用户界面就会用它进行响应性更新（广播给其他与此缓存数据有关的组件的GraphQL查询，让他们及时更新更新相关缓存到UI上）。</p><blockquote><p>Apollo还提供一种的方法来及时地修改本地缓存以快速渲染UI并触发相关缓存的查询，待查询返回新的数据后再真正更新本地缓存，详见<a href="https://www.apollographql.com/docs/react/features/optimistic-ui/" target="_blank" rel="noopener">乐观更新</a>。</p><p>基于<code>乐观UI</code>，如果您运行相同的查询两次，则不会看到加载指示符（Apollo Client返回的loading字段）。apollo会检测当前的请求参数是否变化，然后判断是否向服务器发送新的请求。</p></blockquote><h3 id="Apollo范式化缓存-API"><a href="#Apollo范式化缓存-API" class="headerlink" title="Apollo范式化缓存 API"></a>Apollo范式化缓存 <a href="https://www.apollographql.com/docs/react/advanced/caching/#normalization" target="_blank" rel="noopener">API</a></h3><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Mutation, Query &#125; <span class="keyword">from</span> <span class="string">"react-apollo"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> UPDATE_TODO = gql<span class="string">`</span></span><br><span class="line"><span class="string">  mutation UpdateTodo($id: String!, $type: String!) &#123;</span></span><br><span class="line"><span class="string">    updateTodo(id: $id, type: $type) &#123;</span></span><br><span class="line"><span class="string">      id</span></span><br><span class="line"><span class="string">      type</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意：这里通过graphql得到的todos数据是一个包含id和type字段的对象的数组，与 UPDATE_TODO 里面的字段（主要是id）对应</span></span><br><span class="line"><span class="keyword">const</span> GET_TODOS = gql<span class="string">`</span></span><br><span class="line"><span class="string">  query GetTodos &#123;</span></span><br><span class="line"><span class="string">    todos</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Todos = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">  &lt;Query query=&#123;GET_TODOS&#125;&gt;</span><br><span class="line">    &#123;<span class="function">(<span class="params">&#123; loading, error, data &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (loading) <span class="keyword">return</span> &lt;p&gt;Loading...&lt;<span class="regexp">/p&gt;;</span></span><br><span class="line"><span class="regexp">      if (error) return &lt;p&gt;Error :(&lt;/</span>p&gt;;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> data.todos.map(<span class="function">(<span class="params">&#123; id, <span class="keyword">type</span> &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> input;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> (</span><br><span class="line">          &lt;Mutation mutation=&#123;UPDATE_TODO&#125; key=&#123;id&#125;&gt;</span><br><span class="line">            &#123;<span class="function"><span class="params">updateTodo</span> =&gt;</span> (</span><br><span class="line">              &lt;div&gt;</span><br><span class="line">                &lt;p&gt;&#123;<span class="keyword">type</span>&#125;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">                &lt;form</span></span><br><span class="line"><span class="regexp">                  onSubmit=&#123;e =&gt; &#123;</span></span><br><span class="line"><span class="regexp">                    e.preventDefault();</span></span><br><span class="line"><span class="regexp">                    updateTodo(&#123; variables: &#123; id, type: input.value &#125; &#125;);</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">                    input.value = "";</span></span><br><span class="line"><span class="regexp">                  &#125;&#125;</span></span><br><span class="line"><span class="regexp">                &gt;</span></span><br><span class="line"><span class="regexp">                  &lt;input</span></span><br><span class="line"><span class="regexp">                    ref=&#123;node =&gt; &#123;</span></span><br><span class="line"><span class="regexp">                      input = node;</span></span><br><span class="line"><span class="regexp">                    &#125;&#125;</span></span><br><span class="line"><span class="regexp">                  /</span>&gt;</span><br><span class="line">                  &lt;button <span class="keyword">type</span>=<span class="string">"submit"</span>&gt;Update Todo&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">                &lt;/</span>form&gt;</span><br><span class="line">              &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">            )&#125;</span></span><br><span class="line"><span class="regexp">          &lt;/</span>Mutation&gt;</span><br><span class="line">        );</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;&#125;</span><br><span class="line">  &lt;<span class="regexp">/Query&gt;</span></span><br><span class="line"><span class="regexp">);</span></span><br></pre></td></tr></table></figure><p>注意：这一次在<code>mutate函数</code>（这里命名为<code>updateTodo</code>）里并没有调用update函数，在也没有传递update函数给Mutation组件，但是UI会立即更新。这就是范式化缓存的魅力了。</p><blockquote><p><code>范式化缓存</code>——<code>InMemoryCache</code>在将数据保存到存储之前对数据进行范式化，方法是将结果拆分为单个对象，为每个对象创建唯一标识符，并将这些对象存储在展平的数据结构中（创建的唯一标识符为这些对象的键，成为缓存键）。<br>默认情况下，<code>InMemoryCache</code>将尝试使用常见的<code>id</code>和<code>_id的主键</code>作为唯一标识符（如果它们与对象上的<code>__typename</code>字段一起存在）。</p><p><img src="/images/React-GraphQL-apollo-client/ROOT_QUERY.png" alt=""></p><p><img src="/images/React-GraphQL-apollo-client/id&amp;typename.png" alt=""></p><p>如果未指定<code>id</code>和<code>_id</code>，或者未指定<code>__typename</code>，则<code>InMemoryCache</code>将按照查询到对象的层级关系依次回退到根查询为止。</p><p><img src="/images/React-GraphQL-apollo-client/__typename.png" alt=""></p><p>例如<code>ROOT_QUERY.allPeople.0</code>将作为数据中<code>allPeople[0]</code>对象的缓存键（cache key）被存储到缓存的根查询（ROOT_QUERY）下。（在展平的数据结构中，所有对象都在<code>ROOT_QUERY</code>下）：</p><p><code>即使我们不打算在我们的UI中使用mutation返回的结果，我们仍然需要返回更新的ID和属性</code>，以便我们的UI进行自动更新。</p></blockquote><p>以上代码中，我们不需要指定<code>update函数</code>，因为TODOS查询将使用缓存中更新的TODO数据自动重建查询结果。</p><blockquote><p>结合上一节介绍的<code>update函数</code>那样——并非每次mutation都需要使用update函数——其原因就是依据Apollo Cache的范式化数据结构，<br>在尽量减少手动操作数据的情况下自动更新UI，当前后端都规范化数据后（特别是唯一标识符<code>id</code>的统一, <code>__typename</code>字段的定义），<br>在<code>query</code>或<code>mutation</code>操作后，我们几乎不用手动处理数据，就能实现UI的自动更新。</p><p>例如：如果只需要更新缓存里面的单条数据，只需要返回这条数据的ID和要更新的属性即可，这种情况下通常不需要使用update函数。</p></blockquote><p>如果想要自定义唯一标识符，即不用默认的<code>ID</code>来生成缓存键，可以使用<code>InMemoryCache</code>构造函数的<code>dataIdFromObject</code>函数：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cache = <span class="keyword">new</span> InMemoryCache(&#123;</span><br><span class="line">  dataIdFromObject: <span class="function"><span class="params">object</span> =&gt;</span> object.key || <span class="literal">null</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>在指定自定义dataIdFromObject时，Apollo Client不会将类型名称添加到缓存键，因此，如果您的<code>ID</code>在所有对象中不唯一，则可能需要在<code>dataIdFromObject</code>中包含<code>__typename</code>。</p><p>在谷歌浏览器中安装<a href="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm" target="_blank" rel="noopener">apollo devtools</a>扩展(需要科学上网)，可以清晰看到这种范式化缓存的存储状态。</p><h2 id="中场休息"><a href="#中场休息" class="headerlink" title="中场休息"></a>中场休息</h2><p><strong>使用<code>redux</code>管理状态，重心是放在如何去拿数据上；而<code>apollo</code>把重心放在需要什么数据上。理解这一点非常重要！</strong></p><p>还记得这句话吗？我们在本文档开篇的时候介绍过。现在理解了吗？现在，我们回过头来梳理一下自己学到的知识点：</p><ul><li>当学习了怎样去获取数据（query）以及更新数据和修改数据（mutation）后，原来Apollo和React结合，原来组件可以这么简单的与数据交互！</li><li>当学习了Apollo缓存后，我们对Apollo数据存储的理解又上升了一个台阶，把所有查询回来的对象一一拆分，通过唯一标识符的形式把一个深层级的对象展平，直观展现在缓存的根查询中。</li><li>当学习了Apollo的范式化缓存后，我们才知道，原来自动更新UI可以如此优雅！我们甚至不需要管理数据，只需按照规范传递数据即可！</li></ul><h2 id="本地状态管理-详情"><a href="#本地状态管理-详情" class="headerlink" title="本地状态管理 详情"></a>本地状态管理 <a href="https://www.apollographql.com/docs/react/essentials/local-state/" target="_blank" rel="noopener">详情</a></h2><p>上半场我们接触了<code>本地与服务端</code>的远程数据交互，接下来，我们将进入<code>本地的状态管理</code>。</p><p>Apollo Client在<code>2.5版本</code>具有内置的本地状态处理功能，允许将本地数据与远程数据一起存储在Apollo缓存中。要访问本地数据，只需使用<code>GraphQL查询</code>即可。</p><p>而在<code>2.5版本之前</code>，如果想要使用本地状态管理，必须引入已经废弃的一个包<code>apollo-link-state</code>(<a href="https://www.apollographql.com/docs/link/links/state/#quick-start" target="_blank" rel="noopener">API</a>)，<br>这个包在2.5版本已被废弃，因为从2.5版本开始，这个包的功能已经集成到apollo的核心之中，不再额外维护一个单独的包。而在apollo的<code>1.x版本</code>，如果要实现本地状态管理，依然得引入redux。</p><p>Apollo Client有两种主要方法可以执行局部状态突变：</p><ul><li>第一种方法是通过调用<code>cache.writeData</code>直接写入缓存。<br>  在<a href="#更新缓存——mutation后内部自动query">更新缓存</a>那一节，我们已经详细介绍过<code>cache.writeData</code>的用法，以及其余<code>update函数</code>的搭配使用。</li><li>第二种方法是创建一个带有GraphQL突变（mutation）的Mutation组件，该组件调用本地客户端解析器（resolvers）。<br>  如果mutation依赖于缓存中的现有值，我们建议使用解析器（resolvers，后面两节将介绍，目前只需知道它的存在，它和<code>apollo-server</code>端的resolver完全相同）。</li></ul><h3 id="直接写入缓存"><a href="#直接写入缓存" class="headerlink" title="直接写入缓存"></a>直接写入缓存</h3><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; ApolloConsumer &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> Link <span class="keyword">from</span> <span class="string">'./Link'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> FilterLink = <span class="function">(<span class="params">&#123; filter, children &#125;</span>) =&gt;</span> (</span><br><span class="line">  &lt;ApolloConsumer&gt;</span><br><span class="line">    &#123;<span class="function"><span class="params">client</span> =&gt;</span> (</span><br><span class="line">      &lt;Link</span><br><span class="line">        onClick=&#123;<span class="function"><span class="params">()</span> =&gt;</span> client.writeData(&#123; data: &#123; visibilityFilter: filter &#125; &#125;)&#125;</span><br><span class="line">      &gt;</span><br><span class="line">        &#123;children&#125;</span><br><span class="line">      &lt;<span class="regexp">/Link&gt;</span></span><br><span class="line"><span class="regexp">    )&#125;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>ApolloConsumer&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure><blockquote><p>Apollo在<code>ApolloConsumer组件</code>(<a href="https://www.apollographql.com/docs/react/api/react-apollo/#apolloconsumer" target="_blank" rel="noopener">API</a>)或<code>Query组件</code>的render prop中注入了Apollo Client实例，<br>所以当使用这些组件时，我们可以直接从组件的<code>props</code>中拿到<code>client实例</code>。</p></blockquote><p>直接写入缓存不需要GraphQL<code>mutate</code>函数或<code>resolvers</code>函数。因此我们在上面的代码中没有使用它们，我们直接在<code>onClick</code>事件函数里面调用<code>client.writeData</code>来写入缓存。</p><p>但是只建议将<code>直接写入缓存</code>用于简单写入，例如写入字符串或一次性写入。<br>重要的是要注意直接写入并不是作为<code>GraphQL突变</code>实现的，因此不应将它们包含在复杂的开发模式之中。<br>它也不会验证你写入缓存的数据是否为有效GraphQL数据的结构。<br>如果以上提到的任何一点对您很重要，则应选择使用本地<code>resolvers</code>。</p><h3 id="client-指令"><a href="#client-指令" class="headerlink" title="@client 指令"></a>@client 指令</h3><p>上一节提到过，<code>Query组件</code>的render prop同样包含client实例。所以配合<code>@client</code>指令，我们可以在<code>Query组件</code>中轻松地从<code>cache</code>或<code>resolvers</code>获取本地状态。<br>或许换个方式介绍大家能理解得更透彻：配合<code>@client</code>指令，我们可以在<code>Query组件</code>中轻松地从<code>cache</code>获取本地状态，或者通过<code>resolvers</code>从<code>cache</code>获取本地状态。</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Query &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span>;</span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> Link <span class="keyword">from</span> <span class="string">'./Link'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> GET_VISIBILITY_FILTER = gql<span class="string">`</span></span><br><span class="line"><span class="string">  &#123;</span></span><br><span class="line"><span class="string">    visibilityFilter @client</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> FilterLink = <span class="function">(<span class="params">&#123; filter, children &#125;</span>) =&gt;</span> (</span><br><span class="line">  &lt;Query query=&#123;GET_VISIBILITY_FILTER&#125;&gt;</span><br><span class="line">    &#123;<span class="function">(<span class="params">&#123; data, client &#125;</span>) =&gt;</span> (</span><br><span class="line">      &lt;Link</span><br><span class="line">        onClick=&#123;<span class="function"><span class="params">()</span> =&gt;</span> client.writeData(&#123; data: &#123; visibilityFilter: filter &#125; &#125;)&#125;</span><br><span class="line">        active=&#123;data.visibilityFilter === filter&#125;</span><br><span class="line">      &gt;</span><br><span class="line">        &#123;children&#125;</span><br><span class="line">      &lt;<span class="regexp">/Link&gt;</span></span><br><span class="line"><span class="regexp">    )&#125;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>Query&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>代码解读：</p><p><code>@client</code>指令告诉Apollo Client在本地获取数据（cache或resolvers），而不是将其发送到graphql服务器。<br>在调用<code>client.writeData</code>后，<code>render prop函数</code>上的查询结果将自动更新。同时所有缓存的写入和读取都是同步的，所以不必担心加载状态（loading）。</p><h3 id="本地解析器——resolvers"><a href="#本地解析器——resolvers" class="headerlink" title="本地解析器——resolvers"></a>本地解析器——<a href="https://www.apollographql.com/docs/react/essentials/local-state/#local-resolvers" target="_blank" rel="noopener">resolvers</a></h3><p>终于见到了你——resolvers！前面几节都一笔带过了<code>resolvers</code>（解析器），现在，我们来了解它到底有什么强大的功能。</p><p>如果要依赖本地状态实现<code>GraphQL的突变</code>，我们只需要在本地resolvers对象中<code>指定一个与之对应的函数</code>即可。<br>在Apollo Client的实例化中，<code>resolvers</code>映射为一个<code>对象</code>，这个对象中保存着每一个用于本地突变的<code>resolver函数</code>。<br>当在GraphQL的字段上找到<code>@client</code>指令时，Apollo Client会在<code>resolvers对象</code>中寻找与之对应的<code>resolver函数</code>，这个对应关系是通过<code>resolvers的键</code>来关联的。</p><blockquote><p>即：当执行没有加<code>@client</code>指令的查询或突变时，<code>GraphQL</code>文档中的字段已预定义在了服务端，所以我们只需在查询或突变时按照服务端定义的字段编写<code>GraphQL</code>文档即可；<br>当加上@<code>client</code>指令后，Apollo Client不会向服务端发送请求，转而在自己内部寻找GraphQL文档内指定的字段。但是，我们怎么去访问本地的这些字段呢？或许他们根本就不存在。<br>(关于<code>@client</code>的运作方式，请参考官方文档中关于——<a href="https://www.apollographql.com/docs/react/essentials/local-state/#local-data-query-flow" target="_blank" rel="noopener">本地数据查询流程</a>的部分，由于篇幅原因，本文档不再详细介绍。)<br>这时，我们就需要自己定义可以访问这些字段的方式——resolver，在解析器对象（resolvers）中定义一个解析函数（resolver），以供GraphQL查询或突变在使用了<code>@client</code>指令时调用，<br>这样就建立了GraphQL查询或突变与Apollo Client之间的联系，通过这个函数可以解析有<code>@client</code>指令控制的查询或突变，因此这个函数被命名为解析函数，意指从本地解析函数中寻找GraphQL文档中指定的字段的值。</p></blockquote><p>那解析函数定义在哪里呢？</p><p>其实它在ApolloClient的构造函数中，也就是说我们实例化Apollo Client时，需要传递resolvers给它。</p><blockquote><p>解析器，它和<code>client-server</code>的resolver函数完全相同：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt;   fieldName: <span class="function">(<span class="params">obj, args, context, info</span>) =&gt;</span> result;</span><br><span class="line">&gt;</span><br></pre></td></tr></table></figure></blockquote><blockquote><p><code>obj {object}</code>: 包含父字段上resolver函数返回的结果的对象，或者为DOM树最顶层的查询或突变的<code>ROOT_QUERY</code>对象<br><code>args {object}</code>: 包含传递到GraphQL文档中的所有参数的对象。例如，如果使用<code>updateNetworkStatus（isConnected：true）</code>触发查询或突变，则<code>args</code>为<code>{isConnected：true}</code>。<br><code>context {object}</code>: React组件与Apollo Client网络堆栈之间共享的上下文信息的对象。除了可能存在的任何自定义context属性外，本地resolvers始终会收到以下内容：</p><ul><li><code>context.client</code>: Apollo Client的实例</li><li><code>context.cache</code>: Apollo Cache的实例<br>  context.cache.readQuery, .writeQuery, .readFragment, .writeFragment, and .writeData: 一系列用于操作cache的<a href="https://www.apollographql.com/docs/react/essentials/local-state/#managing-the-cache" target="_blank" rel="noopener">API</a></li><li><code>context.getCacheKey</code>: 使用<code>__typename</code>和<code>id</code>从cache中获取<code>key</code>。<br><code>info {object}</code>: 有关查询执行状态的信息。实际中，你可能永远也不会使用到这个参数。</li></ul></blockquote><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; ApolloClient &#125; <span class="keyword">from</span> <span class="string">'apollo-client'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; InMemoryCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-inmemory'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  cache: <span class="keyword">new</span> InMemoryCache(),</span><br><span class="line">  resolvers: &#123;</span><br><span class="line">    Mutation: &#123;</span><br><span class="line">      toggleTodo: <span class="function">(<span class="params">_root, variables, &#123; cache, getCacheKey &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> id = getCacheKey(&#123; __typename: <span class="string">'TodoItem'</span>, id: variables.id &#125;)</span><br><span class="line">        <span class="keyword">const</span> fragment = gql<span class="string">`</span></span><br><span class="line"><span class="string">          fragment completeTodo on TodoItem &#123;</span></span><br><span class="line"><span class="string">            completed</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        `</span>;</span><br><span class="line">        <span class="keyword">const</span> todo = cache.readFragment(&#123; fragment, id &#125;);</span><br><span class="line">        <span class="keyword">const</span> data = &#123; ...todo, completed: !todo.completed &#125;;</span><br><span class="line">        cache.writeData(&#123; id, data &#125;);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>代码解析：</p><p>为了切换todo的状态，首先需要查询缓存以找出todo当前状态的内容，然后通过使用<code>cache.readFragment</code>从缓存中读取片段来实现此目的。<br>此函数采用<code>fragment和id</code>，它对应于<code>item</code>的缓存键（<code>cache key</code>）。我们通过调用<code>context</code>中的<code>getCacheKey</code>并传入项目的<code>__typename</code>和<code>id</code>来获取缓存键。</p><p>一旦读取了fragment，就可以切换todo的已完成状态并将更新的数据写回缓存。由于我们不打算在UI中使用mutation的返回结果，因此我们返回<code>null</code>，因为默认情况下所有GraphQL类型都可以为空。</p><p>下面，我们来看一下怎么调用这个toggleTodo解析函数（触发toggleTodo突变）：</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Mutation &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span>;</span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TOGGLE_TODO = gql<span class="string">`</span></span><br><span class="line"><span class="string">  mutation ToggleTodo($id: Int!) &#123;</span></span><br><span class="line"><span class="string">    toggleTodo(id: $id) @client</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Todo = <span class="function">(<span class="params">&#123; id, completed, text &#125;</span>) =&gt;</span> (</span><br><span class="line">  &lt;Mutation mutation=&#123;TOGGLE_TODO&#125; variables=&#123;&#123; id &#125;&#125;&gt;</span><br><span class="line">    <span class="comment">// 特别注意，此toggleTodo非解析器里面的toggleTodo，这个toggleTodo是我们之前介绍过的mutate函数，这里被更名为‘toggleTodo’而已，不要混淆了</span></span><br><span class="line">    &#123;<span class="function"><span class="params">toggleTodo</span> =&gt;</span> (</span><br><span class="line">      &lt;li</span><br><span class="line">        onClick=&#123;toggleTodo&#125;</span><br><span class="line">        style=&#123;&#123;</span><br><span class="line">          textDecoration: completed ? <span class="string">'line-through'</span> : <span class="string">'none'</span>,</span><br><span class="line">        &#125;&#125;</span><br><span class="line">      &gt;</span><br><span class="line">        &#123;text&#125;</span><br><span class="line">      &lt;<span class="regexp">/li&gt;</span></span><br><span class="line"><span class="regexp">    )&#125;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>Mutation&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>代码解析：</p><p>首先，我们创建一个GraphQL突变文档，它将我们想要切换的item的id作为唯一的参数。我们通过使用<code>@client</code>指令标记<code>GraphQL</code>的<code>toggleTodo</code>字段来指示这是一个本地突变。<br>这将告诉Apollo Client调用我们本地突变解析器（resolvers）里面的<code>toggleTodo</code>解析函数来解析该字段。然后，我们创建一个<code>Mutation组件</code>，就像我们操作远程突变一样。<br>最后，将GraphQL突变传递给组件，并在<code>render prop函数</code>的UI中触发它。</p><h3 id="查询本地状态"><a href="#查询本地状态" class="headerlink" title="查询本地状态"></a>查询本地状态</h3><p>查询本地数据与查询GraphQL服务器非常相似。唯一的区别是本地查询在字段上添加了<code>@client</code>指令，以指示它们应该从Apollo Client cache或resolvers中解析。</p><p>我们来看一个例子：</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Query &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span>;</span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> Todo <span class="keyword">from</span> <span class="string">'./Todo'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> GET_TODOS = gql<span class="string">`</span></span><br><span class="line"><span class="string">  &#123;</span></span><br><span class="line"><span class="string">    todos @client &#123;</span></span><br><span class="line"><span class="string">      id</span></span><br><span class="line"><span class="string">      completed</span></span><br><span class="line"><span class="string">      text</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">    visibilityFilter @client</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TodoList = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">  &lt;Query query=&#123;GET_TODOS&#125;&gt;</span><br><span class="line">    &#123;</span><br><span class="line">      (&#123; data: &#123; todos, visibilityFilter &#125; &#125;) =&gt; (</span><br><span class="line">        &lt;ul&gt;</span><br><span class="line">          &#123;</span><br><span class="line">            getVisibleTodos(todos, visibilityFilter).map(<span class="function"><span class="params">todo</span> =&gt;</span> (</span><br><span class="line">              &lt;Todo key=&#123;todo.id&#125; &#123;...todo&#125; /&gt;</span><br><span class="line">            ))</span><br><span class="line">          &#125;</span><br><span class="line">        &lt;<span class="regexp">/ul&gt;</span></span><br><span class="line"><span class="regexp">      )</span></span><br><span class="line"><span class="regexp">    &#125;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>Query&gt;</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>代码解析：</p><p>创建GraphQL查询并将<code>@client</code>指令添加到GraphQL文档的todos和visibilityFilter字段。<br>然后，我们将查询传递给<code>Query组件</code>。<code>@client</code>指令让<code>Query组件</code>知道应该从Apollo Client缓存中提取todos和visibilityFilter，或者使用预定义的本地resolver解析。</p><p>由于上面的查询在安装组件后立即运行，如果cache中没有item或者没有定义任何本地resolver，我们该怎么办？</p><p>我们需要在运行查询之前将初始状态写入缓存，以防止错误输出。</p><h3 id="初始化本地状态"><a href="#初始化本地状态" class="headerlink" title="初始化本地状态"></a>初始化本地状态</h3><p>通常，我们需要将初始状态写入缓存，以便在触发mutation之前查询数据的所有组件都不会出错。<br>要实现此目的，可以使用<code>cache.writeData</code>为初始值准备缓存。</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; ApolloClient &#125; <span class="keyword">from</span> <span class="string">'apollo-client'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; InMemoryCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-inmemory'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cache = <span class="keyword">new</span> InMemoryCache();</span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  cache,</span><br><span class="line">  resolvers: &#123; <span class="comment">/* ... */</span> &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">cache.writeData(&#123;</span><br><span class="line">  data: &#123;</span><br><span class="line">    todos: [],</span><br><span class="line">    visibilityFilter: <span class="string">'SHOW_ALL'</span>,</span><br><span class="line">    networkStatus: &#123;</span><br><span class="line">      __typename: <span class="string">'NetworkStatus'</span>,</span><br><span class="line">      isConnected: <span class="literal">false</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>注意：Apollo v2.4和v2.5写入初始本地状态的方式不一样，详情参考<a href="https://www.apollographql.com/docs/link/links/state/#defaults" target="_blank" rel="noopener">官方API</a>。</p><h3 id="重置本地状态-缓存"><a href="#重置本地状态-缓存" class="headerlink" title="重置本地状态/缓存"></a>重置本地状态/缓存</h3><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; ApolloClient &#125; <span class="keyword">from</span> <span class="string">'apollo-client'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; InMemoryCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-inmemory'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cache = <span class="keyword">new</span> InMemoryCache();</span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  cache,</span><br><span class="line">  resolvers: &#123; <span class="comment">/* ... */</span> &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> data = &#123;</span><br><span class="line">  todos: [],</span><br><span class="line">  visibilityFilter: <span class="string">'SHOW_ALL'</span>,</span><br><span class="line">  networkStatus: &#123;</span><br><span class="line">    __typename: <span class="string">'NetworkStatus'</span>,</span><br><span class="line">    isConnected: <span class="literal">false</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">cache.writeData(&#123; data &#125;);</span><br><span class="line"></span><br><span class="line">client.onResetStore(<span class="function"><span class="params">()</span> =&gt;</span> cache.writeData(&#123; data &#125;));</span><br></pre></td></tr></table></figure><p>使用<code>client.onResetStore</code>方法可以重置缓存。</p><h3 id="同时请求本地状态和远程数据"><a href="#同时请求本地状态和远程数据" class="headerlink" title="同时请求本地状态和远程数据"></a>同时请求本地状态和远程数据</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mutation ToggleTodo($id: Int!) &#123;</span><br><span class="line">  toggleTodo(id: $id) @client</span><br><span class="line">  getData(id: $id) &#123;</span><br><span class="line">    id,</span><br><span class="line">    name</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>只需在需要从本地查询的字段后面加上<code>@client</code>指令即可。</p><h3 id="使用-client字段作为变量"><a href="#使用-client字段作为变量" class="headerlink" title="使用@client字段作为变量"></a>使用@client字段作为变量</h3><p>在同一个graphql语句中，还可以将从本地查到的状态用于下一个查询，通过<code>@export</code>指令</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; ApolloClient &#125; <span class="keyword">from</span> <span class="string">'apollo-client'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; InMemoryCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-inmemory'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; HttpLink &#125; <span class="keyword">from</span> <span class="string">'apollo-link-http'</span>;</span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> query = gql<span class="string">`</span></span><br><span class="line"><span class="string">  query currentAuthorPostCount($authorId: Int!) &#123;</span></span><br><span class="line"><span class="string">    currentAuthorId @client @export(as: "authorId")</span></span><br><span class="line"><span class="string">    postCount(authorId: $authorId)</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cache = <span class="keyword">new</span> InMemoryCache();</span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  link: <span class="keyword">new</span> HttpLink(&#123; uri: <span class="string">'http://localhost:4000/graphql'</span> &#125;),</span><br><span class="line">  cache,</span><br><span class="line">  resolvers: &#123;&#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">cache.writeData(&#123;</span><br><span class="line">  data: &#123;</span><br><span class="line">    currentAuthorId: <span class="number">12345</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ... run the query using client.query, the &lt;Query /&gt; component, etc.</span></span><br></pre></td></tr></table></figure><p>在上面的示例中，<code>currentAuthorId</code>首先从缓存加载，然后作为authorId变量（由@export（as：“authorId”）指令指定）传递到后续postCount字段中。<br>@export指令也可用于选择集中的特定字段，如：</p><p><code>@export</code>指令还可以用于选择集中的特定字段</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; ApolloClient &#125; <span class="keyword">from</span> <span class="string">'apollo-client'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; InMemoryCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-inmemory'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; HttpLink &#125; <span class="keyword">from</span> <span class="string">'apollo-link-http'</span>;</span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> query = gql<span class="string">`</span></span><br><span class="line"><span class="string">  query currentAuthorPostCount($authorId: Int!) &#123;</span></span><br><span class="line"><span class="string">    currentAuthor @client &#123;</span></span><br><span class="line"><span class="string">      name</span></span><br><span class="line"><span class="string">      authorId @export(as: "authorId")</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">    postCount(authorId: $authorId)</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cache = <span class="keyword">new</span> InMemoryCache();</span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  link: <span class="keyword">new</span> HttpLink(&#123; uri: <span class="string">'http://localhost:4000/graphql'</span> &#125;),</span><br><span class="line">  cache,</span><br><span class="line">  resolvers: &#123;&#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">cache.writeData(&#123;</span><br><span class="line">  data: &#123;</span><br><span class="line">    currentAuthor: &#123;</span><br><span class="line">      __typename: <span class="string">'Author'</span>,</span><br><span class="line">      name: <span class="string">'John Smith'</span>,</span><br><span class="line">      authorId: <span class="number">12345</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ... run the query using client.query, the &lt;Query /&gt; component, etc.</span></span><br></pre></td></tr></table></figure><p><code>@export</code>指令使用不仅限于远程查询；它还可以用于为其他<code>@client</code>字段或选择集定义变量：(注意以下代码中GraphQL文档的<code>currentAuthorId</code>和<code>postCount</code>字段之后都有<code>@client</code>指令)</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; ApolloClient &#125; <span class="keyword">from</span> <span class="string">'apollo-client'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; InMemoryCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-inmemory'</span>;</span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> query = gql<span class="string">`</span></span><br><span class="line"><span class="string">  query currentAuthorPostCount($authorId: Int!) &#123;</span></span><br><span class="line"><span class="string">    currentAuthorId @client @export(as: "authorId")</span></span><br><span class="line"><span class="string">    postCount(authorId: $authorId) @client</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cache = <span class="keyword">new</span> InMemoryCache();</span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  cache,</span><br><span class="line">  resolvers: &#123;</span><br><span class="line">    Query: &#123;</span><br><span class="line">      postCount(_, &#123; authorId &#125;) &#123;</span><br><span class="line">        <span class="keyword">return</span> authorId === <span class="number">12345</span> ? <span class="number">100</span> : <span class="number">0</span>;</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">cache.writeData(&#123;</span><br><span class="line">  data: &#123;</span><br><span class="line">    currentAuthorId: <span class="number">12345</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ... run the query using client.query, the &lt;Query /&gt; component, etc.</span></span><br></pre></td></tr></table></figure><h3 id="动态注入resolver"><a href="#动态注入resolver" class="headerlink" title="动态注入resolver"></a>动态注入resolver</h3><p>有时，当我们在APP中使用了代码拆分，如使用<code>react-loadable</code>时，我们并不是很希望所有的resolver都在初始化Apollo客户端的统一写在一起，而是希望单独拆分到各自的模块中，这样在APP编译后，<br>每个模块各自resolver将包含在自己的包中，这样也有助于减少入口文件的大小，使用<code>addResolvers</code> 和 <code>setResolvers</code>即可办到（<a href="https://www.apollographql.com/docs/react/essentials/local-state/#methods" target="_blank" rel="noopener">API</a>），<br>例如以下代码：</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Loadable <span class="keyword">from</span> <span class="string">'react-loadable'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> Loading <span class="keyword">from</span> <span class="string">'./components/Loading'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> Stats = Loadable(&#123;</span><br><span class="line">  loader: <span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">import</span>(<span class="string">'./components/stats/Stats'</span>),</span><br><span class="line">  loading: Loading,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; ApolloConsumer, Query &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span>;</span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> GET_MESSAGE_COUNT = gql<span class="string">`</span></span><br><span class="line"><span class="string">  &#123;</span></span><br><span class="line"><span class="string">    messageCount @client &#123;</span></span><br><span class="line"><span class="string">      total</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> resolvers = &#123;</span><br><span class="line">  Query: &#123;</span><br><span class="line">    messageCount: <span class="function">(<span class="params">_, args, &#123; cache &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// ... calculate and return the number of messages in</span></span><br><span class="line">      <span class="comment">// the cache ...</span></span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        total: <span class="number">123</span>,</span><br><span class="line">        __typename: <span class="string">'MessageCount'</span>,</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> MessageCount = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;ApolloConsumer&gt;</span><br><span class="line">      &#123;<span class="function">(<span class="params">client</span>) =&gt;</span> &#123;</span><br><span class="line">        client.addResolvers(resolvers);</span><br><span class="line">        <span class="keyword">return</span> (</span><br><span class="line">          &lt;Query query=&#123;GET_MESSAGE_COUNT&#125;&gt;</span><br><span class="line">            &#123;<span class="function">(<span class="params">&#123; loading, data: &#123; messageCount &#125; &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">              <span class="keyword">if</span> (loading) <span class="keyword">return</span> <span class="string">'Loading ...'</span>;</span><br><span class="line">              <span class="keyword">return</span> (</span><br><span class="line">                &lt;p&gt;</span><br><span class="line">                  Total <span class="built_in">number</span> of messages: &#123;messageCount.total&#125;</span><br><span class="line">                &lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">              );</span></span><br><span class="line"><span class="regexp">            &#125;&#125;</span></span><br><span class="line"><span class="regexp">          &lt;/</span>Query&gt;</span><br><span class="line">        );</span><br><span class="line">      &#125;&#125;</span><br><span class="line">    &lt;<span class="regexp">/ApolloConsumer&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default MessageCount;</span></span><br></pre></td></tr></table></figure><h2 id="脱离React标签的写法"><a href="#脱离React标签的写法" class="headerlink" title="脱离React标签的写法"></a>脱离React标签的写法</h2><p>由于编程习惯的不同，有些人（比如我），并不是很喜欢（或者说成习惯）把<code>逻辑代码</code>和<code>React标签</code>混合写在一起，就如我们从本文档开始一路看来所有的示例代码那样！<br>个人觉得在一个大型的项目中把<code>Query标签</code>、<code>Mutation标签</code>以及其他的各种标签层层嵌套，再加上各种逻辑实现代码，全部挤在React组件中，真的是一件糟糕的事情。<br>虽然官方推崇这种写法（以上绝大部分代码是官方文档的示例），他们给出的理由是这样写更方便，更简单！</p><p>因人而异吧！</p><p>我个人更倾向于把<code>GraphQL和Apollo的逻辑部分</code>与<code>React组件</code>分离开，我们可以使用<code>react-apollo</code>库提供的<code>graphql</code>和<code>compose</code>方法做到分离。<br>当然，在<code>react-apollo</code>这个包中并不止这两个方法，还有其他的方法，请参考<a href="https://www.apollographql.com/docs/react/api/react-apollo/" target="_blank" rel="noopener">React Apollo API</a></p><p>下面展示一段分离开的代码示例：</p><figure class="highlight typescript"><figcaption><span>jsx</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Avatar, Card, Icon &#125; <span class="keyword">from</span> <span class="string">'antd'</span></span><br><span class="line"><span class="keyword">import</span> gql <span class="keyword">from</span> <span class="string">'graphql-tag'</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> &#123; useEffect, useRef &#125; <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> &#123; compose, graphql &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span></span><br><span class="line"><span class="keyword">import</span> &#123; QueryState, Typename &#125; <span class="keyword">from</span> <span class="string">'src/config/clientState'</span></span><br><span class="line"><span class="keyword">import</span> &#123; GET_COMPONENTS_LIST, GET_QUERY_STATE &#125; <span class="keyword">from</span> <span class="string">'src/graphql'</span></span><br><span class="line"><span class="keyword">import</span> &#123; getCorrectQueryState &#125; <span class="keyword">from</span> <span class="string">'src/util'</span></span><br><span class="line"><span class="keyword">import</span> Pagination <span class="keyword">from</span> <span class="string">'../pagination'</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'./index.scss'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> &#123; Meta &#125; = Card</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Type = &#123;</span><br><span class="line">  id: <span class="built_in">number</span>,</span><br><span class="line">  name: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Author = &#123;</span><br><span class="line">  username: <span class="built_in">string</span>,</span><br><span class="line">  email: <span class="built_in">string</span>,</span><br><span class="line">  avatar: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Component = &#123;</span><br><span class="line">  id: <span class="built_in">number</span>,</span><br><span class="line">  name: <span class="built_in">string</span>,</span><br><span class="line">  version: <span class="built_in">string</span>,</span><br><span class="line">  chineseName: <span class="built_in">string</span>,</span><br><span class="line">  description: <span class="built_in">string</span>,</span><br><span class="line">  <span class="keyword">type</span>: Type,</span><br><span class="line">  url: <span class="built_in">string</span>,</span><br><span class="line">  author: Author,</span><br><span class="line">  previewUrl: <span class="built_in">string</span>,</span><br><span class="line">  isOwn: <span class="built_in">boolean</span>,</span><br><span class="line">  isStored: <span class="built_in">boolean</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> ListComponent = &#123;</span><br><span class="line">  data: &#123;</span><br><span class="line">    components: Component[],</span><br><span class="line">    compCount: <span class="built_in">number</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  componentsCollect: <span class="function">(<span class="params">[id, isCollect]: [<span class="built_in">number</span>, <span class="built_in">boolean</span>]</span>) =&gt;</span> <span class="built_in">void</span>,</span><br><span class="line">  queryState: QueryState</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ListComponent = <span class="function">(<span class="params">props: ListComponent</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> cardList: React.MutableRefObject&lt;HTMLDivElement | <span class="literal">null</span>&gt; = useRef(<span class="literal">null</span>)</span><br><span class="line">  <span class="keyword">const</span> &#123; data, componentsCollect, queryState &#125; = props</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 加载时组件卡片的动画效果，配合React的ref和key属性使用</span></span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(cardList.current) &#123;</span><br><span class="line">      cardList.current.childNodes.forEach(<span class="function">(<span class="params">element: HTMLElement, index: <span class="built_in">number</span></span>) =&gt;</span> &#123;</span><br><span class="line">        setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">          element.classList.add(<span class="string">'card-load-anim'</span>)</span><br><span class="line">        &#125;, index * <span class="number">40</span>)</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div className=<span class="string">'m-list'</span>&gt;</span><br><span class="line">      &lt;div className=<span class="string">'m-cards'</span> ref=&#123;cardList&#125; key=&#123;<span class="built_in">Math</span>.random()&#125;&gt;</span><br><span class="line">        &#123;</span><br><span class="line">          !data || !data.components</span><br><span class="line">            ? <span class="literal">null</span></span><br><span class="line">            : data.components.map(<span class="function">(<span class="params">o: Components, i: <span class="built_in">number</span></span>) =&gt;</span> &#123;</span><br><span class="line">              <span class="keyword">return</span> (</span><br><span class="line">                &lt;Card</span><br><span class="line">                  key=&#123;<span class="string">`m-list-btn-<span class="subst">$&#123;i&#125;</span>`</span>&#125;</span><br><span class="line">                  hoverable=&#123;<span class="literal">true</span>&#125;</span><br><span class="line">                  cover=&#123;&lt;img alt=<span class="string">'example'</span> src=<span class="string">'https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png'</span> /&gt;&#125;</span><br><span class="line">                  actions=&#123;</span><br><span class="line">                    [</span><br><span class="line">                      &lt;a</span><br><span class="line">                        className=&#123;<span class="string">`<span class="subst">$&#123;o.isOwn ? 'm-list-btn-text-disabled' : 'm-list-btn-text'&#125;</span>`</span>&#125;</span><br><span class="line">                        key=&#123;<span class="string">`m-list-btn-<span class="subst">$&#123;i&#125;</span>-1`</span>&#125;</span><br><span class="line">                        href=<span class="string">'javascript:void(0)'</span></span><br><span class="line">                        onClick=&#123;!o.isOwn ? componentsCollect.bind(<span class="literal">null</span>, [o.id, !o.isStored]) : <span class="literal">null</span>&#125;</span><br><span class="line">                      &gt;</span><br><span class="line">                        &lt;Icon <span class="keyword">type</span>=<span class="string">'copy'</span> /&gt;</span><br><span class="line">                        &#123;o.isStored ? <span class="string">'取消收藏'</span> : <span class="string">'收藏'</span>&#125;</span><br><span class="line">                      &lt;<span class="regexp">/a&gt;,</span></span><br><span class="line"><span class="regexp">                      &lt;a</span></span><br><span class="line"><span class="regexp">                        className='m-list-btn-text'</span></span><br><span class="line"><span class="regexp">                        key='m-list-btn-2'</span></span><br><span class="line"><span class="regexp">                        href='javascript:void(0)'</span></span><br><span class="line"><span class="regexp">                      &gt;</span></span><br><span class="line"><span class="regexp">                        &lt;Icon type='file-search' /</span>&gt;</span><br><span class="line">                        文档</span><br><span class="line">                      &lt;<span class="regexp">/a&gt;</span></span><br><span class="line"><span class="regexp">                    ]</span></span><br><span class="line"><span class="regexp">                  &#125;</span></span><br><span class="line"><span class="regexp">                &gt;</span></span><br><span class="line"><span class="regexp">                  &lt;Meta</span></span><br><span class="line"><span class="regexp">                    avatar=&#123;&lt;Avatar src='https:/</span><span class="regexp">/zos.alipayobjects.com/</span>rmsportal/ODTLcjxAfvqbxHnVXCYX.png<span class="string">' /&gt;&#125;</span></span><br><span class="line"><span class="string">                    title=&#123;o.name&#125;</span></span><br><span class="line"><span class="string">                    description=&#123;o.description&#125;</span></span><br><span class="line"><span class="string">                  /&gt;</span></span><br><span class="line"><span class="string">                &lt;/Card&gt;</span></span><br><span class="line"><span class="string">              )</span></span><br><span class="line"><span class="string">            &#125;)</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &lt;/div&gt;</span></span><br><span class="line"><span class="string">      &lt;Pagination totalPages=&#123;Math.ceil(data.compCount / queryState.pagination.size)&#125; total=&#123;data.compCount&#125; /&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">  )</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">export default compose(</span></span><br><span class="line"><span class="string">  graphql(</span></span><br><span class="line"><span class="string">    gql(GET_QUERY_STATE),</span></span><br><span class="line"><span class="string">    &#123;</span></span><br><span class="line"><span class="string">      props: (&#123; data: &#123; queryState &#125; &#125;: any) =&gt; (&#123; queryState &#125;)</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  ),</span></span><br><span class="line"><span class="string">  graphql(</span></span><br><span class="line"><span class="string">    gql(GET_COMPONENTS_LIST),</span></span><br><span class="line"><span class="string">    &#123;</span></span><br><span class="line"><span class="string">      options: (&#123; queryState &#125;: any) =&gt; (&#123;</span></span><br><span class="line"><span class="string">        variables: &#123;</span></span><br><span class="line"><span class="string">          ...getCorrectQueryState(queryState)</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;)</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  ),</span></span><br><span class="line"><span class="string">  graphql(gql`</span></span><br><span class="line"><span class="string">    mutation ($id: Int!, $isCollect: Boolean!)&#123;</span></span><br><span class="line"><span class="string">      storeComponent(id: $id, isStore: $isCollect)</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  `, &#123;</span></span><br><span class="line"><span class="string">    props: (&#123; mutate &#125;: any) =&gt; (&#123;</span></span><br><span class="line"><span class="string">      componentsCollect: ([id, isCollect]: [number, boolean]) =&gt; &#123;</span></span><br><span class="line"><span class="string">        debugger</span></span><br><span class="line"><span class="string">        mutate(&#123;</span></span><br><span class="line"><span class="string">          variables: &#123; id, isCollect &#125;,</span></span><br><span class="line"><span class="string">          optimisticResponse: &#123;</span></span><br><span class="line"><span class="string">            __typename: Typename.Mutation,</span></span><br><span class="line"><span class="string">            storeComponent: &#123;</span></span><br><span class="line"><span class="string">              __typename: Typename.Component,</span></span><br><span class="line"><span class="string">              id,</span></span><br><span class="line"><span class="string">              isStored: isCollect</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;)</span></span><br><span class="line"><span class="string">      &#125;</span></span><br><span class="line"><span class="string">    &#125;)</span></span><br><span class="line"><span class="string">  &#125;)</span></span><br><span class="line"><span class="string">)(ListComponent)</span></span><br></pre></td></tr></table></figure><p>写在最后：</p><p>如果认真学习完这扁文档，相信你对Apollo技术栈开发React应用已经算是入门了，今后开发时遇到问题，多看一看官方文档，相信你会很快掌握它。</p><p>由于水平有限，这篇文章是我自己一边翻译一边加入自己的理解而写成的，其中肯定少不了一些不妥或错误的地方，欢迎大家指正！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文档诸多例子来源于官方文档，但也在编写过程中添加了我对这套技术栈的一些理解，如果你更喜欢看官方文档，请移步&lt;a href=&quot;https://www.apollographql.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;官网&lt;/a&gt;/&lt;a href=&quot;https://www.apollographql.com/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么要使用apollo？&lt;br&gt;没有redux繁琐的action、reducer、dispatch……让全局管理store变得简单、直白！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用&lt;code&gt;redux&lt;/code&gt;管理状态，重心是放在如何去拿数据上；而&lt;code&gt;apollo&lt;/code&gt;把重心放在需要什么数据上。理解这一点非常重要！&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;好了，废话不多说，我们立即开始！&lt;/p&gt;
    
    </summary>
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/categories/javascript/"/>
    
    
      <category term="react" scheme="http://blog.xieyangogo.cn/tags/react/"/>
    
      <category term="typescript" scheme="http://blog.xieyangogo.cn/tags/typescript/"/>
    
      <category term="apollo-client" scheme="http://blog.xieyangogo.cn/tags/apollo-client/"/>
    
      <category term="Apollo" scheme="http://blog.xieyangogo.cn/tags/Apollo/"/>
    
  </entry>
  
  <entry>
    <title>new 操作符到底做了什么？</title>
    <link href="http://blog.xieyangogo.cn/2019/04/12/new-%E6%93%8D%E4%BD%9C%E7%AC%A6%E5%88%B0%E5%BA%95%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F/"/>
    <id>http://blog.xieyangogo.cn/2019/04/12/new-操作符到底做了什么？/</id>
    <published>2019-04-12T02:48:05.000Z</published>
    <updated>2019-04-15T01:28:24.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>相信很多才接触前端的小伙伴甚至工作几年的前端小伙伴对new这个操作符的了解还停留在一知半解的地步，比较模糊。</p><p>就比如前不久接触到一个入职两年的前端小伙伴，他告诉我new是用来创建对象的，无可厚非，可能很多人都会这么答！</p><p>那这么答到底是错很是对呢？</p></blockquote><hr><p>下面我们全面来讨论一下这个问题：</p><p>我们要拿到一个对象，有很多方式，其中最常见的一种便是对象字面量：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;&#125;</span><br></pre></td></tr></table></figure><blockquote><p>但是从语法上来看，这就是一个赋值语句，是把对面字面量赋值给了<code>obj</code>这个变量（这样说或许不是很准确，其实这里是得到了一个对象的实例！！）</p><p>很多时候，我们说要创建一个对象，很多小伙伴双手一摸键盘，啪啪几下就敲出了这句代码。</p><p>上面说了，这句话其实只是得到了一个对象的实例，那这句代码到底还能不能和创建对象画上等号呢？</p></blockquote><p>我们继续往下看。</p><p>要拿到一个对象的实例，还有一种和对象字面量等价的做法就是构造函数：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = <span class="keyword">new</span> <span class="built_in">Object</span>()</span><br></pre></td></tr></table></figure><blockquote><p>这句代码一敲出来，相信小伙伴们对刚才我说的<code>obj</code>只是一个实例对象没有异议了吧！</p><p>那很多小伙伴又会问了：这不就是<code>new</code>了一个新对象出来嘛！</p><p>没错，这确实是new了一个新对象出来，因为javascript之中，万物解释对象。</p><p>obj是一个对象，而且是通过new运算符得到的，所以说很多小伙伴就肯定的说：new就是用来创建对象的！</p><p>这就不难解释很多人把创建对象和实例化对象混为一谈!!</p><p>我们在换个思路看看：既然js一切皆为对象，那为什么还需要创建对象呢？本身就是对象，我们何来创建一说？那我们可不可以把这是一种<code>继承</code>呢？</p><p><img src="/images/newOperation/qqpyimg1555239090.gif" alt=""></p><p>说了这么多，相信不少伙伴已经看晕了，但是我们的目的就是一个：理清new是来做继承的而不是所谓的创建对象！！</p></blockquote><hr><p>那继承得到的实例对象有什么特点呢？</p><ol><li>访问构造函数里面的属性</li><li>访问原型链上的属性</li></ol><p>下面是一段经典的继承，通过这段代码来热热身，好戏马上开始：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name</span><br><span class="line">  <span class="keyword">this</span>.age = age</span><br><span class="line">  <span class="keyword">this</span>.gender = <span class="string">'男'</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.nation = <span class="string">'汉'</span></span><br><span class="line"></span><br><span class="line">Person.prototype.say = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person(<span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="built_in">console</span>.log(person.age)</span><br><span class="line"><span class="built_in">console</span>.log(person.gender)</span><br><span class="line"><span class="built_in">console</span>.log(person.nation)</span><br><span class="line"></span><br><span class="line">person.say()</span><br></pre></td></tr></table></figure><ul><li>现在我们来解决第一个问题：我们可以通过什么方式实现访问到构造函数里面的属性呢？答案是<code>call</code>或<code>apply</code></li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = [<span class="string">'A'</span>, <span class="string">'B'</span>]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  Parent.call(<span class="keyword">this</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> child = <span class="keyword">new</span> Child()</span><br><span class="line"><span class="built_in">console</span>.log(child.name) <span class="comment">// ['A', 'B']</span></span><br><span class="line"></span><br><span class="line">child.name.push(<span class="string">'C'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(child.name) <span class="comment">// ['A', 'B', 'C']</span></span><br></pre></td></tr></table></figure><ul><li>第一个问题解决了，那我们又来解决第二个：那又怎么访问原型链上的属性呢？答案是<code>__proto__</code></li></ul><p>现在我们把上面那段热身代码稍加改造，不使用new来创建实例：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name</span><br><span class="line">  <span class="keyword">this</span>.age = age</span><br><span class="line">  <span class="keyword">this</span>.gender = <span class="string">'男'</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.nation = <span class="string">'汉'</span></span><br><span class="line"></span><br><span class="line">Person.prototype.say = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// var person = new Person('小明', 25)</span></span><br><span class="line"><span class="keyword">var</span> person = New(Person, <span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="built_in">console</span>.log(person.age)</span><br><span class="line"><span class="built_in">console</span>.log(person.gender)</span><br><span class="line"><span class="built_in">console</span>.log(person.nation)</span><br><span class="line"></span><br><span class="line">person.say()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">New</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> obj = &#123;&#125;</span><br><span class="line">  Constructor = [].shift.call(<span class="built_in">arguments</span>) <span class="comment">// 获取arguments第一个参数：构造函数</span></span><br><span class="line">  <span class="comment">// 注意：此时的arguments参数在shift()方法的截取后只剩下两个元素</span></span><br><span class="line">  obj.__proto__ = Constructor.prototype <span class="comment">// 把构造函数的原型赋值给obj对象</span></span><br><span class="line">  Constructor.apply(obj, <span class="built_in">arguments</span>) <span class="comment">// 改变够着函数指针，指向obj，这是刚才上面说到的访问构造函数里面的属性和方法的方式</span></span><br><span class="line">  <span class="keyword">return</span> obj</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上代码中的New函数，就是new操作符的实现</p><p><code>主要步骤：</code></p><p><code>1. 创建一个空对象</code></p><p><code>2. 获取arguments第一个参数</code></p><p><code>3. 将构造函数的原型链赋给obj</code></p><p><code>4. 使用apply改变构造函数this指向，指向obj对象，其后，obj就可以访问到构造函数中的属性以及原型上的属性和方法了</code></p><p><code>5. 返回obj对象</code></p><blockquote><p>可能很多小伙伴看到这里觉得<code>new</code>不就是做了这些事情吗，然而~~</p><p>然而我们却忽略了一点，js里面的函数是有返回值的，即使构造函数也不例外。</p></blockquote><p>如果我们在构造函数里面返回一个对象或一个基本值，上面的New函数会怎样？</p><p>我们再来看一段代码：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name</span><br><span class="line">  <span class="keyword">this</span>.age = age</span><br><span class="line">  <span class="keyword">this</span>.gender = <span class="string">'男'</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    name: name,</span><br><span class="line">    gender: <span class="string">'男'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.nation = <span class="string">'汉'</span></span><br><span class="line"></span><br><span class="line">Person.prototype.say = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> Person(<span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="built_in">console</span>.log(person.age)</span><br><span class="line"><span class="built_in">console</span>.log(person.gender)</span><br><span class="line"><span class="built_in">console</span>.log(person.nation)</span><br><span class="line"></span><br><span class="line">person.say()</span><br></pre></td></tr></table></figure></p><p>执行代码，发现只有<code>name</code>和<code>gender</code>这两个字段如期输出，<code>age</code>、<code>nation</code>为undefined，<code>say()</code>报错。</p><p>改一下代码构造函数的代码：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name</span><br><span class="line">  <span class="keyword">this</span>.age = age</span><br><span class="line">  <span class="keyword">this</span>.gender = <span class="string">'男'</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// return &#123;</span></span><br><span class="line">  <span class="comment">//   name: name,</span></span><br><span class="line">  <span class="comment">//   gender: '男'</span></span><br><span class="line">  <span class="comment">// &#125;</span></span><br><span class="line">  <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure></p><p>执行一下代码，发现所有字段终于如期输出。</p><p><code>这里做个小结：</code></p><p><code>1. 当构造函数返回引用类型时，构造里面的属性不能使用，只能使用返回的对象；</code></p><p><code>2. 当构造函数返回基本类型时，和没有返回值的情况相同，构造函数不受影响。</code></p><p>那我们现在来考虑下New函数要怎么改才能实现上面总结的两点功能呢？继续往下看：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">New</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> obj = &#123;&#125;</span><br><span class="line">  Constructor = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line">  obj.__proto__ = Constructor.prototype</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Constructor.apply(obj, arguments)</span></span><br><span class="line">  <span class="keyword">var</span> result = Constructor.apply(obj, <span class="built_in">arguments</span>)</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// return obj</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">'object'</span> ? result : obj</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = New(Person, <span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure><p>执行此代码，发现已经实现了上面总结的两点。</p><p>解决方案：使用变量接收构造函数的返回值，然后在New函数里面判断一下返回值类型，根据不同类型返回不同的值。</p><blockquote><p>看到这里。又有小伙伴说，这下new已经完全实现了吧？！！</p><p>答案肯定是否定的。</p></blockquote><p>下面我们继续看一段代码：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name</span><br><span class="line">  <span class="keyword">this</span>.age = age</span><br><span class="line">  <span class="keyword">this</span>.gender = <span class="string">'男'</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 返回引用类型</span></span><br><span class="line">  <span class="comment">// return &#123;</span></span><br><span class="line">  <span class="comment">//   name: name,</span></span><br><span class="line">  <span class="comment">//   gender: '男'</span></span><br><span class="line">  <span class="comment">// &#125;</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 返回基本类型</span></span><br><span class="line">  <span class="comment">// return 1</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 例外</span></span><br><span class="line">  <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再执行代码，发现又出问题了！！！</p><blockquote><p>又出问题了！为什么……？<br>…<br>刚才不是总结了返回基本类型时构造函数不受影响吗，而null就是基本类型啊？<br>…<br><img src="/images/newOperation/1555238692.jpg" alt=""></p></blockquote><p>解惑：null是基本类型没错，但是使用操作符typeof后我们不难发现：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typeof</span> <span class="literal">null</span> === <span class="string">'object'</span> <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>特例：<code>typeof null</code>返回为<code>&#39;object&#39;</code>，因为特殊值<code>null</code>被认为是一个<code>空的对象引用</code>。</p><p>明白了这一点，那问题就好解决了：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">New</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> obj = &#123;&#125;</span><br><span class="line">  Constructor = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line">  obj.__proto__ = Constructor.prototype</span><br><span class="line">  <span class="comment">// Constructor.apply(obj, arguments)</span></span><br><span class="line">  <span class="keyword">var</span> result = Constructor.apply(obj, <span class="built_in">arguments</span>)</span><br><span class="line">  <span class="comment">// return obj</span></span><br><span class="line">  <span class="comment">// return typeof result === 'object' ? result : obj</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">'object'</span> ? result || obj : obj</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = New(Person, <span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure><p>解决方案：判断一下构造函数返回值result，如果result是一个引用（引用类型和null），就返回result，但如果此时result为false（null），就使用操作符<code>||</code>之后的<code>obj</code></p><blockquote><p>好了，到现在应该又有小伙伴发问了，这下New函数是彻彻底底实现了吧！！！</p><p>答案是，离完成不远了！！</p></blockquote><p>在功能上，New函数基本完成了，但是在代码严谨度上，我们还需要做一点工作，继续往下看：</p><p>这里，我们在文章开篇做的铺垫要派上用场了：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;&#125;</span><br></pre></td></tr></table></figure><p>实际上等价于</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = <span class="keyword">new</span> <span class="built_in">Object</span>()</span><br></pre></td></tr></table></figure><p>前面说了，以上两段代码其实只是获取了object对象的一个实例。再者，<code>我们本来就是要实现new，但是我们在实现new的过程中却使用了new</code>！</p><p>这个问题把我们引入到了到底是先有鸡还是先有蛋的问题上！</p><p>这里，我们就要考虑到ECMAScript底层的API了——<code>Object.create(null)</code></p><p>这句代码的意思才是真真切切地<code>创建</code>了一个对象！！</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">New</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// var obj = &#123;&#125;</span></span><br><span class="line">  <span class="comment">// var obj = new Object()</span></span><br><span class="line">  <span class="keyword">var</span> obj = <span class="built_in">Object</span>.create(<span class="literal">null</span>)</span><br><span class="line">  Constructor = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line">  obj.__proto__ = Constructor.prototype</span><br><span class="line">  <span class="comment">// Constructor.apply(obj, arguments)</span></span><br><span class="line">  <span class="keyword">var</span> result = Constructor.apply(obj, <span class="built_in">arguments</span>)</span><br><span class="line">  <span class="comment">// return obj</span></span><br><span class="line">  <span class="comment">// return typeof result === 'object' ? result : obj</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">'object'</span> ? result || obj : obj</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = New(Person, <span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="built_in">console</span>.log(person.age)</span><br><span class="line"><span class="built_in">console</span>.log(person.gender)</span><br><span class="line"><span class="comment">// 这样改了之后，以下两句先注释掉，原因后面再讨论</span></span><br><span class="line"><span class="comment">// console.log(person.nation)</span></span><br><span class="line"><span class="comment">// person.say()</span></span><br></pre></td></tr></table></figure><blockquote><p>好了好了，小伙伴常常舒了一口气，这样总算完成了！！<br>但是，现实总是残酷的！<br><img src="/images/newOperation/1555238450.jpg" alt=""><br>小伙伴：啥？还有完没完？</p></blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name</span><br><span class="line">  <span class="keyword">this</span>.age = age</span><br><span class="line">  <span class="keyword">this</span>.gender = <span class="string">'男'</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.nation = <span class="string">'汉'</span></span><br><span class="line"></span><br><span class="line">Person.prototype.say = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">New</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// var obj = &#123;&#125;</span></span><br><span class="line">  <span class="comment">// var obj = new Object()</span></span><br><span class="line">  <span class="keyword">var</span> obj = <span class="built_in">Object</span>.create(<span class="literal">null</span>)</span><br><span class="line">  Constructor = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line">  obj.__proto__ = Constructor.prototype</span><br><span class="line">  <span class="comment">// Constructor.apply(obj, arguments)</span></span><br><span class="line">  <span class="keyword">var</span> result = Constructor.apply(obj, <span class="built_in">arguments</span>)</span><br><span class="line">  <span class="comment">// return obj</span></span><br><span class="line">  <span class="comment">// return typeof result === 'object' ? result : obj</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">'object'</span> ? result || obj : obj</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = New(Person, <span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="built_in">console</span>.log(person.age)</span><br><span class="line"><span class="built_in">console</span>.log(person.gender)</span><br><span class="line"><span class="comment">// 这里解开刚才的注释</span></span><br><span class="line"><span class="built_in">console</span>.log(person.nation)</span><br><span class="line">person.say()</span><br></pre></td></tr></table></figure><p>别急，我们执行一下修改后的代码，发现原型链上的属性<code>nation</code>和方法<code>say()</code>报错，这又是为什么呢？</p><p><img src="/images/newOperation/newOperation.png" alt=""></p><p><img src="/images/newOperation/newOperation2.png" alt=""></p><p>从上图我们可以清除地看到，<code>Object.create(null)</code>创建的对象是没有原型链的，而后两个对象则是拥有<code>__proto__</code>属性，拥有原型链，这也证明了后两个对象是通过继承得来的。</p><p>那既然通过<code>Object.create(null)</code>创建的对象没有原型链（原型链断了），那我们在创建对象的时候把原型链加上不就行了，那怎么加呢？</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name</span><br><span class="line">  <span class="keyword">this</span>.age = age</span><br><span class="line">  <span class="keyword">this</span>.gender = <span class="string">'男'</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.nation = <span class="string">'汉'</span></span><br><span class="line"></span><br><span class="line">Person.prototype.say = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">New</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  Constructor = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// var obj = &#123;&#125;</span></span><br><span class="line">  <span class="comment">// var obj = new Object()</span></span><br><span class="line">  <span class="comment">// var obj = Object.create(null)</span></span><br><span class="line">  <span class="keyword">var</span> obj = <span class="built_in">Object</span>.create(Constructor.prototype)</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// obj.__proto__ = Constructor.prototype</span></span><br><span class="line">  <span class="comment">// Constructor.apply(obj, arguments)</span></span><br><span class="line">  <span class="keyword">var</span> result = Constructor.apply(obj, <span class="built_in">arguments</span>)</span><br><span class="line">  <span class="comment">// return obj</span></span><br><span class="line">  <span class="comment">// return typeof result === 'object' ? result : obj</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">'object'</span> ? result || obj : obj</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = New(Person, <span class="string">'小明'</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person.name)</span><br><span class="line"><span class="built_in">console</span>.log(person.age)</span><br><span class="line"><span class="built_in">console</span>.log(person.gender)</span><br><span class="line"><span class="built_in">console</span>.log(person.nation)</span><br><span class="line"></span><br><span class="line">person.say()</span><br></pre></td></tr></table></figure><p>这样创建的对象就拥有了它初始的原型链了，这个原型链是我们传进来的构造函数赋予它的。</p><p>也就是说，我们在创建新对象的时候，就为它指定了原型链了——<code>新创建的对象继承自传进来的构造函数！</code></p><blockquote><p>看到这里，小伙伴们长长舒了一口气，有本事你再给我安排一个坑出来！</p><p><img src="/images/newOperation/1555237869.jpg" alt=""></p><p>既然都看到这里了，大家要相信我们离最终的曙光已经不远了！</p><p><img src="/images/newOperation/qqpyimg1555239497.gif" alt=""></p><p>我想说的是，坑是没有了，但是为了程序员吹毛求疵的精神！哦不对，是精益求精的精神，我们还有必要啰嗦一点点！！</p></blockquote><p>想必细心的小伙伴已经注意到了，为什么最后一步中的以下代码：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Constructor = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line"><span class="keyword">var</span> obj = <span class="built_in">Object</span>.create(Constructor.prototype)</span><br></pre></td></tr></table></figure><p>不能使用以下代码来代替？</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = <span class="built_in">Object</span>.create(<span class="literal">null</span>)</span><br><span class="line">Constructor = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line">obj.__proto__ = Constructor.prototype</span><br></pre></td></tr></table></figure><p>换个方式说，这两段代码大致的意思基本相同：<code>都是将构造器的原型赋予新创建的对象。</code>但是为何第二段代码要报错（访问不到原型链上的属性）呢？</p><p>这个问题很吃基本功，认真去研究研究js的底层API<code>Object.create</code>以及原型链等知识，就会明白其中的道理。小伙伴可以拉到文章末尾，我把重点都记录下来了，以供大家参考。</p><hr><p>现在，我们来梳理下最终的New函数做了什么事，也就是本文讨论的结果——new操作符到底做了什么？</p><ol><li>获取实参中的第一个参数（构造函数），就是调用New函数传进来的第一个参数，暂时记为<code>Constructor</code>；</li><li>使用<code>Constructor</code>的原型链结合<code>Object.create</code>来<code>创建</code>一个对象，此时新对象的原型链为<code>Constructor</code>函数的原型对象；（结合我们上面讨论的，要访问原型链上面的属性和方法，要使用实例对象的<strong>proto</strong>属性）</li><li>改变<code>Constructor</code>函数的this指向，指向新创建的实例对象，然后<code>call</code>方法再调用<code>Constructor</code>函数，为新对象赋予属性和方法；（结合我们上面讨论的，要访问构造函数的属性和方法，要使用call或apply）</li><li>返回新创建的对象，为<code>Constructor</code>函数的一个实例对象。</li></ol><p>现在我，我们来回答文章开始时提出的问题，new是用来创建对象的吗？</p><p><code>现在我们可以勇敢的回答，new是用来做继承的，而创建对象的其实是Object.create(null)。</code><br><code>在new操作符的作用下，我们使用新创建的对象去继承了他的构造函数上的属性和方法、以及他的原型链上的属性和方法！</code></p><hr><p>写在最后：</p><blockquote><p>补充一点关于<code>原型链</code>的知识:</p><ol><li>JavaScript中的函数也是对象，而且对象除了使用字面量定以外，都需要通过函数来创建对象；</li><li>prototype属性可以给函数和对象添加可共享（继承）的方法、属性，而<strong>proto</strong>是查找某函数或对象的原型链方式；</li><li>prototype和<strong>proto</strong>都指向原型对象；</li><li>任意一个函数（包括构造函数）都有一个prototype属性，指向该函数的原型对象；</li><li>任意一个实例化的对象，都有一个<strong>proto</strong>属性，指向该实例化对象的构造函数的原型对象。</li></ol></blockquote><blockquote><p>补充一下关于<code>Object.create()</code>的知识：</p><ol><li>Object.create(null)可以创建一个没有原型链、真正意义上的空对象，该对象不拥有js原生对象（Object）的任何特性和功能。<br>就如：即使通过人为赋值的方式（<code>newObj.__proto__ = constructor.prototype</code>）给这个对象赋予了原型链，<br>也不能实现原型链逐层查找属性的功能，因为这个对象看起来似乎即使有了<code>&quot;__proto__&quot;</code>属性，但是它始终没有直接或间接继承自Object.prototype,<br>也就不可能拥有js原生对象（Object）的特性或功能了；</li><li>该API配合Object.defineProperty可以创建javascript极其灵活的自定义对象；</li><li>该API是实现继承的一种方式；</li><li>…</li></ol></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;相信很多才接触前端的小伙伴甚至工作几年的前端小伙伴对new这个操作符的了解还停留在一知半解的地步，比较模糊。&lt;/p&gt;
&lt;p&gt;就比如前不久接触到一个入职两年的前端小伙伴，他告诉我new是用来创建对象的，无可厚非，可能很多人都会这么答！&lt;/p&gt;
&lt;p&gt;
      
    
    </summary>
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/categories/javascript/"/>
    
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/tags/javascript/"/>
    
      <category term="基础知识" scheme="http://blog.xieyangogo.cn/tags/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript设计模式</title>
    <link href="http://blog.xieyangogo.cn/2019/02/27/JavaScript%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <id>http://blog.xieyangogo.cn/2019/02/27/JavaScript设计模式/</id>
    <published>2019-02-27T05:51:02.000Z</published>
    <updated>2019-02-27T07:32:29.000Z</updated>
    
    <content type="html"><![CDATA[<ol><li><p>原型模式</p><p> 从设计模式的角度讲，原型模式是用于创建对象的一种模式，如果我们想要创建一个对象， 一种方法是先指定它的类型，然后通过类来创建这个对象。原型模式选择了另外一种方式，我们 不再关心对象的具体类型，而是找到一个对象，然后通过克隆来创建一个一模一样的对象。</p></li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Plane = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.blood = <span class="number">100</span></span><br><span class="line">    <span class="keyword">this</span>.attackLevel = <span class="number">1</span></span><br><span class="line">    <span class="keyword">this</span>.defenseLevel = <span class="number">1</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">var</span> plane = <span class="keyword">new</span> Plane()</span><br><span class="line">  plane.blood = <span class="number">500</span></span><br><span class="line">  plane.attackLevel = <span class="number">10</span></span><br><span class="line">  plane.defenseLevel = <span class="number">7</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">var</span> clonePlane = <span class="built_in">Object</span>.create(plane)</span><br><span class="line">  <span class="built_in">console</span>.log(clonePlane) <span class="comment">// 输出:Object &#123;blood: 500, attackLevel: 10, defenseLevel: 7&#125;</span></span><br><span class="line">  <span class="comment">// 在不支持 Object.create 方法的浏览器中，则可以使用以下代码:</span></span><br><span class="line">  <span class="built_in">Object</span>.create = <span class="built_in">Object</span>.create || <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> F = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">    F.prototype = obj</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> F()</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>  当然在 JavaScript 这种类型模糊的语言中，创建对象非常容易，也不存在类型耦合的问题。 从设计模式的角度来讲，原型模式的意义并不算大 。但 JavaScript 本身是一门基于原型的面向对 象语言，它的对象系统就是使用原型模式来搭建的，在这里称之为原型编程范型也许更合适。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;ol&gt;
&lt;li&gt;&lt;p&gt;原型模式&lt;/p&gt;
&lt;p&gt; 从设计模式的角度讲，原型模式是用于创建对象的一种模式，如果我们想要创建一个对象， 一种方法是先指定它的类型，然后通过类来创建这个对象。原型模式选择了另外一种方式，我们 不再关心对象的具体类型，而是找到一个对象，然后通过克隆来创建一
      
    
    </summary>
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/categories/javascript/"/>
    
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/tags/javascript/"/>
    
      <category term="设计模式" scheme="http://blog.xieyangogo.cn/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
  </entry>
  
  <entry>
    <title>在CI上使用git subtree强制推送代码到gh-pages</title>
    <link href="http://blog.xieyangogo.cn/2019/01/23/%E5%9C%A8CI%E4%B8%8A%E4%BD%BF%E7%94%A8git%20subtree%E5%BC%BA%E5%88%B6%E6%8E%A8%E9%80%81%E4%BB%A3%E7%A0%81%E5%88%B0gh-pages/"/>
    <id>http://blog.xieyangogo.cn/2019/01/23/在CI上使用git subtree强制推送代码到gh-pages/</id>
    <published>2019-01-23T09:15:23.000Z</published>
    <updated>2019-01-28T02:43:49.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>最近在前端持续集成上面遇到一个问题，在百度上查了无数资料，无解！！也可能是我搜索姿势不对… -_-<br>但是当我尝试在google上搜索时，很快得到了答案。<br>写这段话的目的不是为了黑百度，扬谷歌，望勿借题发挥。至于我的目的，你们自行脑补。😄</p></blockquote><p>前端持续集成，目前流行的平台 circleCI、travis ci等，两者的区别不多介绍，大家可以自行搜索。</p><p>CI的配置也不多做介绍，有兴趣的可以去他们的官网。</p><p>我用CI做了个发布demo的测试，主要流程是当我提交代码到github仓库时，CI会自动拉取代码或者说clone最新代码到虚拟机上，然后CI会找到仓库里的.yml(CI的配置文件)，并依次执行配置文件里面的预先定义好的代码。</p><p>配置文件里面的代码流程是利用webpack编译一个静态的demo出来，编译输出地址为<code>.examples</code>，然后将examples里面的静态文件推送到gh-pages分支。</p><p>代码如下：</p><blockquote><p>注意：<code>${...}</code>里面的内容为CI变量，这个可以在CI平台的设置里面预先定义好，这里不过多讨论。<br>为什么要在用变量，而不直接写在配置文件里？如果是私有仓库，则可以这么做；如果是公开仓库，那你的密码会被所有人看见。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">git remote rm origin</span><br><span class="line">git remote add orgin <span class="string">"https://<span class="variable">$&#123;username&#125;</span>:<span class="variable">$&#123;password&#125;</span>@github.com/<span class="variable">$&#123;username&#125;</span>/<span class="variable">$&#123;repositoryName&#125;</span>.git"</span></span><br><span class="line"></span><br><span class="line">yarn webpack --config build/webpack.config.demo</span><br><span class="line"></span><br><span class="line">git subtree push -P examples github gh-pages</span><br></pre></td></tr></table></figure><p>执行<code>git remote rm origin</code>的原因是CI在clone项目的时候使用的是如下命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> --depth=50 --branch=master https://github.com/<span class="variable">$&#123;username&#125;</span>/<span class="variable">$&#123;repositoryName&#125;</span>.git <span class="variable">$&#123;username&#125;</span>/<span class="variable">$&#123;repositoryName&#125;</span></span><br></pre></td></tr></table></figure><p>可以清楚地看到这只是一个普通的clone仓库代码，里面没有包含用户名和密码，这样就不能直接push代码到github仓库了，所以要做一下修改。</p><p>因为这些流程是在CI的虚拟机里面完成的，我们不能也无法在push代码的时候输入用户名和密码，所以要把仓库的用户名（username）和密码（password）写到仓库地址中去。</p><p>如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add orgin <span class="string">"https://<span class="variable">$&#123;username&#125;</span>:<span class="variable">$&#123;password&#125;</span>@github.com/<span class="variable">$&#123;username&#125;</span>/<span class="variable">$&#123;repositoryName&#125;</span>.git"</span></span><br></pre></td></tr></table></figure><p>这样我们就可以把webpack编译好的demo的静态文件后直接push到gh-pages分支了。</p><blockquote><p>当然还有另外的解决方案，如：</p><ol><li>在虚拟机里面设置全局的用户名和密码。</li><li>用<code>token</code>来代替<code>password</code>代替，这样自己的密码被保护得更好，只需一个token就行。</li></ol></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn webpack --config build/webpack.config.demo</span><br></pre></td></tr></table></figure><p>这句代码就是用webpack打包静态文件，输出到<code>./examples</code>文件夹下。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git subtree push -P examples github gh-pages</span><br></pre></td></tr></table></figure><p>这句代码是把examples文件夹下的demo静态文件push到gh-pages分支。</p><p>执行到这里，问题来了，CI虚拟机报错：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"> ! [rejected]        ******299ab2694e13f437654jb8561c98****** -&gt; gh-pages (fetch first)</span><br><span class="line">error: failed to push some refs to <span class="string">'https://[secure]@github.com/oceanxy/react-tabllist.git'</span></span><br><span class="line">hint: Updates were rejected because the remote contains work that you <span class="keyword">do</span></span><br><span class="line">hint: not have locally. This is usually caused by another repository pushing</span><br><span class="line">hint: to the same ref. You may want to first integrate the remote changes</span><br><span class="line">hint: (e.g., <span class="string">'git pull ...'</span>) before pushing again.</span><br></pre></td></tr></table></figure><p>依照问题的提示，在<code>push</code>代码之前执行<code>git pull</code>。</p><p>但是这样做又会包另外一个错误：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"> ! [rejected]        ******299ab2694e13f437654jb8561c98****** -&gt; gh-pages (non-fast-forward)</span><br><span class="line">error: failed to push some refs to <span class="string">'https://oceanxy:[secure]@github.com/oceanxy/react-tabllist.git'</span></span><br><span class="line">hint: Updates were rejected because a pushed branch tip is behind its remote</span><br><span class="line">hint: counterpart. Check out this branch and integrate the remote changes</span><br><span class="line">hint: (e.g. <span class="string">'git pull ...'</span>) before pushing again.</span><br></pre></td></tr></table></figure><p>最终换了下思路，强制推送新的静态文件到gh-pages分支</p><p>但是我使用的是git subtree，它没有强制推送 –force 这个选项，最终在google里搜索得到如下方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push github `git subtree split --prefix examples master`:gh-pages --force</span><br></pre></td></tr></table></figure><p>然后提交代码到github，CI自动拉去最新代码及配置文件开始跑虚拟机，等待…success！</p><p>最终跑通的流程代码如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">git remote rm origin</span><br><span class="line">git remote add orgin <span class="string">"https://<span class="variable">$&#123;username&#125;</span>:<span class="variable">$&#123;password&#125;</span>@github.com/<span class="variable">$&#123;username&#125;</span>/<span class="variable">$&#123;repositoryName&#125;</span>.git"</span></span><br><span class="line"></span><br><span class="line">yarn webpack --config build/webpack.config.demo</span><br><span class="line"></span><br><span class="line">git push github `git subtree split --prefix examples master`:gh-pages --force</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;最近在前端持续集成上面遇到一个问题，在百度上查了无数资料，无解！！也可能是我搜索姿势不对… -_-&lt;br&gt;但是当我尝试在google上搜索时，很快得到了答案。&lt;br&gt;写这段话的目的不是为了黑百度，扬谷歌，望勿借题发挥。至于我的目的，你们自行脑补。�
      
    
    </summary>
    
      <category term="git subtree" scheme="http://blog.xieyangogo.cn/categories/git-subtree/"/>
    
    
      <category term="CI" scheme="http://blog.xieyangogo.cn/tags/CI/"/>
    
      <category term="git" scheme="http://blog.xieyangogo.cn/tags/git/"/>
    
      <category term="git subtree" scheme="http://blog.xieyangogo.cn/tags/git-subtree/"/>
    
      <category term="强制推送" scheme="http://blog.xieyangogo.cn/tags/%E5%BC%BA%E5%88%B6%E6%8E%A8%E9%80%81/"/>
    
  </entry>
  
  <entry>
    <title>jest学习笔记</title>
    <link href="http://blog.xieyangogo.cn/2019/01/21/jest%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>http://blog.xieyangogo.cn/2019/01/21/jest学习笔记/</id>
    <published>2019-01-21T03:12:50.000Z</published>
    <updated>2019-01-21T06:29:05.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本笔记根据<a href="https://jestjs.io/docs/zh-Hans/more-resources" target="_blank" rel="noopener">jest官方文档23.6</a>而来，根据个人理解，整合官网零散的文档并提炼了jest入门常用的内容到本笔记，以备后续使用和查阅！因个人能力有限，如有缺陷，请海涵！也欢迎您提出宝贵的意见。<br>本文使用yarn来管理npm包，如需npm或其他方式，请自行查阅相关文档。</p></blockquote><h2 id="开始使用之前的准备和配置"><a href="#开始使用之前的准备和配置" class="headerlink" title="开始使用之前的准备和配置"></a><a href="https://jestjs.io/docs/zh-Hans/getting-started" target="_blank" rel="noopener">开始使用之前的准备和配置</a></h2><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p><strong>npm</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev jest</span><br></pre></td></tr></table></figure></p><p><strong>yarn</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add --dev jest</span><br></pre></td></tr></table></figure></p><blockquote><p>全局安装来使用 CLI<br>yarn global add jest<br>使用 CLI 对 my-test 文件进行测试<br>jest my-test –notify –config=config.json<br>–config 使用config.json 作为一个配置文件。<br>–notify 在运行完成后显示一个原生的操作系统通知。<br>更多CLI信息<a href="https://jestjs.io/docs/zh-Hans/cli" target="_blank" rel="noopener">请参阅</a></p></blockquote><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jest --init</span><br></pre></td></tr></table></figure><blockquote><p>在根目录生成一个基本的配置文件 jest.config.js</p></blockquote><h3 id="配合babel和react，如未使用则跳过此处"><a href="#配合babel和react，如未使用则跳过此处" class="headerlink" title="配合babel和react，如未使用则跳过此处"></a>配合babel和react，如未使用则跳过此处</h3><p><strong>babel 7.0 以前版本</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add --dev babel-jest babel-core regenerator-runtime</span><br></pre></td></tr></table></figure></p><p>然后在 .babelrc 文件做如下配置：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"presets"</span>: [<span class="string">"env"</span>, <span class="string">"react"</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>babel 7.0及以后版本</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add --dev babel-jest babel-core@^7.0.0-bridge.0 @babel/core regenerator-runtime</span><br></pre></td></tr></table></figure></p><p>然后在 .babelrc 文件做如下配置：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"presets"</span>: [</span><br><span class="line">      <span class="string">"@babel/preset-env"</span>,</span><br><span class="line">      <span class="string">"@babel/preset-react"</span></span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>注意：</strong></p><p>如果您使用选项 {“modules”：false} 关闭了ES6模块的转换，则必须确保在测试环境中启用此功能。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">// .babelrc</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"presets"</span>: [[<span class="string">"env"</span>, &#123;<span class="attr">"modules"</span>: <span class="literal">false</span>&#125;], <span class="string">"react"</span>],</span><br><span class="line">  <span class="attr">"env"</span>: &#123;</span><br><span class="line">    <span class="attr">"test"</span>: &#123;</span><br><span class="line">      <span class="attr">"presets"</span>: [[<span class="string">"env"</span>], <span class="string">"react"</span>]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当你安装 Jest 时，babel-jest 是会被自动安装的，并且如果你的项目下存在一个 Babel 配置文件时，它将会自动对相关文件进行转义。 如果要避免这个行为，你可以显式的重置 transform 配置项：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// package.json</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"jest"</span>: &#123;</span><br><span class="line">    <span class="attr">"transform"</span>: &#123;&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="匹配器（matchers）"><a href="#匹配器（matchers）" class="headerlink" title="匹配器（matchers）"></a>匹配器（<a href="https://jestjs.io/docs/zh-Hans/expect" target="_blank" rel="noopener">matchers</a>）</h2><h3 id="普通匹配器"><a href="#普通匹配器" class="headerlink" title="普通匹配器"></a>普通匹配器</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 精准（exact）匹配 调用的Object.is </span><br><span class="line">test(&apos;two plus two is four&apos;, () =&gt; &#123;</span><br><span class="line">  expect(2 + 2).toBe(4);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// 匹配对象的值是否相等，请使用 toEqual() 代替 toBe()</span><br><span class="line">test(&apos;object assignment&apos;, () =&gt; &#123;</span><br><span class="line">  const data = &#123;one: 1&#125;;</span><br><span class="line">  data[&apos;two&apos;] = 2;</span><br><span class="line">  expect(data).toEqual(&#123;one: 1, two: 2&#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>expect(2 + 2) 返回一个“期望对象（expect Object）”<br>toBe(4) 和 toEqual() 是匹配器，toEqual()会递归检查对象或数组的每个字段</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// 取反的匹配 not.toBe()</span><br><span class="line">test(&apos;adding positive numbers is not zero&apos;, () =&gt; &#123;</span><br><span class="line">  for (let a = 1; a &lt; 10; a++) &#123;</span><br><span class="line">    for (let b = 1; b &lt; 10; b++) &#123;</span><br><span class="line">      expect(a + b).not.toBe(0);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Truthiness"><a href="#Truthiness" class="headerlink" title="Truthiness"></a>Truthiness</h3><p>toBeNull 只匹配 null<br>toBeUndefined 只匹配 undefined<br>toBeDefined 与 toBeUndefined 相反<br>toBeTruthy 匹配任何 if 语句为真<br>toBeFalsy 匹配任何 if 语句为假</p><p>例如：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">test(&apos;null&apos;, () =&gt; &#123;</span><br><span class="line">  const n = null;</span><br><span class="line">  expect(n).toBeNull();</span><br><span class="line">  expect(n).toBeDefined();</span><br><span class="line">  expect(n).not.toBeUndefined();</span><br><span class="line">  expect(n).not.toBeTruthy();</span><br><span class="line">  expect(n).toBeFalsy();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;zero&apos;, () =&gt; &#123;</span><br><span class="line">  const z = 0;</span><br><span class="line">  expect(z).not.toBeNull();</span><br><span class="line">  expect(z).toBeDefined();</span><br><span class="line">  expect(z).not.toBeUndefined();</span><br><span class="line">  expect(z).not.toBeTruthy();</span><br><span class="line">  expect(z).toBeFalsy();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h3 id="数字"><a href="#数字" class="headerlink" title="数字"></a>数字</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">test(&apos;two plus two&apos;, () =&gt; &#123;</span><br><span class="line">  const value = 2 + 2;</span><br><span class="line">  expect(value).toBeGreaterThan(3);</span><br><span class="line">  expect(value).toBeGreaterThanOrEqual(3.5);</span><br><span class="line">  expect(value).toBeLessThan(5);</span><br><span class="line">  expect(value).toBeLessThanOrEqual(4.5);</span><br><span class="line"></span><br><span class="line">  // toBe and toEqual are equivalent for numbers</span><br><span class="line">  expect(value).toBe(4);</span><br><span class="line">  expect(value).toEqual(4);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// 对于比较浮点数相等，使用 toBeCloseTo 而不是 toEqual，因为你不希望测试取决于一个小小的舍入误差。</span><br><span class="line">test(&apos;两个浮点数字相加&apos;, () =&gt; &#123;</span><br><span class="line">  const value = 0.1 + 0.2;</span><br><span class="line">  //expect(value).toBe(0.3);           这句会报错，因为浮点数有舍入误差</span><br><span class="line">  expect(value).toBeCloseTo(0.3); // 这句可以运行</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// toMatch(正则表达式)</span><br><span class="line">test(&apos;there is no I in team&apos;, () =&gt; &#123;</span><br><span class="line">  expect(&apos;team&apos;).not.toMatch(/I/);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;but there is a &quot;stop&quot; in Christoph&apos;, () =&gt; &#123;</span><br><span class="line">  expect(&apos;Christoph&apos;).toMatch(/stop/);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">const shoppingList = [</span><br><span class="line">  &apos;diapers&apos;,</span><br><span class="line">  &apos;kleenex&apos;,</span><br><span class="line">  &apos;trash bags&apos;,</span><br><span class="line">  &apos;paper towels&apos;,</span><br><span class="line">  &apos;beer&apos;,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">test(&apos;购物清单（shopping list）里面有啤酒（beer）&apos;, () =&gt; &#123;</span><br><span class="line">  expect(shoppingList).toContain(&apos;beer&apos;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="例外"><a href="#例外" class="headerlink" title="例外"></a>例外</h3><p>如果你想要测试的特定函数抛出一个错误，在它调用时，使用 toThrow。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">function compileAndroidCode() &#123;</span><br><span class="line">  throw new ConfigError(&apos;you are using the wrong JDK&apos;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">test(&apos;compiling android goes as expected&apos;, () =&gt; &#123;</span><br><span class="line">  expect(compileAndroidCode).toThrow();</span><br><span class="line">  expect(compileAndroidCode).toThrow(ConfigError);</span><br><span class="line"></span><br><span class="line">  // You can also use the exact error message or a regexp</span><br><span class="line">  expect(compileAndroidCode).toThrow(&apos;you are using the wrong JDK&apos;);</span><br><span class="line">  expect(compileAndroidCode).toThrow(/JDK/);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>匹配器的完整列表，请查阅<a href="https://jestjs.io/docs/zh-Hans/expect" target="_blank" rel="noopener">参考文档</a>。</p><h2 id="测试异步代码"><a href="#测试异步代码" class="headerlink" title="测试异步代码"></a><a href="https://jestjs.io/docs/zh-Hans/asynchronous" target="_blank" rel="noopener">测试异步代码</a></h2><p>在JavaScript中执行异步代码是很常见的。 当你有以异步方式运行的代码时，Jest 需要知道当前它测试的代码是否已完成，然后它可以转移到另一个测试。 Jest有若干方法处理这种情况。</p><h3 id="回调"><a href="#回调" class="headerlink" title="回调"></a>回调</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">// 错误写法</span><br><span class="line">test(&apos;the data is peanut butter&apos;, () =&gt; &#123;</span><br><span class="line">  function callback(data) &#123;</span><br><span class="line">    expect(data).toBe(&apos;peanut butter&apos;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  fetchData(callback);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">// 正确写法</span><br><span class="line">test(&apos;the data is peanut butter&apos;, done =&gt; &#123;</span><br><span class="line">  function callback(data) &#123;</span><br><span class="line">    expect(data).toBe(&apos;peanut butter&apos;);</span><br><span class="line">    done();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  fetchData(callback);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>如果 done()永远不会调用，这个测试将失败，这也是你所希望发生的。</p><h3 id="Promises"><a href="#Promises" class="headerlink" title="Promises"></a>Promises</h3><p>从您的测试返回一个 Promise, Jest 会等待这一 Promise 来解决。 如果 Promise 被拒绝，则测试将自动失败。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">test(&apos;the data is peanut butter&apos;, () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  </span><br><span class="line">  // fetchData，返回值是应该解析为字符串 &apos;peanut butter&apos; 的 Promise </span><br><span class="line">  return fetchData().then(data =&gt; &#123;</span><br><span class="line">    expect(data).toBe(&apos;peanut butter&apos;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>一定要返回 Promise - 如果你省略 return 语句，您的测试将在 fetchData 完成之前完成。</p><p>如果你想要 Promise 被拒绝，使用 .catch 方法。 请确保添加 expect.assertions 来验证一定数量的断言被调用。 否则一个fulfilled态的 Promise 不会让测试失败︰</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">test(&apos;the fetch fails with an error&apos;, () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  return fetchData().catch(e =&gt; expect(e).toMatch(&apos;error&apos;));</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="resolves-rejects"><a href="#resolves-rejects" class="headerlink" title=".resolves / .rejects"></a>.resolves / .rejects</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">test(&apos;the data is peanut butter&apos;, () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  return expect(fetchData()).resolves.toBe(&apos;peanut butter&apos;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;the fetch fails with an error&apos;, () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  return expect(fetchData()).rejects.toMatch(&apos;error&apos;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Async-Await"><a href="#Async-Await" class="headerlink" title="Async/Await"></a>Async/Await</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">test(&apos;the data is peanut butter&apos;, async () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  const data = await fetchData();</span><br><span class="line">  expect(data).toBe(&apos;peanut butter&apos;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;the fetch fails with an error&apos;, async () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  try &#123;</span><br><span class="line">    await fetchData();</span><br><span class="line">  &#125; catch (e) &#123;</span><br><span class="line">    expect(e).toMatch(&apos;error&apos;);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>您可以将<code>async</code>和<code>await</code>与<code>.resolves</code>或<code>.rejects</code>结合起来</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">test(&apos;the data is peanut butter&apos;, async () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  await expect(fetchData()).resolves.toBe(&apos;peanut butter&apos;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;the fetch fails with an error&apos;, async () =&gt; &#123;</span><br><span class="line">  expect.assertions(1);</span><br><span class="line">  await expect(fetchData()).rejects.toMatch(&apos;error&apos;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="Setup-and-Teardown"><a href="#Setup-and-Teardown" class="headerlink" title="Setup and Teardown"></a><a href="https://jestjs.io/docs/zh-Hans/setup-teardown" target="_blank" rel="noopener">Setup and Teardown</a></h2><p>写测试的时候你经常需要在运行测试前做一些准备工作，和在运行测试后进行一些整理工作。 Jest 提供辅助函数来处理这个问题。</p><h3 id="为多次测试重复设置"><a href="#为多次测试重复设置" class="headerlink" title="为多次测试重复设置"></a>为多次测试重复设置</h3><p>如果你有一些要为多次测试重复设置的工作，你可以使用 beforeEach 和 afterEach。</p><p>例如，我们考虑一些与城市信息数据库进行交互的测试。 你必须在每个测试之前调用方法 initializeCityDatabase() ，同时必须在每个测试后，调用方法 clearCityDatabase()。 你可以这样做：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">beforeEach(() =&gt; &#123;</span><br><span class="line">  initializeCityDatabase();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">afterEach(() =&gt; &#123;</span><br><span class="line">  clearCityDatabase();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;city database has Vienna&apos;, () =&gt; &#123;</span><br><span class="line">  expect(isCity(&apos;Vienna&apos;)).toBeTruthy();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;city database has San Juan&apos;, () =&gt; &#123;</span><br><span class="line">  expect(isCity(&apos;San Juan&apos;)).toBeTruthy();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>beforeEach 和 afterEach 能够通过与 异步代码测试 相同的方式处理异步代码——他们可以采取 done 参数或返回一个 promise。 例如，如果 initializeCityDatabase() 返回解决数据库初始化时的 promise ，我们会想返回这一 promise︰</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">beforeEach(() =&gt; &#123;</span><br><span class="line">  return initializeCityDatabase();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="一次性设置"><a href="#一次性设置" class="headerlink" title="一次性设置"></a>一次性设置</h3><p>在某些情况下，你只需要在文件的开头做一次设置。 当这种设置是异步行为时，可能非常恼人，你不太可能一行就解决它。 Jest 提供 beforeAll 和 afterAll 处理这种情况。</p><p>例如，如果 initializeCityDatabase 和 clearCityDatabase 都返回了 promise ，城市数据库可以在测试中重用，我们就能把我们的测试代码改成这样:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">beforeAll(() =&gt; &#123;</span><br><span class="line">  return initializeCityDatabase();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">afterAll(() =&gt; &#123;</span><br><span class="line">  return clearCityDatabase();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;city database has Vienna&apos;, () =&gt; &#123;</span><br><span class="line">  expect(isCity(&apos;Vienna&apos;)).toBeTruthy();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">test(&apos;city database has San Juan&apos;, () =&gt; &#123;</span><br><span class="line">  expect(isCity(&apos;San Juan&apos;)).toBeTruthy();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>其他内容请参考<a href="https://jestjs.io/docs/zh-Hans/more-resources" target="_blank" rel="noopener">官网文档</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;本笔记根据&lt;a href=&quot;https://jestjs.io/docs/zh-Hans/more-resources&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jest官方文档23.6&lt;/a&gt;而来，根据个人理解，整合官网零散
      
    
    </summary>
    
      <category term="util" scheme="http://blog.xieyangogo.cn/categories/util/"/>
    
    
      <category term="jest" scheme="http://blog.xieyangogo.cn/tags/jest/"/>
    
      <category term="前端单元测试" scheme="http://blog.xieyangogo.cn/tags/%E5%89%8D%E7%AB%AF%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>AntDesign的Upload组件在上传文件时，传的文件对象无效问题</title>
    <link href="http://blog.xieyangogo.cn/2018/07/17/AntDesign%E7%9A%84Upload%E7%BB%84%E4%BB%B6%E5%9C%A8%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6%E6%97%B6%EF%BC%8C%E4%BC%A0%E7%9A%84%E6%96%87%E4%BB%B6%E5%AF%B9%E8%B1%A1%E6%97%A0%E6%95%88%E9%97%AE%E9%A2%98/"/>
    <id>http://blog.xieyangogo.cn/2018/07/17/AntDesign的Upload组件在上传文件时，传的文件对象无效问题/</id>
    <published>2018-07-17T07:37:05.000Z</published>
    <updated>2018-08-09T06:55:40.000Z</updated>
    
    <content type="html"><![CDATA[<h4 id="1、问题描述"><a href="#1、问题描述" class="headerlink" title="1、问题描述"></a>1、问题描述</h4><p><code>ant design</code>的<code>Upload 组件</code>(下称‘Upload 组件’)封装到<code>FormData</code>对象后，文件对象被转换成 <code>&#39;[object Object]&#39;</code> 的字符串形式，文件流失效。</p><h4 id="2、原因分析"><a href="#2、原因分析" class="headerlink" title="2、原因分析"></a>2、原因分析</h4><p> <strong>先从<code>Upload 组件</code>说起</strong></p><p> <code>Upload 组件</code>在文件上传前后都有诸多的回调函数，其中最常用的就是<code>onChange</code>回调函数，它在上传文件状态发生改变时被调用，这个回调函数有一个参数<code>info</code>，值为上传的文件对象，也正是我们需要传到后台的文件对象，如下截图：</p><p> <img src="/images/antDesignUploadFile/15.5.png" alt="avatar"></p><p> 可以清楚看到<code>info</code>这个参数是一个对象，里面也包含两个对象，分别为<code>file</code>和<code>fileList</code>，<code>file</code>是当前最近上传的一个文件对象，<code>fileList</code>是一个数组，包含当前批量或多次上传的多个文件对象。</p><p> 我们发现<code>Upload 组件</code>把文件上传后的各种信息再次封装了一层。（不要以为这句话是废话，这里为后面埋伏笔，或者说成设铺垫。😁）</p><p> 当把<code>fileList</code>这个数组通过以下代码——</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> formData = <span class="keyword">new</span> FormData()</span><br><span class="line">formData.append(<span class="string">'key'</span>, fileList)</span><br></pre></td></tr></table></figure><p> ——添加到<code>formData</code>对象里面后，发现<code>formData</code>对象里面的<code>fileList</code>字段被转换成了<code>&#39;[Object Object]&#39;</code>，无法通过文件流的形式传到后台，这里暂且将这个对象称为<code>一般对象</code>。</p><p><strong>为什么会出现这个问题呢？下面来仔细看看<code>FormData</code>对象</strong></p><p> <code>FormData</code>对象其实是在<code>XMLHttpRequest</code>2级定义的，它是为序列化表以及创建与表单格式相同的数据提供便利，用于XHR传输。</p><p> 再来看一看<code>formData.append(key, values[, filename])</code>方法。</p><p> <code>append()</code>方法会添加一个新值到<code>FormData</code>对象内的一个已存在的键中，如果键不存在，则会添加该键；如果指定的键已经存在，会把新值添加到已有值集合的后面。</p><p><strong>append()方法的参数：</strong></p><ul><li><p><strong>name</strong> - value 中包含的数据对应的表单名称。</p></li><li><p><strong>value</strong> - 表单的值。可以是 USVString 或 Blob (包括子类型，如 File)。</p></li><li><p><strong>filename【可选】</strong> - 传给服务器的文件名称 (一个 USVString), 当一个 Blob 或 File 被作为第二个参数的时候， Blob 对象的默认文件名是 “blob”。 File 对象的默认文件名是该文件的名称。</p></li></ul><h4 id="3、解决方案"><a href="#3、解决方案" class="headerlink" title="3、解决方案"></a>3、解决方案</h4><p> 通过上面<code>value</code>参数的注释，我们能看到它的取值类型为<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/USVString" target="_blank" rel="noopener">USVString</a>或者<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Blob" target="_blank" rel="noopener">Blob</a></p><p> 而我们刚才取值的<code>file</code>或者<code>fileList</code>都是<code>javascript</code>的‘一般对象’，并非<code>Blob</code>对象，那我们要传给后台的<code>Blob</code>对象到底在哪？</p><p> 通过查阅文档，发现<code>&lt;input type=&#39;file&#39;&gt;</code>的值（<code>File</code>对象）本身就是一个<code>Blob</code>对象，或者说成<code>File</code>对象继承了<code>Blob</code>对象。</p><p> 说到这里，那我们到底该到哪里去获取<code>File</code>对象呢？想必细心的你已经发现了，神秘的它就在<code>Upload 组件</code>的<code>onChange</code>回调函数的返回的参数中，即：</p> <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">onchange(info) &#123;</span><br><span class="line">  info.file.originFileObj</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p> 如果还不明白，请看下面的截图：</p><p> <img src="/images/antDesignUploadFile/15.5-2.png" alt="avatar"></p><p> 另外，<code>Upload 组件</code>的官方文档中还有其他的回调函数可以拿到原生的（未经过<code>Upload 组件</code>处理的）<code>File</code>对象，比如<code>beforeUpload</code>，详情见<a href="https://ant.design/components/upload-cn/#API" target="_blank" rel="noopener">官方文档</a></p><p> 总结，我们只需要简单的处理一下<code>Upload 组件</code>的<code>onChange</code>回调函数的返回参数，即可拿到可以封装到<code>FormData</code>中的<code>File</code>对象。</p><h4 id="4、最后来一个温馨提醒"><a href="#4、最后来一个温馨提醒" class="headerlink" title="4、最后来一个温馨提醒"></a>4、最后来一个温馨提醒</h4><ol><li><p>可以使用 FormData 来上传文件，但是必须得在<code>&lt;form&gt;</code>标签上添加属性: enctype=”multipart/form-data”</p></li><li><p>如果使用 XHR 上传表单，记得设置 contentType=false，然后使用第1步的<code>&lt;form&gt;</code>表单。</p><p> <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects" target="_blank" rel="noopener">更多具体详情请参考FormData文档</a></p></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;1、问题描述&quot;&gt;&lt;a href=&quot;#1、问题描述&quot; class=&quot;headerlink&quot; title=&quot;1、问题描述&quot;&gt;&lt;/a&gt;1、问题描述&lt;/h4&gt;&lt;p&gt;&lt;code&gt;ant design&lt;/code&gt;的&lt;code&gt;Upload 组件&lt;/code&gt;(下称‘Uplo
      
    
    </summary>
    
      <category term="React" scheme="http://blog.xieyangogo.cn/categories/React/"/>
    
    
      <category term="React" scheme="http://blog.xieyangogo.cn/tags/React/"/>
    
      <category term="Ant Design" scheme="http://blog.xieyangogo.cn/tags/Ant-Design/"/>
    
      <category term="File" scheme="http://blog.xieyangogo.cn/tags/File/"/>
    
      <category term="Upload" scheme="http://blog.xieyangogo.cn/tags/Upload/"/>
    
  </entry>
  
  <entry>
    <title>react获取ref的值为undefined</title>
    <link href="http://blog.xieyangogo.cn/2018/04/25/react%E8%8E%B7%E5%8F%96ref%E7%9A%84%E5%80%BC%E4%B8%BAundefined/"/>
    <id>http://blog.xieyangogo.cn/2018/04/25/react获取ref的值为undefined/</id>
    <published>2018-04-25T04:32:03.000Z</published>
    <updated>2018-07-17T08:13:05.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1、问题描述"><a href="#1、问题描述" class="headerlink" title="1、问题描述"></a>1、问题描述</h3><p>在 render() 里面某个DOM 结点上定义了 ref=’xxx’ 后，在生命周期里面使用 this.refs.xxx 获取 xxx 结点，控制台报错 xxx 结点 undefined。</p><h3 id="2、原因分析"><a href="#2、原因分析" class="headerlink" title="2、原因分析"></a>2、原因分析</h3><p><img src="/images/bVCOam.png" alt=""><br>根据 react 的机制（见上图），render() 方法执行后会进入生命周期 componentDidMount 中，正常情况下在 componentDidMount 中可以使用 this.refs.xxx 获取在 render() 中定义的 ref 属性，但是这里出现了 undefined ，即 this.refs.xxx 的值为 undefined。</p><p>为什么会出现这样的情况呢？<br>在 render() 内，rerutn（DOM结构）之前有一句 return false，导致在 DOM 中定义的 ref=’xxx’ 没被渲染，故而 react 取不到 xxx 的值（主要是在检测数据的合法性方面会导致这个问题）</p><h3 id="3、解决方案"><a href="#3、解决方案" class="headerlink" title="3、解决方案"></a>3、解决方案</h3><p>1、 在 componentDidMount 中取值时检测一下 xxx 结点是否已被定义：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; keyPersonnelChart &#125; = <span class="keyword">this</span>.refs</span><br><span class="line"><span class="keyword">if</span> ( xxx !== <span class="literal">undefined</span>) &#123;</span><br><span class="line"><span class="comment">// do something</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>2、不要在 render() 中 return(DOM结构) 之前使用 return false 语句，延伸出 不要在父级检测数据的合法性，直接将这一步放到子组件中进行。例如：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">    <span class="keyword">if</span> ( !<span class="keyword">this</span>.props.data ) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">    &lt;div ref=<span class="string">'xxx'</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;1、问题描述&quot;&gt;&lt;a href=&quot;#1、问题描述&quot; class=&quot;headerlink&quot; title=&quot;1、问题描述&quot;&gt;&lt;/a&gt;1、问题描述&lt;/h3&gt;&lt;p&gt;在 render() 里面某个DOM 结点上定义了 ref=’xxx’ 后，在生命周期里面使用 this.r
      
    
    </summary>
    
    
      <category term="react" scheme="http://blog.xieyangogo.cn/tags/react/"/>
    
      <category term="ref" scheme="http://blog.xieyangogo.cn/tags/ref/"/>
    
  </entry>
  
  <entry>
    <title>sass基础入门</title>
    <link href="http://blog.xieyangogo.cn/2018/04/10/sass%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8/"/>
    <id>http://blog.xieyangogo.cn/2018/04/10/sass基础入门/</id>
    <published>2018-04-10T09:10:12.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、-SASS"><a href="#一、-SASS" class="headerlink" title="一、 SASS"></a>一、 SASS</h1><p>sass 采用编程式的写法来编写样式表，最后编译成我们常见的css样式表。</p><h1 id="二、-安装"><a href="#二、-安装" class="headerlink" title="二、 安装"></a>二、 安装</h1><ul><li><p>SASS 由 Ruby 语言编写，但是不懂Ruby，不妨碍我们学习使用 SASS，因为两者之间没有关联性。<br>唯一的条件是必须在 Ruby 环境下运行 SASS。</p></li><li><p>SASS 是普通的文本文件，兼容 css 语法。</p></li><li><p>SASS 文件的后缀名为 .sass 或者 .scss。<strong>严重推荐使用 .scss 写法</strong></p></li></ul><a id="more"></a><p>.sass 语法</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">body</span><br><span class="line">  background: #eee</span><br><span class="line">  font-size: 12px</span><br><span class="line">p</span><br><span class="line">  background: #0982c1</span><br><span class="line">  span</span><br><span class="line">    color: red</span><br></pre></td></tr></table></figure><p>.scss 语法</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="number">#eee</span>;</span><br><span class="line">  <span class="attribute">font-size</span>:<span class="number">12px</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="number">#0982c1</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="selector-tag">span</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: red;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="https://rubyinstaller.org/downloads/" target="_blank" rel="noopener">Ruby 下载</a></p><p><a href="https://www.sass.hk/" target="_blank" rel="noopener">sass中文网</a></p><p>安装 Ruby 之后，安装 SASS</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gem install sass</span><br></pre></td></tr></table></figure><h1 id="三、-命令"><a href="#三、-命令" class="headerlink" title="三、 命令"></a>三、 命令</h1><p>显示 test.scss 文件编译后的 css 代码：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass test.scss</span><br></pre></td></tr></table></figure><p>将 test.scss 文件编译后的 css 代码保存到 test.css 文件：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass test.scss test.css</span><br></pre></td></tr></table></figure><p>或者写成</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass test.scss:test.css</span><br></pre></td></tr></table></figure><h2 id="1-编译风格"><a href="#1-编译风格" class="headerlink" title="1. 编译风格"></a>1. 编译风格</h2><ul><li><p>nested：嵌套缩进的css代码，默认</p></li><li><p>expanded：没有缩进的、扩展的css代码</p></li><li><p>compact：简洁格式的css代码</p></li><li><p>compressed：压缩后的css代码，一般用于生产环境</p></li></ul><h3 id="i-使用-nested-风格"><a href="#i-使用-nested-风格" class="headerlink" title="i. 使用 nested 风格"></a>i. 使用 nested 风格</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass test.scss test.css --style nested</span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="number">#eee</span>;</span><br><span class="line">    <span class="attribute">font-size</span>: <span class="number">12px</span>;&#125;</span><br><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="number">#0982c1</span>;&#125; </span><br><span class="line"><span class="selector-tag">p</span> <span class="selector-tag">span</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: red;&#125;</span><br></pre></td></tr></table></figure><h3 id="ii-使用-expanded-风格"><a href="#ii-使用-expanded-风格" class="headerlink" title="ii. 使用 expanded 风格"></a>ii. 使用 expanded 风格</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass test.scss test.css --style expanded</span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="number">#eee</span>;</span><br><span class="line">    <span class="attribute">font-size</span>:<span class="number">12px</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="number">#0982c1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">p</span> <span class="selector-tag">span</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="iii-使用-compact-风格"><a href="#iii-使用-compact-风格" class="headerlink" title="iii. 使用 compact 风格"></a>iii. 使用 compact 风格</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass test.scss test.css --style compact</span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;<span class="attribute">background</span>: <span class="number">#eee</span>;<span class="attribute">font-size</span>:<span class="number">12px</span>;&#125;</span><br><span class="line"><span class="selector-tag">p</span> &#123;<span class="attribute">background</span>: <span class="number">#0982c1</span>;&#125;</span><br><span class="line"><span class="selector-tag">p</span> <span class="selector-tag">span</span> &#123;<span class="attribute">color</span>: red;&#125;</span><br></pre></td></tr></table></figure><h3 id="iv-使用-compressed-风格"><a href="#iv-使用-compressed-风格" class="headerlink" title="iv. 使用 compressed 风格"></a>iv. 使用 compressed 风格</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass test.scss test.css --style compressed</span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span>&#123;<span class="attribute">background</span>:<span class="number">#eee</span>;<span class="attribute">font-size</span>:<span class="number">12px</span>;&#125;<span class="selector-tag">p</span>&#123;<span class="attribute">background</span>:<span class="number">#0982c1</span>;&#125;<span class="selector-tag">p</span> <span class="selector-tag">span</span>&#123;<span class="attribute">color</span>:red;&#125;</span><br></pre></td></tr></table></figure><h2 id="2-监听某个文件或目录，一旦文件被修改，立即编译"><a href="#2-监听某个文件或目录，一旦文件被修改，立即编译" class="headerlink" title="2. 监听某个文件或目录，一旦文件被修改，立即编译"></a>2. 监听某个文件或目录，一旦文件被修改，立即编译</h2><p>监听一个文件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass --watch test.scss test.css</span><br></pre></td></tr></table></figure></p><p>监听一个目录<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass --watch app/sass output/stylesheets</span><br></pre></td></tr></table></figure></p><h1 id="四、-基本写法"><a href="#四、-基本写法" class="headerlink" title="四、 基本写法"></a>四、 基本写法</h1><h2 id="1-File-Encoding"><a href="#1-File-Encoding" class="headerlink" title="1. File Encoding"></a>1. File Encoding</h2><p>如果 SASS 文件中存在中文等其他字符，一般是注释，请在文件第一行写上：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">charset</span> <span class="string">"utf-8"</span>;</span><br></pre></td></tr></table></figure><p><strong><a href="http://www.ruanyifeng.com/blog/2012/06/sass.html" target="_blank" rel="noopener">参考文档</a></strong></p><h2 id="2-变量"><a href="#2-变量" class="headerlink" title="2. 变量"></a>2. 变量</h2><p>所有变量以 $ 开头</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$width</span>: <span class="number">10px</span> !default; <span class="comment">// 默认变量</span></span><br><span class="line"><span class="variable">$height</span>: <span class="number">10px</span> !global; <span class="comment">// 全局变量</span></span><br></pre></td></tr></table></figure><h2 id="3-变量的作用域"><a href="#3-变量的作用域" class="headerlink" title="3. 变量的作用域"></a>3. 变量的作用域</h2><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$color</span>: blue; <span class="comment">// 定义变量1</span></span><br><span class="line"><span class="selector-tag">nav</span> &#123;</span><br><span class="line">  <span class="selector-tag">span</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: <span class="variable">$color</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="selector-tag">ul</span> &#123;</span><br><span class="line">    <span class="variable">$color</span>: red; <span class="comment">// 定义局部变量2，此处不会改变变量1（$color: blue）的值。</span></span><br><span class="line">    <span class="attribute">color</span>: <span class="variable">$color</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="selector-tag">p</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: <span class="variable">$color</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">nav</span> <span class="selector-tag">span</span>&#123;</span><br><span class="line">  <span class="attribute">color</span>: blue;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">nav</span> <span class="selector-tag">ul</span>&#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">nav</span> <span class="selector-tag">p</span>&#123;</span><br><span class="line">  <span class="attribute">color</span>: blue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="4-选择器嵌套"><a href="#4-选择器嵌套" class="headerlink" title="4. 选择器嵌套"></a>4. 选择器嵌套</h2><blockquote><p>使用 &amp; 表示父级</p></blockquote><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#top_nav</span> &#123;</span><br><span class="line">  <span class="selector-tag">a</span> &#123;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">10px</span>;</span><br><span class="line">    <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line">    </span><br><span class="line">    &amp;:hover &#123; <span class="comment">// 父级（a元素）的hover状态</span></span><br><span class="line">      <span class="attribute">color</span>: <span class="variable">$color</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#top_nav</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">10px</span>;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#top_nav</span> <span class="selector-tag">a</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#ddd</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="5-属性嵌套"><a href="#5-属性嵌套" class="headerlink" title="5. 属性嵌套"></a>5. 属性嵌套</h2><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.wrap</span> &#123;</span><br><span class="line">  <span class="attribute">font</span>: &#123;</span><br><span class="line">    size: <span class="number">14px</span>;</span><br><span class="line">    weight: bold;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.wrap</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">14px</span>;</span><br><span class="line">  <span class="attribute">font-weight</span>: bold;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="6-混合申明"><a href="#6-混合申明" class="headerlink" title="6. 混合申明"></a>6. 混合申明</h2><h3 id="i-无参数"><a href="#i-无参数" class="headerlink" title="i. 无参数"></a>i. 无参数</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">mixin</span> radius1 &#123;</span><br><span class="line">  -webkit-<span class="attribute">border-radius</span>: <span class="number">3px</span>;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">3px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.r1</span> &#123;</span><br><span class="line">  @<span class="keyword">include</span> radius1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ii-有参数（-变量-默认值-）"><a href="#ii-有参数（-变量-默认值-）" class="headerlink" title="ii. 有参数（ 变量[:默认值] ）"></a>ii. 有参数（ 变量[:默认值] ）</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">mixin</span> radius2(<span class="variable">$radius</span>: 3px) &#123;</span><br><span class="line">  -webkit-<span class="attribute">border-radius</span>: <span class="variable">$radius</span>;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="variable">$radius</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.r2-1</span> &#123;</span><br><span class="line">  @<span class="keyword">include</span> radius2; <span class="comment">// 使用默认值</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.r2-2</span> &#123;</span><br><span class="line">  @<span class="keyword">include</span> radius2(<span class="number">20px</span>); <span class="comment">// 传递自定义参数</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="iii-多个参数"><a href="#iii-多个参数" class="headerlink" title="iii. 多个参数"></a>iii. 多个参数</h3><ul><li>声明一个混合boxSize，有两个参数高度和宽度</li><li>在.box中调用此混合并为赋值宽度100px，高度200px</li></ul><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">mixin</span> boxSize(<span class="variable">$width</span>: 10px, <span class="variable">$height</span>: 100px) &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="variable">$width</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="variable">$height</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.box</span> &#123;</span><br><span class="line">  @<span class="keyword">include</span> boxSize(<span class="number">100px</span>, 200px);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 另一种写法 */</span></span><br><span class="line"><span class="selector-class">.box2</span> &#123;</span><br><span class="line">  @<span class="keyword">include</span> boxSize(<span class="variable">$width</span>: 100px, <span class="variable">$height</span>: 200px);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box</span>,</span><br><span class="line"><span class="selector-class">.box2</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="iv-多组值参数"><a href="#iv-多组值参数" class="headerlink" title="iv. 多组值参数"></a>iv. 多组值参数</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">mixin</span> box-shadow(<span class="variable">$shadow</span>...) &#123;</span><br><span class="line">  -webkit-<span class="attribute">box-shadow</span>: <span class="variable">$shadow</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="variable">$shadow</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.box-shadow</span> &#123;</span><br><span class="line">  <span class="attribute">border</span>: <span class="variable">$width</span> solid <span class="variable">$color</span>;</span><br><span class="line">  @<span class="keyword">include</span> box-shadow(<span class="number">0</span> 2px 2px red, 0 3px 3px blue, 0 4px 4px yellow);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="7-继承"><a href="#7-继承" class="headerlink" title="7. 继承"></a>7. 继承</h2><h3 id="i-简单继承"><a href="#i-简单继承" class="headerlink" title="i. 简单继承"></a>i. 简单继承</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="variable">$color</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">a</span>:hover &#123;</span><br><span class="line">  <span class="attribute">text-decoration</span>: underline;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.disabled</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: gray;</span><br><span class="line">  @<span class="keyword">extend</span> a; <span class="comment">// 继承a元素</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.hoverlink</span> &#123;</span><br><span class="line">  @<span class="keyword">extend</span> a:hover; <span class="comment">// 继承a元素的hover状态</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ii-继承多个选择器"><a href="#ii-继承多个选择器" class="headerlink" title="ii. 继承多个选择器"></a>ii. 继承多个选择器</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.one</span> &#123;</span><br><span class="line">  @<span class="keyword">include</span> boxSize(<span class="number">100px</span>, 100px); <span class="comment">//使用混合</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.two</span> &#123;</span><br><span class="line">  @<span class="keyword">extend</span> .one;</span><br><span class="line">  @<span class="keyword">extend</span> .three;</span><br><span class="line">  <span class="attribute">background</span>: red;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#000</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.three</span> &#123;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">10px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="8-选择器占位符"><a href="#8-选择器占位符" class="headerlink" title="8. 选择器占位符 %"></a>8. 选择器占位符 %</h2><p>没有被继承的选择器占位不会被编译成css</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">%bgd &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="number">#fff</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">%mar &#123;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">5px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">%pad &#123;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">5px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn</span> &#123;</span><br><span class="line">  @<span class="keyword">extend</span> %mar;</span><br><span class="line">  @<span class="keyword">extend</span> %pad;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.block</span> &#123;</span><br><span class="line">  @<span class="keyword">extend</span> %mar;</span><br><span class="line">  <span class="selector-tag">span</span> &#123;</span><br><span class="line">    @<span class="keyword">extend</span> %pad;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后 （此处没有编译scss中的占位选择器 %bgd ）</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.btn</span>, <span class="selector-class">.block</span> &#123;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">5px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn</span>, <span class="selector-class">.block</span> <span class="selector-tag">span</span> &#123;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">5px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="五、-进阶"><a href="#五、-进阶" class="headerlink" title="五、 进阶"></a>五、 进阶</h1><h2 id="1-数据类型"><a href="#1-数据类型" class="headerlink" title="1. 数据类型"></a>1. 数据类型</h2><p>SassScript 支持的6种数据类型</p><ul><li>数字（例如 1.2、13、10px）</li><li>文本字符串，无论是否有引号（例如 “foo”、’bar’、baz）</li><li>颜色（例如 blue、#04a3f9、rgba(255, 0, 0, 0.5)）</li><li>布尔值（例如 true、false）</li><li>空值（例如 null）</li><li>值列表，用空格或逗号分隔（例如 1.5em 1em 0 2em、 Helvetica, Arial, sans-serif）</li></ul><p>SassScript 还支持所有其他 CSS 属性值类型，例如 Unicode 范围和 !important 声明。然而，它不会对这些类型做特殊处理。 它们只会被当做不带引号的字符串看待。</p><h2 id="2-插值语句"><a href="#2-插值语句" class="headerlink" title="2. 插值语句 #{}"></a>2. 插值语句 #{}</h2><p>使用 #{} 插值语句 (interpolation) 时，有引号的字符串将被编译为无引号字符串<br>如果你希望在纯 CSS 中使用变量和斜杠（/）， 你可以用 #{} 包住变量。</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="variable">$font-size</span>: <span class="number">12px</span>;</span><br><span class="line">  <span class="variable">$line-height</span>: <span class="number">30px</span>;</span><br><span class="line">  <span class="attribute">font</span>: <span class="variable">$font-size</span>/<span class="variable">$line-height</span>;       <span class="comment">// =&gt; font: 0.4</span></span><br><span class="line">  <span class="attribute">font</span>: #&#123;<span class="variable">$font-size</span>&#125;/#&#123;<span class="variable">$line-height</span>&#125;; <span class="comment">// =&gt; font: 12px/30px</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">font</span>: <span class="number">12px</span>/<span class="number">30px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在文本字符串中，#{} 形式的表达式可以被用来在字符串中添加动态值：<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">"I ate #&#123;5 + 10&#125; pies!"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">"I ate 15 pies!"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="3-字符串运算："><a href="#3-字符串运算：" class="headerlink" title="3. 字符串运算："></a>3. 字符串运算：</h2><h3 id="i-“-”-可以用来连接字符"><a href="#i-“-”-可以用来连接字符" class="headerlink" title="i. “+” 可以用来连接字符"></a>i. “+” 可以用来连接字符</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">cursor</span>: e + -resize;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">cursor</span>: e-resize;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="ii-有引号和无引号的连接规则：遵循-“-”-左边字符串的规则"><a href="#ii-有引号和无引号的连接规则：遵循-“-”-左边字符串的规则" class="headerlink" title="ii. 有引号和无引号的连接规则：遵循 “+” 左边字符串的规则"></a>ii. 有引号和无引号的连接规则：遵循 “+” 左边字符串的规则</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span>:before &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">"Foo "</span> + Bar;</span><br><span class="line">  <span class="attribute">font-family</span>: sans- + <span class="string">"serif"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">"Foo Bar"</span>;</span><br><span class="line">  <span class="attribute">font-family</span>: sans-serif;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="4-空值（null）会被视作空字符串"><a href="#4-空值（null）会被视作空字符串" class="headerlink" title="4. 空值（null）会被视作空字符串"></a>4. 空值（null）会被视作空字符串</h2><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$value</span>: null;</span><br><span class="line"><span class="selector-tag">p</span>:before &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">"I ate #&#123;$value&#125; pies!"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">"I ate  pies!"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="5-if-判断"><a href="#5-if-判断" class="headerlink" title="5. @if 判断"></a>5. @if 判断</h2><p>除非你的代码中有偏复杂的逻辑，否则没必要在日常开发的样式表中使用条件语句。<br>实际上，条件语句主要适用于库和框架。<br>无论何时，如果你感觉需要它们，请遵守下述准则：</p><ul><li>除非必要，不然不需要括号；</li><li>务必在 @if 之前添加空行；</li><li>务必在左开大括号( { )后换行；</li><li>@else 语句和它前面的右闭大括号( } )写在同一行；</li><li>务必在右闭大括号( } )后添加空行；</li><li>除非下一行还是右闭大括号( } )，那么就在最后一个右闭大括号( } )后添加空行。</li></ul><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  @<span class="keyword">if</span> 1 + 1 == 2 &#123;</span><br><span class="line">    <span class="attribute">border</span>: <span class="number">1px</span> solid;</span><br><span class="line">  &#125;</span><br><span class="line">  @<span class="keyword">if</span> 5 &lt; 3 &#123;</span><br><span class="line">    <span class="attribute">border</span>: <span class="number">2px</span> dotted;</span><br><span class="line">  &#125;</span><br><span class="line">  @<span class="keyword">if</span> null &#123;</span><br><span class="line">    <span class="attribute">border</span>: <span class="number">3px</span> double;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>@if @else 结合使用方法</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  @<span class="keyword">if</span> 1 + 1 == 2 &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">30px</span>;</span><br><span class="line">  &#125; @<span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">30px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="6-三目运算符"><a href="#6-三目运算符" class="headerlink" title="6. 三目运算符"></a>6. 三目运算符</h2><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">if(<span class="variable">$condition</span>, <span class="variable">$condition_true</span>, <span class="variable">$condition_false</span>)</span><br></pre></td></tr></table></figure><p>三个参数分别表示：条件，条件为真的值，条件为假的值。</p><p><strong>PS：这个 if 没有 @ 前缀，和 @if 判断不同</strong></p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$fontBold</span>: true;</span><br><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">font-weight</span>: if(<span class="variable">$fontBold</span>, bold, normal);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">font-weight</span>: bold;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="7-for-循环"><a href="#7-for-循环" class="headerlink" title="7. for 循环"></a>7. for 循环</h2><p>使用 @for 指令</p><p>@for 循环有两种方式：<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">for</span> <span class="variable">$i</span> from start through end &#123;&#125;</span><br></pre></td></tr></table></figure></p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">for</span> <span class="variable">$i</span> from start to end &#123;&#125;</span><br></pre></td></tr></table></figure><p>$i: 表示变量</p><p>start: 表示起始值</p><p>end: 表示结束值</p><p><strong>区别：through 表示包括 end 这个数，而 to 则不包括 end 这个数</strong></p><h3 id="i-through"><a href="#i-through" class="headerlink" title="i. through"></a>i. through</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">for</span> <span class="variable">$i</span> from 1 through 3 &#123;</span><br><span class="line">  <span class="selector-class">.item-</span>#&#123;<span class="variable">$i</span>&#125; &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">2em</span> * <span class="variable">$i</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.item-1</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">2em</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.item-2</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">4em</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.item-3</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">6em</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="ii-to"><a href="#ii-to" class="headerlink" title="ii. to"></a>ii. to</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">for</span> <span class="variable">$i</span> from 10 to 30 &#123;</span><br><span class="line">  <span class="selector-class">.item-</span>#&#123;<span class="variable">$i</span>&#125; &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">2em</span> * <span class="variable">$i</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.item-1</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">2em</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item-2</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">4em</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="8-each-循环"><a href="#8-each-循环" class="headerlink" title="8. each 循环"></a>8. each 循环</h2><p>each 循环就是去遍历一个列表，然后从列表中取出对应的值。</p><p>使用 @each 指令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@each $var in &lt;list&gt;</span><br></pre></td></tr></table></figure><p>$var: 变量名<br>&lt; list &gt;: SassScript 的数据类型–值列表，它将返回一个列表值。</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">each</span> <span class="variable">$animal</span> in puma, egret, salamander &#123;</span><br><span class="line">  .#&#123;<span class="variable">$animal</span>&#125;-<span class="attribute">icon</span> &#123;</span><br><span class="line">    <span class="attribute">background-image</span>: url(<span class="string">'/course/img/#&#123;$animal&#125;.png'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.puma-icon</span> &#123;</span><br><span class="line">  <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">'/course/img/puma.png'</span>);</span><br><span class="line">&#125;``</span><br><span class="line"><span class="selector-class">.egret-icon</span> &#123;</span><br><span class="line">  <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">'/course/img/egret.png'</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.salamander-icon</span> &#123;</span><br><span class="line">  <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">'/course/img/salamander.png'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="9-while循环"><a href="#9-while循环" class="headerlink" title="9. while循环"></a>9. while循环</h2><p>使用 @while 指令</p><p>@while 指令需要 SassScript 表达式，并且会生成不同的样式块。</p><p>直到表达式的值为 false 时停止循环，这个和 for 循环很相似，只要 @while 后面的表达式的值为 true 就会执行。</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$i</span>: <span class="number">6</span>;</span><br><span class="line">@<span class="keyword">while</span> <span class="variable">$i</span> &gt; 0 &#123;</span><br><span class="line">  <span class="selector-class">.item-</span>#&#123;<span class="variable">$i</span>&#125; &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">2em</span> * <span class="variable">$i</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="variable">$i</span>: <span class="variable">$i</span> - <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译后<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.item-6</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">12em</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item-4</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">8em</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item-2</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">4em</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>///////////////////////////////////////////////////////////////////////////////////////////////////////////</p><p>////////////////////////////////////////// 我是华丽的分割线 ///////////////////////////////////</p><p>///////////////////////////////////////////////////////////////////////////////////////////////////////////</p><hr><p><strong>PS： 想开启开挂模式，请继续往下读</strong></p><h1 id="六、-函数"><a href="#六、-函数" class="headerlink" title="六、 函数"></a>六、 函数</h1><h2 id="1-颜色函数"><a href="#1-颜色函数" class="headerlink" title="1. 颜色函数"></a>1. 颜色函数</h2><ul><li><p>rgb($red,$green,$blue)：根据红、绿、蓝三个值创建一个颜色；</p></li><li><p>rgba($red,$green,$blue,$alpha)：根据红、绿、蓝和透明度值创建一个颜色；</p></li><li><p>red($color)：从一个颜色中获取其中红色值；</p></li><li><p>green($color)：从一个颜色中获取其中绿色值；</p></li><li><p>blue($color)：从一个颜色中获取其中蓝色值；</p></li><li><p>mix($color-1,$color-2,[$weight])：把两种颜色混合在一起。</p></li></ul><p><strong>测试：在终端中开启 SASS 的颜色计算</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sass -i</span><br></pre></td></tr></table></figure><p>根据 r:200, g:40, b:88 计算出一个十六进制颜色值: #c82858<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rgb(200,40,88)</span><br></pre></td></tr></table></figure></p><p>根据 #c82858 的 65% 透明度计算出一个 rgba 颜色值: rgba(200, 40, 88, 0.65)<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rgba(<span class="selector-id">#c82858</span>,<span class="selector-class">.65</span>)</span><br></pre></td></tr></table></figure></p><p>从 #c82858 颜色值中得到红色值200 200<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">red(<span class="selector-id">#c82858</span>)</span><br></pre></td></tr></table></figure></p><p>从 #c82858 颜色值中得到绿色值40 40<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">green(<span class="selector-id">#c82858</span>)</span><br></pre></td></tr></table></figure></p><p>从 #c82858 颜色值中得到蓝色值88 88<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">blue(<span class="selector-id">#c82858</span>)</span><br></pre></td></tr></table></figure></p><p>把 #c82858 和 rgba(200, 40, 88, .65) 两颜色按比例混合得到一个新颜色 rgba(200, 40, 80, 0.65105)<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mix(<span class="selector-id">#c82858</span>, rgba(200,40,80,<span class="selector-class">.65</span>), <span class="selector-class">.3</span>)</span><br></pre></td></tr></table></figure></p><h2 id="2-列表函数"><a href="#2-列表函数" class="headerlink" title="2. 列表函数"></a>2. 列表函数</h2><ul><li><p>length($list)：返回一个列表的长度值；</p><blockquote><p>PS: length()函数中的列表参数之间使用空格隔开，不能使用逗号，否则函数将会出错</p></blockquote></li><li><p>nth($list, $n)：返回一个列表中指定的某个标签值</p></li><li><p>join($list1, $list2, [$separator])：将两个列给连接在一起，变成一个列表；</p></li><li><p>append($list1, $val, [$separator])：将某个值放在列表的最后；</p></li><li><p>zip($lists…)：将几个列表结合成一个多维的列表；</p></li><li><p>index($list, $value)：返回一个值在列表中的位置值。</p></li></ul><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">length(10px); <span class="comment">// 1</span></span><br><span class="line">length(10px 20px (<span class="attribute">border</span> 1px solid) 2em); <span class="comment">// 4</span></span><br><span class="line">length(<span class="attribute">border</span> 1px solid); <span class="comment">// 3</span></span><br><span class="line">length(10px,20px,(<span class="attribute">border</span> 1px solid),2em); <span class="comment">// SyntaxError: wrong number of arguments (4 for 1) for `length'</span></span><br><span class="line"></span><br><span class="line">index(1px solid red, 1px); <span class="comment">// 1</span></span><br><span class="line">index(1px solid red, solid); <span class="comment">// 2</span></span><br><span class="line">index(1px solid red, red); <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1px "solid" #008000, 2px "dashed" #0000ff, 3px "dotted" #ff0000</span></span><br><span class="line">zip(1px 2px 3px, solid dashed dotted, green blue red);</span><br></pre></td></tr></table></figure><h2 id="3-更多函数请查阅文档"><a href="#3-更多函数请查阅文档" class="headerlink" title="3. 更多函数请查阅文档"></a>3. 更多函数请<a href="http://sass-lang.com/documentation/Sass/Script/Functions.html" target="_blank" rel="noopener">查阅文档</a></h2><p><strong>其实 SASS 函数在一般的项目中基本用不上，除了写写插件装装B，能用上的场合是…… 思来想去，也没想出来那里用最合适！！</strong></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;一、-SASS&quot;&gt;&lt;a href=&quot;#一、-SASS&quot; class=&quot;headerlink&quot; title=&quot;一、 SASS&quot;&gt;&lt;/a&gt;一、 SASS&lt;/h1&gt;&lt;p&gt;sass 采用编程式的写法来编写样式表，最后编译成我们常见的css样式表。&lt;/p&gt;
&lt;h1 id=&quot;二、-安装&quot;&gt;&lt;a href=&quot;#二、-安装&quot; class=&quot;headerlink&quot; title=&quot;二、 安装&quot;&gt;&lt;/a&gt;二、 安装&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SASS 由 Ruby 语言编写，但是不懂Ruby，不妨碍我们学习使用 SASS，因为两者之间没有关联性。&lt;br&gt;唯一的条件是必须在 Ruby 环境下运行 SASS。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SASS 是普通的文本文件，兼容 css 语法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SASS 文件的后缀名为 .sass 或者 .scss。&lt;strong&gt;严重推荐使用 .scss 写法&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="css" scheme="http://blog.xieyangogo.cn/categories/css/"/>
    
    
      <category term="css" scheme="http://blog.xieyangogo.cn/tags/css/"/>
    
      <category term="sass" scheme="http://blog.xieyangogo.cn/tags/sass/"/>
    
      <category term="css预处理器" scheme="http://blog.xieyangogo.cn/tags/css%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>hexo 文章管理</title>
    <link href="http://blog.xieyangogo.cn/2017/09/28/hexo%20%E6%96%87%E7%AB%A0%E7%AE%A1%E7%90%86/"/>
    <id>http://blog.xieyangogo.cn/2017/09/28/hexo 文章管理/</id>
    <published>2017-09-28T06:25:34.000Z</published>
    <updated>2019-01-21T03:17:12.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>前提：已经使用 npm 预装好了 <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a> 所需的各种依赖包。<br>查阅 <a href="https://hexo.io/docs/" target="_blank" rel="noopener">文档</a> 以获取更多信息。如果在使用Hexo时遇到任何问题，您可以在 <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> 中找到答案，或者您可以在 <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a> 上询问。</p></blockquote><h2 id="一、-流程"><a href="#一、-流程" class="headerlink" title="一、 流程"></a>一、 流程</h2><h3 id="1-全局安装-hexo-命令库"><a href="#1-全局安装-hexo-命令库" class="headerlink" title="1. 全局安装 hexo 命令库"></a>1. 全局安装 hexo 命令库</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-cli -g</span><br></pre></td></tr></table></figure><a id="more"></a><h3 id="2-创建一个博客项目（一般只在首次创建，后续操作从第三步开始即可）"><a href="#2-创建一个博客项目（一般只在首次创建，后续操作从第三步开始即可）" class="headerlink" title="2. 创建一个博客项目（一般只在首次创建，后续操作从第三步开始即可）"></a>2. 创建一个博客项目（一般只在首次创建，后续操作从第三步开始即可）</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo init blog</span><br></pre></td></tr></table></figure><h3 id="3-进入博客项目"><a href="#3-进入博客项目" class="headerlink" title="3. 进入博客项目"></a>3. 进入博客项目</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd blog</span><br></pre></td></tr></table></figure><h3 id="4-新建-amp-编辑文章"><a href="#4-新建-amp-编辑文章" class="headerlink" title="4. 新建&amp;编辑文章"></a>4. 新建&amp;编辑文章</h3><p>命令行中输入以下指令以创建新文章：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new &quot;new article&quot;</span><br></pre></td></tr></table></figure><blockquote><p>详细信息: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p></blockquote><p>执行后在 source/_posts 中生成了一个 new-article.md 文件</p><p>文件中的初始内容为：<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">title:</span> <span class="string">new</span> <span class="string">article</span></span><br><span class="line"><span class="attr">date:</span> <span class="number">2014</span><span class="bullet">-11</span><span class="bullet">-01</span> <span class="number">20</span><span class="string">:10:33</span></span><br><span class="line"><span class="attr">tags:</span></span><br><span class="line"><span class="meta">---</span></span><br></pre></td></tr></table></figure></p><p>使用三个 “-” 字符包裹着页面的属性，采用 yaml 格式书写</p><p>接着是正文，采用 <a href="http://wowubuntu.com/markdown/index.html" target="_blank" rel="noopener">markdown</a> 格式书写</p><blockquote><p>注意：如果使用了第三方服务，如 algolia 搜索服务，在新建了页面后，记得在根目录执行<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo algolia/a</span><br></pre></td></tr></table></figure></p></blockquote><blockquote><p>以更新搜索内容</p></blockquote><h3 id="5-生成静态文件"><a href="#5-生成静态文件" class="headerlink" title="5. 生成静态文件"></a>5. 生成静态文件</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo generate/g</span><br></pre></td></tr></table></figure><blockquote><p>详细信息: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p></blockquote><h3 id="6-本地服务预览"><a href="#6-本地服务预览" class="headerlink" title="6. 本地服务预览"></a>6. 本地服务预览</h3><p>命令行中输入以下指令启动本地服务器<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo server/s</span><br></pre></td></tr></table></figure></p><blockquote><p>详细信息: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p></blockquote><p>然后在浏览器窗口打开 localhost:4000</p><blockquote><p>注意：在新建、删除或修改文章之后，不需要重新启动服务器，只需刷新一下页面就能预览新的改动</p></blockquote><h3 id="7-发布到服务器上"><a href="#7-发布到服务器上" class="headerlink" title="7. 发布到服务器上"></a>7. 发布到服务器上</h3><p>命令行中输入以下指令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo deploy/d</span><br></pre></td></tr></table></figure><blockquote><p>详细信息: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p></blockquote><blockquote><p>前提:必须在站点配置文件 _config.yml 中做以下配置：<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">deploy:</span></span><br><span class="line"><span class="attr">  type:</span> <span class="string">git</span></span><br><span class="line"><span class="attr">  repository:</span> <span class="string">git@github.com:用户名/仓库名.git</span></span><br><span class="line"><span class="attr">  branch:</span> <span class="string">master</span></span><br></pre></td></tr></table></figure></p></blockquote><blockquote><p>这里只会把根目录中的 public 整个文件夹发布到服务器，因为这里面才是所有工作的产出物——我们的博客静态文件。我们之前所有操作都是在为这个产出做准备工作。</p></blockquote><h2 id="二、-属性"><a href="#二、-属性" class="headerlink" title="二、 属性"></a>二、 属性</h2><p>文章可以拥有如下属性：</p><table><thead><tr><th>Setting</th><th>Description</th><th>Default</th></tr></thead><tbody><tr><td>layout</td><td>Layout</td><td>post或page</td></tr><tr><td>title</td><td>文章的标题</td><td></td></tr><tr><td>date</td><td>文章的创建日期</td><td>文章的创建日期</td></tr><tr><td>updated</td><td>文章的更新日期</td><td>文章的更新日期</td></tr><tr><td>comments</td><td>是否开启评论</td><td>true</td></tr><tr><td>tags</td><td>标签</td><td></td></tr><tr><td>categories</td><td>分类</td><td></td></tr><tr><td>permalink</td><td>url中的名字</td><td>文件名</td></tr></tbody></table><h2 id="三、-分类和标签的写法"><a href="#三、-分类和标签的写法" class="headerlink" title="三、 分类和标签的写法"></a>三、 分类和标签的写法</h2><p>在正文之前的配置项中配置相应的文本即可</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">categories:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">日记</span></span><br><span class="line"><span class="attr">tags:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">Hexo</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">node.js</span></span><br><span class="line">  </span><br><span class="line"><span class="comment"># 或者写成数组的形式</span></span><br><span class="line"><span class="attr">categories:</span> <span class="string">日记</span></span><br><span class="line"><span class="attr">tags:</span> <span class="string">[Hexo,</span> <span class="string">node.js]</span></span><br></pre></td></tr></table></figure><h2 id="四、-首页摘要"><a href="#四、-首页摘要" class="headerlink" title="四、 首页摘要"></a>四、 首页摘要</h2><p>同 wordpress 一样，在正文中 <!--more--> 之上的内容显示为摘要。</p><h2 id="五、-layout"><a href="#五、-layout" class="headerlink" title="五、 layout"></a>五、 layout</h2><p>如果你修改了 layout，在 scaffolds 文件夹里一定要有名字对应的模版文件，否则会采用默认模版。</p><h2 id="六、-文件名"><a href="#六、-文件名" class="headerlink" title="六、 文件名"></a>六、 文件名</h2><p>在配置文件中的 new_post_name 项可以设置文件名，默认为 :title，也就是你在命令行输入的名字。<br>文件名可以为下面几个变量和字符串常量的任意组合：</p><table><thead><tr><th>Variable</th><th>Description</th></tr></thead><tbody><tr><td>:title</td><td>Escaped title (lower case and replace spaces with dash)</td></tr><tr><td>:year</td><td>Created year (4-digit)</td></tr><tr><td>:month</td><td>Created month (2-digit)</td></tr><tr><td>:i_month</td><td>Created month (Without leading zeros)</td></tr><tr><td>:day</td><td>Created day (2-digit)</td></tr><tr><td>:i_day</td><td>Created day (Without leading zeros)</td></tr></tbody></table><h2 id="七、-草稿"><a href="#七、-草稿" class="headerlink" title="七、 草稿"></a>七、 草稿</h2><p>草稿相当于很多博客都有的 “私密文章” 功能。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new draft &quot;new draft&quot;</span><br></pre></td></tr></table></figure></p><p>会在 source/_drafts 目录下生成一个 new-draft.md 文件。但是这个文件不被显示在页面上，链接也访问不到。也就是说如果你想把某一篇文章移除显示，又不舍得删除，可以把它移动到 _drafts 目录之中。</p><p>如果你希望强行预览草稿，更改配置文件：<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">render_drafts:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure></p><p>或者，如下方式启动server：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo server --drafts</span><br></pre></td></tr></table></figure></p><p>下面这条命令可以把草稿变成文章，或者页面：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo publish [layout] &lt;filename&gt;</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;前提：已经使用 npm 预装好了 &lt;a href=&quot;https://hexo.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hexo&lt;/a&gt; 所需的各种依赖包。&lt;br&gt;查阅 &lt;a href=&quot;https://hexo.io/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;文档&lt;/a&gt; 以获取更多信息。如果在使用Hexo时遇到任何问题，您可以在 &lt;a href=&quot;https://hexo.io/docs/troubleshooting.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;troubleshooting&lt;/a&gt; 中找到答案，或者您可以在 &lt;a href=&quot;https://github.com/hexojs/hexo/issues&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt; 上询问。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;一、-流程&quot;&gt;&lt;a href=&quot;#一、-流程&quot; class=&quot;headerlink&quot; title=&quot;一、 流程&quot;&gt;&lt;/a&gt;一、 流程&lt;/h2&gt;&lt;h3 id=&quot;1-全局安装-hexo-命令库&quot;&gt;&lt;a href=&quot;#1-全局安装-hexo-命令库&quot; class=&quot;headerlink&quot; title=&quot;1. 全局安装 hexo 命令库&quot;&gt;&lt;/a&gt;1. 全局安装 hexo 命令库&lt;/h3&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;npm install hexo-cli -g&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="hexo" scheme="http://blog.xieyangogo.cn/categories/hexo/"/>
    
    
      <category term="前端" scheme="http://blog.xieyangogo.cn/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="hexo" scheme="http://blog.xieyangogo.cn/tags/hexo/"/>
    
  </entry>
  
  <entry>
    <title>D3学习笔记</title>
    <link href="http://blog.xieyangogo.cn/2017/07/18/D3%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>http://blog.xieyangogo.cn/2017/07/18/D3学习笔记/</id>
    <published>2017-07-18T03:01:00.000Z</published>
    <updated>2018-09-05T03:00:25.000Z</updated>
    
    <content type="html"><![CDATA[<p><a href="/pdf/D3%20学习笔记.pdf" target="_blank">在新标签页面打开</a></p><embed id="pdfPlayer" src="/pdf/D3%20学习笔记.pdf" type="application/pdf" width="100%"><script type="text/javascript">    document.getElementById('pdfPlayer').style.height = window.innerHeight + 'px'</script>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;/pdf/D3%20学习笔记.pdf&quot; target=&quot;_blank&quot;&gt;在新标签页面打开&lt;/a&gt;&lt;/p&gt;
&lt;embed id=&quot;pdfPlayer&quot; src=&quot;/pdf/D3%20学习笔记.pdf&quot; type=&quot;application/pdf&quot; width
      
    
    </summary>
    
      <category term="D3" scheme="http://blog.xieyangogo.cn/categories/D3/"/>
    
    
      <category term="d3" scheme="http://blog.xieyangogo.cn/tags/d3/"/>
    
      <category term="学习笔记" scheme="http://blog.xieyangogo.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>javaScript动画基础</title>
    <link href="http://blog.xieyangogo.cn/2014/12/27/javaScript%E5%8A%A8%E7%94%BB%E5%9F%BA%E7%A1%80/"/>
    <id>http://blog.xieyangogo.cn/2014/12/27/javaScript动画基础/</id>
    <published>2014-12-27T07:03:00.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>为了做好导航菜单，有时候需要在菜单下拉的时候实现动画效果，本文记录实现动画核心要用到两个函数，setTimeout 与 setInterval</p></blockquote><h3 id="过程和原理"><a href="#过程和原理" class="headerlink" title="过程和原理"></a>过程和原理</h3><h4 id="实现一个匿名函数并能自己执行"><a href="#实现一个匿名函数并能自己执行" class="headerlink" title="实现一个匿名函数并能自己执行"></a>实现一个匿名函数并能自己执行</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// some code</span></span><br><span class="line">&#125;) ()</span><br></pre></td></tr></table></figure><p>提到闭包，自动执行的效果，在函数后加一对 “()” 表示自动执行，前边的匿名函数需要用 “()” 包起来，这样才能为宿主（我们的BOM环境）理解，里面的 function(){} 这就是个匿名函数。</p><h4 id="实现动画"><a href="#实现动画" class="headerlink" title="实现动画"></a>实现动画</h4><p>以改变一个box的透明度</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"animation"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>要实现 animation 的透明渐变，需要不断改变其透明度 opacity</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    setTimeout((<span class="function"><span class="keyword">function</span> (<span class="params">pos</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            someAnimation(pos);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)(i / <span class="number">10</span>), i * <span class="number">100</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码有一定的难度，首先解释一下 setTimeout 在此处的用法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setTimeout((<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// some code</span></span><br><span class="line">&#125;)(i / <span class="number">10</span>), i * <span class="number">100</span>)</span><br></pre></td></tr></table></figure><p>setTimeout 第一个参数为要执行的函数，第二个参数为时间参数，意为多久后开始执行，因 javascript 没有块的概念，作用域范围是以函数为准的，所以我们这里使用的闭包，实现原理如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure><p>在这里面执行for循环，达到我们想要的结果，如果我们不使用闭包，代码会如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params">pos</span>) </span>&#123;</span><br><span class="line">        someAnimation(pos);</span><br><span class="line">    &#125;(i / <span class="number">10</span>), i * <span class="number">100</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样的for循环只会执行一次，即 i = 9 时。到目前为止的完整代码如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">someAnimation</span>(<span class="params">args</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">document</span>.getElementById(<span class="string">"animation"</span>).style.opacity = args;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">        setTimeout((<span class="function"><span class="keyword">function</span> (<span class="params">pos</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">                someAnimation(pos);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)(i / <span class="number">10</span>), i * <span class="number">100</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure><p>这样实现了 id 为 animation 的 box 透明度从 0 到 1 的一次变化</p><h4 id="使用-setInterval-实现不间断变化"><a href="#使用-setInterval-实现不间断变化" class="headerlink" title="使用 setInterval 实现不间断变化"></a>使用 setInterval 实现不间断变化</h4><p>setInterval 有两个参数，第一个是要执行的函数，第二个是执行间隔时间</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">someAnimation</span>(<span class="params">args</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">document</span>.getElementById(<span class="string">"animation"</span>).style.opacity = args;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    setInterval(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            setTimeout((<span class="function"><span class="keyword">function</span> (<span class="params">pos</span>) </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">                    someAnimation(pos);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;)(i / <span class="number">10</span>), i * <span class="number">100</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="number">2000</span>);</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"</span></span><br><span class="line"><span class="meta">"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">xmlns</span>=<span class="string">"http://www.w3.org/1999/xhtml"</span> <span class="attr">xml:lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html;charset=UTF-8"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">title</span>&gt;</span><span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">style</span> <span class="attr">type</span>=<span class="string">"text/css"</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="undefined">            #animation &#123;</span></span><br><span class="line"><span class="undefined">                width: 500px;</span></span><br><span class="line"><span class="undefined">                height: 350px;</span></span><br><span class="line"><span class="undefined">                background-color: red;</span></span><br><span class="line"><span class="undefined">            &#125;</span></span><br><span class="line"><span class="undefined">        </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"animation"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">            (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">                <span class="function"><span class="keyword">function</span> <span class="title">someAnimation</span>(<span class="params">args</span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">                    <span class="built_in">document</span>.getElementById(<span class="string">"animation"</span>).style.opacity = args;</span></span><br><span class="line"><span class="undefined">                &#125;</span></span><br><span class="line"><span class="undefined">        </span></span><br><span class="line"><span class="javascript">                setInterval(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">                    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span></span><br><span class="line"><span class="javascript">                        setTimeout((<span class="function"><span class="keyword">function</span> (<span class="params">pos</span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">                            <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="undefined">                                someAnimation(pos);</span></span><br><span class="line"><span class="undefined">                            &#125;</span></span><br><span class="line"><span class="undefined">                        &#125;)(i / 10), i * 100)</span></span><br><span class="line"><span class="undefined">                    &#125;</span></span><br><span class="line"><span class="undefined">                &#125;, 2000);</span></span><br><span class="line"><span class="undefined">            &#125;)()</span></span><br><span class="line"><span class="undefined">        </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;为了做好导航菜单，有时候需要在菜单下拉的时候实现动画效果，本文记录实现动画核心要用到两个函数，setTimeout 与 setInterval&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;过程和原理&quot;&gt;&lt;a href=&quot;#过程和原理&quot; cl
      
    
    </summary>
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/categories/javascript/"/>
    
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/tags/javascript/"/>
    
      <category term="动画" scheme="http://blog.xieyangogo.cn/tags/%E5%8A%A8%E7%94%BB/"/>
    
      <category term="setTimeout" scheme="http://blog.xieyangogo.cn/tags/setTimeout/"/>
    
      <category term="setInterval" scheme="http://blog.xieyangogo.cn/tags/setInterval/"/>
    
  </entry>
  
  <entry>
    <title>javascript难点剖析二：closure（闭包）</title>
    <link href="http://blog.xieyangogo.cn/2014/12/23/javascript%E9%9A%BE%E7%82%B9%E5%89%96%E6%9E%90%E4%BA%8C%EF%BC%9Aclosure%EF%BC%88%E9%97%AD%E5%8C%85%EF%BC%89/"/>
    <id>http://blog.xieyangogo.cn/2014/12/23/javascript难点剖析二：closure（闭包）/</id>
    <published>2014-12-23T07:52:00.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>在上一篇“javascript 难点剖析一：prototy（原型）”后, 我们紧接着来理解什么是闭包。闭包虽不是 javascript 的特色功能, 但要理解还真要费那么点工夫。</p></blockquote><blockquote><p>在理解闭包之前, 首先要清楚 javascript 中的作用域只有2种: <strong>全局作用域</strong> 和 <strong>方法作用域</strong>。全局作用域很好理解了, 方法作用域就是指一个 function 形成一个独立的作用域, 而且方法作用域还能够嵌套。</p></blockquote><p>&ensp;<br>与别的语言不同的是: 花括号 “{}” 不能形成一个独立的作用域, 例如 Java 中的作用域</p><h3 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> global = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 这里面就形成了一个方法作用域</span></span><br><span class="line"><span class="comment">    * 能够保护其中的变量不能被外部访问</span></span><br><span class="line"><span class="comment">    * 但此方法作用域能够访问全局作用域</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">var</span> local1 = <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">console</span>.log(global);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 嵌套方法作用域</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">inner</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">        * 这里再度形成了一个方法作用域</span></span><br><span class="line"><span class="comment">        * 这个作用域内可以访问外部的作用域变量 local1</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">var</span> local2 = <span class="number">2</span>;</span><br><span class="line">        <span class="built_in">console</span>.log(local1);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 在 inner 方法的作用域外就不能访问其内属性了</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">console</span>.log(local2); <span class="comment">// 报错 ReferenceError: aa is not defined</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">foo();</span><br><span class="line"><span class="built_in">console</span>.log(local1); <span class="comment">// 报错 ReferenceError: a is not defined</span></span><br></pre></td></tr></table></figure><h3 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h3><p>我们以 for 循环开始</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(i);</span><br><span class="line">    &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>思考：这段代码会输出什么呢？<br>仔仔细细看了 30 秒，战战兢兢地说：每隔 1 秒后按顺序输出 0-9 吗？<br>0, 1, 2, 3, 4, 5, 6, 7, 8, 9</p></blockquote><p>&ensp;<br>我们将这段代码放到浏览器中执行一下得出结果：<br>在 1 秒后几乎同时输出了 10 个 10（注意不是每隔 1 秒输出一个 10）</p><h3 id="为什么？"><a href="#为什么？" class="headerlink" title="为什么？"></a>为什么？</h3><h4 id="原因"><a href="#原因" class="headerlink" title="原因"></a>原因</h4><p>因为 setTimeout 是异步执行！！</p><h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p>这是由于 javascript 的 “<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop" target="_blank" rel="noopener">消息队列</a>” 和 “异步函数” 机制导致的结果。</p><blockquote><p>知识普及：</p><p>栈：<br>JavaScript 是单线程语言，主线程执行同步代码。<br>函数调用时，便会在内存形成了一个“调用记录”，又称“调用帧”，保存调用位置和内部变量等信息。如果函数内部还调用了其他函数，那么在调用记录上方又会形成一个调用记录，所有的调用记录就形成一个“调用栈”。</p><p>堆：<br>对象被分配在一个堆中，一个用以表示一个内存中大的未被组织的区域。</p><p>消息队列与事件循环Event Loop：<br>一个 JavaScript 运行时包含了一个待处理的消息队列（异步任务），（内部是不进入主线程，而进入”任务队列”（task queue）的任务。比如UI事件、ajax网络请求、定时器setTimeout和setInterval等。<br>每一个消息都与一个函数（回调函数callback）相关联。当栈为空时，从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数（以及因而创建了一个初始堆栈帧）。当栈再次为空的时候，也就意味着消息处理结束。<br>这个过程是循环不断的，所以整个的这种运行机制又称为Event Loop（事件循环）。</p><p>javascript 是单线程，主线程执行同步代码，事件、 I/O 操作等异步任务，将会进入任务队列执行，异步执行有结果之后，就会变为等待状态，形成一个先进先出的执行栈，主线程的同步代码执行完之后，再从”任务队列”中读取事件，执行事件异步任务的回调。<br>这就是为什么执行顺序是， 同步 &gt; 异步 &gt; 回调<br>更简单的说：只要主线程空了（同步），就会去读取”任务队列”（异步），这就是 JavaScript 的运行机制。</p></blockquote><p>&ensp;<br>举个野栗子便于理解</p><p>因为 setTimeout 是异步执行，所以我们可以将这个 for 循环拆成 2 个部分：<br>    第一个部分专门处理 i 值的变化<br>    第二个部分专门执行 setTimeout</p><p>因此我们可以得到如下代码</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** </span></span><br><span class="line"><span class="comment">* 第一个部分</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">i++;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 省略 8 次   </span></span><br><span class="line"></span><br><span class="line">i++;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 直到这里，一直在做变量 i 的自增计算</span></span><br><span class="line"><span class="comment">* i 的值自增到10</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 第二个部分</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(i);</span><br><span class="line">&#125;, <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//省略 8 次</span></span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(i);</span><br><span class="line">&#125;, <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 到这里，执行了 10 次完全相同的代码</span></span><br><span class="line"><span class="comment">* 且同步设置了延迟为 1 秒的10个定时器</span></span><br><span class="line"><span class="comment">* 1秒后，10 个同时输出了 i 的值</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>当执行到第一个 setTimeout 时，javascript 内核检测到这是一个异步函数，所以将这个异步函数移入了消息队列，等待所有主线同步任务的完成后再继续执行。<br>接着第二个、第三个 setTimeout 异步函数依次执行相同操作。<br>直到将第 10 个 setTimeout 异步函数加入消息队列后，同步任务执行完成，然后才开始以先进先出的原则调用“消息队列”里面的异步函数。</p><h4 id="提升"><a href="#提升" class="headerlink" title="提升"></a>提升</h4><p>以上的疑惑解决了之后，我们再来看一个例子：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(i);</span><br><span class="line">    &#125;, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码与上面的不同之处是将 1000 的延迟改为了 0！</p><blockquote><p>思考：这段代码会输出什么呢？<br>随便想了想，上面的有 1000 毫秒的延迟，变量 i 才会瞬间执行到 10，而这里为 0，说明没有延迟，也就是说没有给变量 i 变化的时间，所以答案肯定是<br>0, 1, 2, 3, 4, 5, 6, 7, 8, 9</p></blockquote><p>&ensp;<br>我们将这段代码放到浏览器中执行一下得出结果：<br>几乎同时输出了 10 个 10<br>有的童鞋可能又要疑惑了，为什么？？？？？<br>有这样疑问的童鞋建议把原理认认真真再看一遍，还要多吃点上面的‘野栗子’，有助于理解。</p><h3 id="为什么-setTimeout-中匿名函数没有形成闭包呢？"><a href="#为什么-setTimeout-中匿名函数没有形成闭包呢？" class="headerlink" title="为什么 setTimeout 中匿名函数没有形成闭包呢？"></a>为什么 setTimeout 中匿名函数没有形成闭包呢？</h3><h4 id="原因-1"><a href="#原因-1" class="headerlink" title="原因"></a>原因</h4><p>因为 setTimeout 中的匿名函数没有将 i 作为参数传入来固定这个变量的值, 让其保留下来, 而是直接引用了外部作用域中的 i, 根据上面的 javascript 机制（先同步，再异步），因此 i 变化时, 也影响到了匿名函数。</p><p>因此如果我们定义一个外部函数, 让 i 作为参数传入，即可“闭包”我们要的变量了！！</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    <span class="comment">// 注意关键是我们把想要闭包的值当参数传入一个方法</span></span><br><span class="line">    <span class="comment">// 这个方法 return 一个新的方法 -- 闭包!!</span></span><br><span class="line">    setTimeout(fn(i), <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fn</span>(<span class="params"></span>) </span>&#123; <span class="comment">// 为了深刻理解闭包, 这个函数我没有用参数</span></span><br><span class="line">    <span class="comment">// 神奇的"闭包"发生在这一步, 其实就是作用域和值复制在起了关键作用,</span></span><br><span class="line">    <span class="comment">// 对于数字/字符等类型是复制值, 而不是引用</span></span><br><span class="line">    <span class="keyword">var</span> a = <span class="built_in">arguments</span>[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(a); <span class="comment">// 注意现在我操作的变量已经变成 a 了,</span></span><br><span class="line">        <span class="comment">// 已经和 i 没有半毛线关系了!</span></span><br><span class="line">        <span class="comment">// 而 a 的值就是当时执行时赋予的一个确定值,</span></span><br><span class="line">        <span class="comment">// 不会因 i 的变化而变化了!</span></span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="提升-1"><a href="#提升-1" class="headerlink" title="提升"></a>提升</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    (<span class="function"><span class="keyword">function</span> (<span class="params">a</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 变量 i 的值在传递到这个作用域时被复制给了 a,</span></span><br><span class="line">        <span class="comment">// 因此这个值就不会随外部变量而变化了</span></span><br><span class="line">        setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(a);</span><br><span class="line">        &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125;)(i); <span class="comment">// 我们在这里传入参数来"闭包"变量</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>写在最后：真正理解了作用域也就理解了闭包</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;在上一篇“javascript 难点剖析一：prototy（原型）”后, 我们紧接着来理解什么是闭包。闭包虽不是 javascript 的特色功能, 但要理解还真要费那么点工夫。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;
      
    
    </summary>
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/categories/javascript/"/>
    
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/tags/javascript/"/>
    
      <category term="closure（闭包）" scheme="http://blog.xieyangogo.cn/tags/closure%EF%BC%88%E9%97%AD%E5%8C%85%EF%BC%89/"/>
    
      <category term="javascript作用域" scheme="http://blog.xieyangogo.cn/tags/javascript%E4%BD%9C%E7%94%A8%E5%9F%9F/"/>
    
      <category term="javascript核心" scheme="http://blog.xieyangogo.cn/tags/javascript%E6%A0%B8%E5%BF%83/"/>
    
      <category term="javascript消息队列" scheme="http://blog.xieyangogo.cn/tags/javascript%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"/>
    
      <category term="javascript同步、异步" scheme="http://blog.xieyangogo.cn/tags/javascript%E5%90%8C%E6%AD%A5%E3%80%81%E5%BC%82%E6%AD%A5/"/>
    
  </entry>
  
  <entry>
    <title>javascript难点剖析一：prototype（原型）</title>
    <link href="http://blog.xieyangogo.cn/2014/12/23/javascript%E9%9A%BE%E7%82%B9%E5%89%96%E6%9E%90%E4%B8%80%EF%BC%9Aprototype%EF%BC%88%E5%8E%9F%E5%9E%8B%EF%BC%89/"/>
    <id>http://blog.xieyangogo.cn/2014/12/23/javascript难点剖析一：prototype（原型）/</id>
    <published>2014-12-23T07:50:00.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>要理解 javascript 中的 prototype，首先必须弄清楚以下几个概念</p><ol><li>javascript 中所有的东西都是对象</li><li>javascript 中所有的东西都由 Object 衍生而来, 即所有东西原型链的终点指向 Object.prototype<br>[“constructor”, “toString”, “toLocaleString”, “valueOf”, “hasOwnProperty”, “isPrototypeOf”, “propertyIsEnumerable”, “<strong>defineGetter</strong>“, “<strong>lookupGetter</strong>“, “<strong>defineSetter</strong>“, “<strong>lookupSetter</strong>“]<br>console.log(Object.getOwnPropertyNames(Object.prototype));</li><li>javascript 中构造函数和实例(对象)之间的微妙关系<br>构造函数通过定义 prototype 来约定其实例的规格, 再通过 new 来构造出实例, 他们的作用就是生产对象.<br>而构造函数本身又是 Function 的实例, 因此也可以查到它的 <strong>proto</strong> (原型链)</li></ol></blockquote><h3 id="构造函数与实例"><a href="#构造函数与实例" class="headerlink" title="构造函数与实例"></a>构造函数与实例</h3><h4 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h4><p>   Object              //javascript 原生API提供的构造函数<br>   function Foo() {}   //自定义的构造函数</p><h4 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h4><p>   new Object()<br>   new Foo()<br>   实例就“只能”查看 <strong>proto</strong> 来得知自己是基于什么 prototype 被制造出来的，而“不能”再重新定义实例的 prototype （即不能定义实例的实例）</p><h3 id="构造函数到底是什么"><a href="#构造函数到底是什么" class="headerlink" title="构造函数到底是什么"></a>构造函数到底是什么</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Empty</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Function</span>.prototype, <span class="built_in">Function</span>.__proto__)     <span class="comment">// Object &#123;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Empty</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.prototype, <span class="built_in">Object</span>.__proto__)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>&#123;&#125;       <span class="comment">// Foo &#123;&#125;                            </span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Empty</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line"><span class="built_in">console</span>.log(Foo.prototype, Foo.__proto__)</span><br></pre></td></tr></table></figure><p>prototype 输出的格式为: 构造函数名 原型</p><p>首先看下Object.prototype输出了什么?<br>Object {} -&gt; 前面的Object为构造函数的名称, 后面的那个表示原型, 这里是一个{}, 即一个Object对象的实例(空对象)<br>那么 Foo {} 我们就明白是什么意思了, Foo 就是构造函数的名称, 原型也是一个空对象</p><p>再来看看由构造函数构造出来的实例<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o = <span class="keyword">new</span> <span class="built_in">Object</span>();       <span class="comment">// var o = &#123;&#125;;      // undefined              </span></span><br><span class="line"><span class="built_in">Object</span> &#123;&#125;</span><br><span class="line"><span class="built_in">console</span>.log(o.prototype, o.__proto__);</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line"><span class="keyword">var</span> i = <span class="keyword">new</span> Foo();            <span class="comment">// undefined              </span></span><br><span class="line">Foo &#123;&#125;</span><br><span class="line"><span class="built_in">console</span>.log(i.prototype, i.__proto__);</span><br></pre></td></tr></table></figure></p><p>我们再深入一点, 定义下 F 的原型看看到底会发生些什么?<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">Foo.prototype.a = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line"><span class="keyword">var</span> i = <span class="keyword">new</span> Foo();       <span class="comment">// undefined              </span></span><br><span class="line">Foo &#123;<span class="attr">a</span>: <span class="function"><span class="keyword">function</span>&#125;</span></span><br><span class="line"><span class="function"><span class="title">console</span>.<span class="title">log</span>(<span class="params">i.prototype, i.__proto__</span>);</span></span><br></pre></td></tr></table></figure></p><p>这样我们就清楚的看到 i 是由 Foo 构造出来的, 原型是 {a: function}, 就是原本的空对象原型新增了一个 a 方法</p><p>我们再换一种情况, 完全覆盖 Foo 的原型会怎么样?<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">Foo.prototype = &#123;</span><br><span class="line">   a: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> i = <span class="keyword">new</span> Foo();      <span class="comment">// undefined             </span></span><br><span class="line"><span class="built_in">Object</span> &#123;<span class="attr">a</span>: <span class="function"><span class="keyword">function</span>&#125;</span></span><br><span class="line"><span class="function"><span class="title">console</span>.<span class="title">log</span>(<span class="params">i.prototype, i.__proto__</span>);</span></span><br></pre></td></tr></table></figure></p><p>咦~ 为什么这里表明 i 是由 Object 构造出来的? 不对吧!<br>因为我们完全将 Foo 的 prototype 覆盖, 其实也就是将原型指定为对象{a: function}, 但这会造成原本的 constructor 信息丢失, 变成了对象{a: function}指定的 constructor。<br>那么对象{a: function}的constructor是什么呢?<br>因为对象{a: function}其实就相对于<br>var o = {a: function() {}} // new了一个Object<br>那么 o 的 constructor 当然是 Object 啦</p><p>我们来纠正下这个错误<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">Foo.prototype = &#123;</span><br><span class="line">   a: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>重新指定正确的构造函数<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Foo.prototype.constructor = Foo;</span><br><span class="line"><span class="keyword">var</span> i = <span class="keyword">new</span> Foo();   <span class="comment">// undefined             </span></span><br><span class="line">Foo &#123;<span class="attr">a</span>: <span class="function"><span class="keyword">function</span>, <span class="title">constructor</span>: <span class="title">function</span>&#125;</span></span><br><span class="line"><span class="function"><span class="title">console</span>.<span class="title">log</span>(<span class="params">i.prototype, i.__proto__</span>);</span></span><br></pre></td></tr></table></figure></p><p>现在又能得到正确的原型信息了</p><h3 id="原型链"><a href="#原型链" class="headerlink" title="原型链"></a>原型链</h3><p>简单的来讲和OOP中的继承关系(链)是一样的, 一层一层往上找, 直至最终的 Object.prototype<br>prototype chain</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;要理解 javascript 中的 prototype，首先必须弄清楚以下几个概念&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;javascript 中所有的东西都是对象&lt;/li&gt;
&lt;li&gt;javascript 中所有的东西都由 Object 衍生而来, 即所有东西
      
    
    </summary>
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/categories/javascript/"/>
    
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/tags/javascript/"/>
    
      <category term="prototype（原型）" scheme="http://blog.xieyangogo.cn/tags/prototype%EF%BC%88%E5%8E%9F%E5%9E%8B%EF%BC%89/"/>
    
      <category term="javascript原型链" scheme="http://blog.xieyangogo.cn/tags/javascript%E5%8E%9F%E5%9E%8B%E9%93%BE/"/>
    
      <category term="面向对象编程" scheme="http://blog.xieyangogo.cn/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>CSS 960 grid 入门</title>
    <link href="http://blog.xieyangogo.cn/2014/11/21/CSS-960-grid-%E5%85%A5%E9%97%A8/"/>
    <id>http://blog.xieyangogo.cn/2014/11/21/CSS-960-grid-入门/</id>
    <published>2014-11-21T09:05:00.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>960 GRID SYSTEM 官网 <a href="http://960.gs/" target="_blank" rel="noopener">http://960.gs/</a></p></blockquote><h3 id="什么是CSS框架？"><a href="#什么是CSS框架？" class="headerlink" title="什么是CSS框架？"></a>什么是CSS框架？</h3><p>CSS框架是一种你能够使用在你的web项目中概念上的结构，是别人已经写完的，而且很完善的CSS定义集合。CSS框架一般是CSS文件的集合，包括基本风格的字体排版，表单样式，表格布局等等，比如：</p><ul><li>grid.css 表格布局</li><li>layout.css 布局</li><li>form.css 表单</li><li>general.css CSS常规设置</li></ul><h3 id="CSS框架的种类："><a href="#CSS框架的种类：" class="headerlink" title="CSS框架的种类："></a>CSS框架的种类：</h3><p>CSS框架很多，可以说太多了，最近几年像潮水般涌出。只是国内用CSS框架的不多，倒是PHP框架和Spring框架用的更多些。比较著名的CSS框架如下，不同的框架完成的任务不一定完全一样，每个框架都有自己的特色：</p><ul><li>YUI Grids CSS</li><li>Blueprint</li><li>YAML CSS Framework</li><li>CleverCSS</li><li>…</li></ul><h3 id="960Grid"><a href="#960Grid" class="headerlink" title="960Grid"></a>960Grid</h3><p>由于电脑显示器有方屏、宽屏，宽屏又分16:9和16:10。分辨率更是多种多样，这么多不同模式下得用户如何能看到外观整齐、一致的网站呢？人们发现一个奇妙的事情：960px的宽度是Very Good！无论什么屏，那种分辨率都通吃。<br>现在有人专门开发了一组网站架构CSS规则，只要遵守这些简单的规则（其实就是class类），你就能快速设计出960px宽度的网页结构来。<br>这，就是960Grid。<br>官网：<a href="http://960.gs/" target="_blank" rel="noopener">http://960.gs/</a></p><h3 id="法律问题"><a href="#法律问题" class="headerlink" title="法律问题"></a>法律问题</h3><p>960网格系统的文件都是免费的，MIT / GPL开源许可协议。大家可以放心大胆使用。</p><h3 id="如何使用960Grid框架？"><a href="#如何使用960Grid框架？" class="headerlink" title="如何使用960Grid框架？"></a>如何使用960Grid框架？</h3><ul><li><p>在html文件中引入相关的外部CSS文件：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"css/reset.css"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"css/text.css"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"css/960.css"</span> /&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p>在这必须解释一下，960.css为主要排版样式，缺它肯定不行。还有两个css文件：reset.css和text.css，它们的主要作用是为了消除浏览器间显示差异准备的，前者消除了html标签在各浏览器间的差异，而后者则主要针对的是字体。</p></blockquote></li></ul><p>&ensp;</p><ul><li><p>定义一个DIV大容器，放下整个页面：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>  这个DIV块一定要设定12列还是16列，其实不管12列还是16列宽度都是960px，只不过看你需不需要更细分的列了。</p></li><li><p>在这个DIV大容器里开始布局网页，首先添加一个LOGO栏，这个栏横向跨越整个960宽度：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>  此时，注意了，在12列的container中，使用了一个12列的grid，下划线后数字就是该DIV所占的列数。当然为了布局方便，我们一定会加入更多的class或id的，最后如下：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 logo"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>  设置好 .logo 的CSS的高和背景图片后，能看到如下图所示的界面：<br><img src="/blog/images/CSS-960-grid/css-960-1.jpg" alt=""></p></li></ul><h3 id="设置导航栏"><a href="#设置导航栏" class="headerlink" title="设置导航栏"></a>设置导航栏</h3><p>刚才那个logo块其实已经完工了，但为了清除它对身后的CSS设置影响，建议在每个横向DIV大块做完之后，都加上class=”clear”进行收尾。所以刚才的代码现在更改如下：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>然后再加入导航栏的DIV块，和LOGO一样，也是一个横向大块，设置后代码如下：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 logo"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 nav"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ul</span>&gt;</span>...<span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>添加样式后，网页变为这样：<br><img src="/blog/images/CSS-960-grid/css-960-2.jpg" alt=""></p><h3 id="添加PhotoSlide的位置"><a href="#添加PhotoSlide的位置" class="headerlink" title="添加PhotoSlide的位置"></a>添加PhotoSlide的位置</h3><p>一般网站为了漂亮和醒目，会在nav下加入photoslide，有的是js的，有的是flash的，但无论哪种都能把网站衬托起来。当然例子中就不真的放photoslide了。现在要把横行分为两个部分：left和right，left写文字，right放个图片提提神。<br>按原案例样子，left会占据7列的宽度，right将占据5列的宽度，7+5=12列！所以一个class用grid_7，另一个用gird_5。然后直接在块后加上clear类。由于这两个块高度一致，边框也一致，所以都加入一个topslider类设置它们的共同特征。<br>现在把代码写好：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 logo"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 nav"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ul</span>&gt;</span>...<span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_7 topslider"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_5 topslider"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>填入其它Html标记和各种素材，完成后，如下图所示：<br><img src="/blog/images/CSS-960-grid/css-960-3.jpg" alt=""></p><h3 id="完成接下来的代码"><a href="#完成接下来的代码" class="headerlink" title="完成接下来的代码"></a>完成接下来的代码</h3><p>用两段grid_12的名为class=”spacer”的div夹住四篇文章部分，四篇文章分别使用四个grid_3来完成。为什么是四个grid_3呢？横向12列，4篇等宽文章，每篇12/4=3！如果到现在你还有这个疑问，说明你其实没弄明白960grid的原理。建议回到页首再看下来。<br>加入很多代码后：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 logo"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 nav"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ul</span>&gt;</span>...<span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_7 topslider"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_5 topslider"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 spacer"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">  </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_3"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_3"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_3"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_3"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">  </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">  </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_12 spacer"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">  </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_4 footer"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_4 footer"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_4 footer"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>然后添加内容，设置CSS，就是这个页面了：<br><img src="/blog/images/CSS-960-grid/css-960-4.jpg" alt=""></p><p>到目前为止，能明白960Grid原理就达到目的。接下来的是关于这个框架的高级内容部分。</p><h3 id="高级部分："><a href="#高级部分：" class="headerlink" title="高级部分："></a>高级部分：</h3><h4 id="alpha和omega参数"><a href="#alpha和omega参数" class="headerlink" title="alpha和omega参数"></a>alpha和omega参数</h4><p>默认情况下，左右margin都是10px，则列间距为20px，但最左边的容器不需要左margin，最右边的容器不需要右margin，所以要改成这样，用alpha出去左margin，omega除去右margin：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container_12″&gt;</span></span></span><br><span class="line"><span class="tag"><span class="string">    &lt;div class="</span><span class="attr">grid_2</span> <span class="attr">alpha</span>"&gt;</span>左侧导航栏<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_8″&gt;主版块&lt;/div&gt;</span></span></span><br><span class="line"><span class="tag"><span class="string">    &lt;div class="</span><span class="attr">grid_2</span> <span class="attr">omega</span>"&gt;</span>右侧广告栏<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h4 id="prefix和suffix参数"><a href="#prefix和suffix参数" class="headerlink" title="prefix和suffix参数"></a>prefix和suffix参数</h4><p>如同上面那两个margin快捷参数一样，不必非得单独为某个div单独命名id或class，960Grid已经提供了prefix和suffix来处理padding。左侧补白（padding-left）是prefix_i，右侧的是suffix_i。i的值就是补白所占的列数，比如你想左侧加2列补白，则可以使用：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_5 prefix_2"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h4 id="push和pull参数"><a href="#push和pull参数" class="headerlink" title="push和pull参数"></a>push和pull参数</h4><p>最绝的还是这两个参数，一般人用的不多。因为大家都满足自己的网页宽度是960px了，如果现在真想把其中一个涉及到图片或广告的div做到1024px宽，怎么办？其实很好解决，一个div做reletive，它的子div的为absolue并且left为负值就可以，但这种CSS有点麻烦。<br>960Grid提供push和pull参数，push是推，pull是拉。关键是在什么位置推拉，就是说那里才是动作的起点？我们浏览网页时几乎都是自左向右看，所以该动作也以左侧为起点，现在想象你就站在最左边。push_i就是把一个块向右推移i个列宽，pull_i就是把这个块向左拉i个列宽。<br>push和pull动作对其他兄弟块没有影响。可以直接推出960px去它就在整个网页右边独单显示了，也可以pull出来，就在左边单独显示了。有点像sohu侧边广告的感觉。上个图，直观感受下吧，但sohu可不是用push和pull做出来的，我的意思是和sohu的效果一样。<br><img src="/blog/images/CSS-960-grid/css-960-5.jpg" alt=""></p><h4 id="clear参数"><a href="#clear参数" class="headerlink" title="clear参数"></a>clear参数</h4><p>class=”clear”，什么时候用呢。当你想换行的时候就使用它，即使一个横行只有grid_5和grid_4，就是说没有占满12列，也可以在其后增加一个让其后的任何内容都从下一行行首开始：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>如果遇到这种情况：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_4"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_5"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"clear"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"grid_3"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>原本横向占满12列的4+5+3，中间夹个clear。会发生什么事呢？grid_3这个块就会从下一个横行最左边出现。所以clear就是一个回车，相当于块的 &lt;br /&gt;。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ul><li>margin参数是alpha和omega；</li><li>padding参数是prefix和suffix；</li><li>脱离文档流移动参数是push和pull；</li><li>clear是块回车换行。</li></ul><blockquote><p>960 grid css web框架。或许不能称作框架。<br>官方网站：<a href="http://960.gs/" target="_blank" rel="noopener">http://960.gs/</a></p><p>因为：</p><p>960px = 12 <em> (60px + 20px) = 16 </em> (40px + 20px) = 24 * (30px + 10px)</p><p>所以：</p><p>960 个像素作 12 栏、16栏或 24 栏分割</p><p>12 栏和 16 栏分割的每个 grid 左右 margin 各 10px，24 栏分割每个 grid 左右 margin 各 5px。</p></blockquote><p>&ensp;</p><p>几个重要的关键字和概念:</p><ul><li>container容器：960px的宽度，左右margin auto 居中。</li><li>grid_x x方格：最小方格宽度*x px宽度+左margin 10px+右margin 10px（24栏布局分别为5px）</li><li>prefix_x前间距：使用padding属性。让grid和前面保持（最小方格宽度+间距）<em>x的距离.(例如 12栏的 prefix_3 的前间距为 （60px+20px）</em>3 即向前隔开三个方格的距离 )。</li><li>suffix_x 后间距：同理prefix_x,向后隔开x个方格。</li><li>pull_x 向左推：在正常的布局下向左推x个方格。</li><li>push_x 向右推：在正常的布局下向右推x个方格。</li><li>alpha 清除前间距：清除使用该class方格的前间距，方格会自动全部向前。</li><li>omega 清楚后间距：清除使用该class方格的后间距。</li><li><p>clear 换行（或者这样说有不妥）：重新换行继续方格布局。</p></li><li><p>心得：container 容器一定要外包在所有的grid布局的外面。它就像一页纸一样，我们可以在内部写方格。grid_x控制方格的宽度，prefix_x suffix_x控制方格间距，pull_x push_x控制方格左右浮动，alpha omega 清除左右的margin。clear 换行。</p></li><li>所以呢，可以理解为就是类似于在container内用方格写字就好啦！</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;960 GRID SYSTEM 官网 &lt;a href=&quot;http://960.gs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://960.gs/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;什么是C
      
    
    </summary>
    
      <category term="css" scheme="http://blog.xieyangogo.cn/categories/css/"/>
    
    
      <category term="css" scheme="http://blog.xieyangogo.cn/tags/css/"/>
    
      <category term="css960" scheme="http://blog.xieyangogo.cn/tags/css960/"/>
    
  </entry>
  
  <entry>
    <title>高效 jQuery 的奥秘</title>
    <link href="http://blog.xieyangogo.cn/2014/11/20/%E9%AB%98%E6%95%88-jQuery-%E7%9A%84%E5%A5%A5%E7%A7%98/"/>
    <id>http://blog.xieyangogo.cn/2014/11/20/高效-jQuery-的奥秘/</id>
    <published>2014-11-20T07:10:00.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>讨论 jQuery 和 JavaScript 性能的文章并不罕见。然而，本文我计划总结一些速度方面的技巧和我本人的一些建议，来提升你的 jQuery 和 JavaScript 代码。好的代码会带来速度的提升。快速渲染和响应意味着更好的用户体验。<br>首先，在脑子里牢牢记住 jQuery 就是 JavaScript 。这意味着我们应该采取相同的编码惯例、风格指南和最佳实践。</p><p>首先，如果你是一个 javascript 新手，我建议您阅读 《<a href="http://code.tutsplus.com/tutorials/24-javascript-best-practices-for-beginners--net-5399" target="_blank" rel="noopener">24 JavaScript Best Practices for Beginners</a>》（译文《<a href="http://www.xieyangogo.cn/blog/2017/09/30/%E5%88%9D%E5%AD%A6-javascript-%E7%9A%84-24-%E6%9D%A1%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/" target="_blank" rel="noopener">给JavaScript初学者的24条最佳实践</a>》） ,这是一篇高质量的 javascript 教程，接触 jQuery 之前最好先阅读。</p></blockquote><p>当你准备使用 jQuery ，我强烈建议你遵循下面这些指南：</p><h3 id="缓存变量"><a href="#缓存变量" class="headerlink" title="缓存变量"></a>缓存变量</h3><p>DOM遍历是昂贵的，所以尽量将会被重用的元素缓存。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line"> h = $(<span class="string">'#element'</span>).height();</span><br><span class="line"> $(<span class="string">'#element'</span>).css(<span class="string">'height'</span>, h - <span class="number">20</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 建议</span></span><br><span class="line"> $element = $(<span class="string">'#element'</span>);</span><br><span class="line"> h = $element.height();</span><br><span class="line"> $element.css(<span class="string">'height'</span>, h - <span class="number">20</span>);</span><br></pre></td></tr></table></figure><h3 id="避免全局变量"><a href="#避免全局变量" class="headerlink" title="避免全局变量"></a>避免全局变量</h3><p>jQuery与JavaScript一样，一般来说，最好确保你的变量在函数作用域内。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$element = $(<span class="string">'#element'</span>);</span><br><span class="line">h = $element.height();</span><br><span class="line">$element.css(<span class="string">'height'</span>, h - <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line"><span class="keyword">var</span> $element = $(<span class="string">'#element'</span>);</span><br><span class="line"><span class="keyword">var</span> h = $element.height();</span><br><span class="line">$element.css(<span class="string">'height'</span>, h - <span class="number">20</span>);</span><br></pre></td></tr></table></figure><h3 id="使用匈牙利命名法"><a href="#使用匈牙利命名法" class="headerlink" title="使用匈牙利命名法"></a>使用匈牙利命名法</h3><p>在变量前加$前缀，便于识别出jQuery对象。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line"><span class="keyword">var</span> first = $(<span class="string">'#first'</span>);</span><br><span class="line"><span class="keyword">var</span> second = $(<span class="string">'#second'</span>);</span><br><span class="line"><span class="keyword">var</span> value = $first.val();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议 - 在jQuery对象前加$前缀</span></span><br><span class="line"><span class="keyword">var</span> $first = $(<span class="string">'#first'</span>);</span><br><span class="line"><span class="keyword">var</span> $second = $(<span class="string">'#second'</span>);</span><br><span class="line"><span class="keyword">var</span> value = $first.val();</span><br></pre></td></tr></table></figure><h3 id="使用-Var-链（单-Var-模式）"><a href="#使用-Var-链（单-Var-模式）" class="headerlink" title="使用 Var 链（单 Var 模式）"></a>使用 Var 链（单 Var 模式）</h3><p>将多条var语句合并为一条语句，我建议将未赋值的变量放到后面。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span></span><br><span class="line">    $first = $(<span class="string">'#first'</span>),</span><br><span class="line">    $second = $(<span class="string">'#second'</span>),</span><br><span class="line">    value = $first.val(),</span><br><span class="line">    k = <span class="number">3</span>,</span><br><span class="line">    cookiestring = <span class="string">'SOMECOOKIESPLEASE'</span>,</span><br><span class="line">    i,</span><br><span class="line">    j,</span><br><span class="line">    myArray = &#123;&#125;;</span><br></pre></td></tr></table></figure><h3 id="请使用-‘On’"><a href="#请使用-‘On’" class="headerlink" title="请使用 ‘On’"></a>请使用 ‘On’</h3><p>在新版jQuery中，更短的 on(“click”) 用来取代类似 click() 这样的函数。在之前的版本中 on() 就是 bind()。自从jQuery 1.7版本后，on() 附加事件处理程序的首选方法。然而，出于一致性考虑，你可以简单的全部使用 on()方法。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$first.click(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    $first.css(<span class="string">'border'</span>, <span class="string">'1px solid red'</span>);</span><br><span class="line">    $first.css(<span class="string">'color'</span>, <span class="string">'blue'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">$first.hover(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    $first.css(<span class="string">'border'</span>, <span class="string">'1px solid red'</span>);</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$first.on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    $first.css(<span class="string">'border'</span>, <span class="string">'1px solid red'</span>);</span><br><span class="line">    $first.css(<span class="string">'color'</span>, <span class="string">'blue'</span>);</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">$first.on(<span class="string">'hover'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    $first.css(<span class="string">'border'</span>, <span class="string">'1px solid red'</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="精简javascript"><a href="#精简javascript" class="headerlink" title="精简javascript"></a>精简javascript</h3><p>一般来说,最好尽可能合并函数。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$first.click(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    $first.css(<span class="string">'border'</span>, <span class="string">'1px solid red'</span>);</span><br><span class="line">    $first.css(<span class="string">'color'</span>, <span class="string">'blue'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$first.on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    $first.css(&#123;</span><br><span class="line">        <span class="string">'border'</span>: <span class="string">'1px solid red'</span>,</span><br><span class="line">        <span class="string">'color'</span>: <span class="string">'blue'</span></span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h3 id="链式操作"><a href="#链式操作" class="headerlink" title="链式操作"></a>链式操作</h3><p>jQuery实现方法的链式操作是非常容易的。下面利用这一点。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$second.html(value);</span><br><span class="line">$second.on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    alert(<span class="string">'hello everybody'</span>);</span><br><span class="line">&#125;);</span><br><span class="line">$second.fadeIn(<span class="string">'slow'</span>);</span><br><span class="line">$second.animate(&#123;<span class="attr">height</span>: <span class="string">'120px'</span>&#125;, <span class="number">500</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$second.html(value);</span><br><span class="line">$second.on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    alert(<span class="string">'hello everybody'</span>);</span><br><span class="line">&#125;).fadeIn(<span class="string">'slow'</span>).animate(&#123;<span class="attr">height</span>: <span class="string">'120px'</span>&#125;, <span class="number">500</span>);</span><br></pre></td></tr></table></figure><h3 id="维持代码的可读性"><a href="#维持代码的可读性" class="headerlink" title="维持代码的可读性"></a>维持代码的可读性</h3><p>伴随着精简代码和使用链式的同时，可能带来代码的难以阅读。添加缩紧和换行能起到很好的效果。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$second.html(value);</span><br><span class="line">$second.on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    alert(<span class="string">'hello everybody'</span>);</span><br><span class="line">&#125;).fadeIn(<span class="string">'slow'</span>).animate(&#123;<span class="attr">height</span>: <span class="string">'120px'</span>&#125;, <span class="number">500</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$second.html(value);</span><br><span class="line">$second</span><br><span class="line">    .on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        alert(<span class="string">'hello everybody'</span>);</span><br><span class="line">    &#125;)</span><br><span class="line">    .fadeIn(<span class="string">'slow'</span>)</span><br><span class="line">    .animate(&#123;<span class="attr">height</span>: <span class="string">'120px'</span>&#125;, <span class="number">500</span>);</span><br></pre></td></tr></table></figure><h3 id="选择短路求值"><a href="#选择短路求值" class="headerlink" title="选择短路求值"></a>选择短路求值</h3><p>短路求值是一个从左到右求值的表达式，用 &amp;&amp;（逻辑与）或 || （逻辑或）操作符。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initVar</span>(<span class="params">$myVar</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!$myVar) &#123;</span><br><span class="line">        $myVar = $(<span class="string">'#selector'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initVar</span>(<span class="params">$myVar</span>) </span>&#123;</span><br><span class="line">    $myVar = $myVar || $(<span class="string">'#selector'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="选择捷径"><a href="#选择捷径" class="headerlink" title="选择捷径"></a>选择捷径</h3><p>精简代码的其中一种方式是利用编码捷径。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line"><span class="keyword">if</span> (collection.length &gt; <span class="number">0</span>) &#123;...&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line"><span class="keyword">if</span> (collection.length) &#123;...&#125;</span><br></pre></td></tr></table></figure><h3 id="繁重的操作中分离元素"><a href="#繁重的操作中分离元素" class="headerlink" title="繁重的操作中分离元素"></a>繁重的操作中分离元素</h3><p>如果你打算对DOM元素做大量操作（连续设置多个属性或css样式），建议首先分离元素然后在添加。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line"><span class="keyword">var</span></span><br><span class="line">    $container = $(<span class="string">"#container"</span>),</span><br><span class="line">    $containerLi = $(<span class="string">"#container li"</span>),</span><br><span class="line">    $element = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">$element = $containerLi.first();</span><br><span class="line"><span class="comment">//... 许多复杂的操作</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// better</span></span><br><span class="line"><span class="keyword">var</span></span><br><span class="line">    $container = $(<span class="string">"#container"</span>),</span><br><span class="line">    $containerLi = $container.find(<span class="string">"li"</span>),</span><br><span class="line">    $element = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">$element = $containerLi.first().detach();</span><br><span class="line"><span class="comment">//... 许多复杂的操作</span></span><br><span class="line"></span><br><span class="line">$container.append($element);</span><br></pre></td></tr></table></figure><h3 id="熟记技巧"><a href="#熟记技巧" class="headerlink" title="熟记技巧"></a>熟记技巧</h3><p>你可能对使用jQuery中的方法缺少经验,一定要查看的文档，可能会有一个更好或更快的方法来使用它。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$(<span class="string">'#id'</span>).data(key, value);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议 (高效)</span></span><br><span class="line">$.data(<span class="string">'#id'</span>, key, value);</span><br></pre></td></tr></table></figure><h3 id="使用子查询缓存的父元素"><a href="#使用子查询缓存的父元素" class="headerlink" title="使用子查询缓存的父元素"></a>使用子查询缓存的父元素</h3><p>正如前面所提到的，DOM遍历是一项昂贵的操作。典型做法是缓存父元素并在选择子元素时重用这些缓存元素。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line"><span class="keyword">var</span></span><br><span class="line">    $container = $(<span class="string">'#container'</span>),</span><br><span class="line">    $containerLi = $(<span class="string">'#container li'</span>),</span><br><span class="line">    $containerLiSpan = $(<span class="string">'#container li span'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议 (高效)</span></span><br><span class="line"><span class="keyword">var</span></span><br><span class="line">    $container = $(<span class="string">'#container '</span>),</span><br><span class="line">    $containerLi = $container.find(<span class="string">'li'</span>),</span><br><span class="line">    $containerLiSpan = $containerLi.find(<span class="string">'span'</span>);</span><br></pre></td></tr></table></figure><h3 id="避免通用选择符"><a href="#避免通用选择符" class="headerlink" title="避免通用选择符"></a>避免通用选择符</h3><p>将通用选择符放到后代选择符中，性能非常糟糕。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$(<span class="string">'.container &gt; *'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$(<span class="string">'.container'</span>).children();</span><br></pre></td></tr></table></figure><h3 id="避免隐式通用选择符"><a href="#避免隐式通用选择符" class="headerlink" title="避免隐式通用选择符"></a>避免隐式通用选择符</h3><p>通用选择符有时是隐式的，不容易发现。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$(<span class="string">'.someclass :radio'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$(<span class="string">'.someclass input:radio'</span>);</span><br></pre></td></tr></table></figure></p><h3 id="优化选择符"><a href="#优化选择符" class="headerlink" title="优化选择符"></a>优化选择符</h3><p>例如，Id选择符应该是唯一的，所以没有必要添加额外的选择符。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$(<span class="string">'div#myid'</span>);</span><br><span class="line">$(<span class="string">'div#footer a.myLink'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$(<span class="string">'#myid'</span>);</span><br><span class="line">$(<span class="string">'#footer .myLink'</span>);</span><br></pre></td></tr></table></figure><h3 id="避免多个ID选择符"><a href="#避免多个ID选择符" class="headerlink" title="避免多个ID选择符"></a>避免多个ID选择符</h3><p>在此强调，ID 选择符应该是唯一的，不需要添加额外的选择符，更不需要多个后代ID选择符。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕</span></span><br><span class="line">$(<span class="string">'#outer #inner'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$(<span class="string">'#inner'</span>);</span><br></pre></td></tr></table></figure><h3 id="坚持最新版本"><a href="#坚持最新版本" class="headerlink" title="坚持最新版本"></a>坚持最新版本</h3><p>新版本通常更好：更轻量级，更高效。显然，你需要考虑你要支持的代码的兼容性。例如，2.0 版本不支持 ie 6/7/8。</p><h3 id="摒弃弃用方法"><a href="#摒弃弃用方法" class="headerlink" title="摒弃弃用方法"></a>摒弃弃用方法</h3><p>关注每个新版本的废弃方法是非常重要的并尽量避免使用这些方法。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 糟糕 - live 已经废弃</span></span><br><span class="line">$(<span class="string">'#stuff'</span>).live(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'hooray'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建议</span></span><br><span class="line">$(<span class="string">'#stuff'</span>).on(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'hooray'</span>);</span><br><span class="line">&#125;);<span class="comment">// 注：此处可能不当，应为live能实现实时绑定，delegate或许更合适</span></span><br></pre></td></tr></table></figure><h3 id="利用CDN"><a href="#利用CDN" class="headerlink" title="利用CDN"></a>利用CDN</h3><p>谷歌的CND能保证选择离用户最近的缓存并迅速响应。（使用谷歌CND请自行搜索地址，此处地址以不能使用，推荐jquery官网提供的CDN）。</p><h3 id="必要时组合-jQuery-和-javascript-原生代码"><a href="#必要时组合-jQuery-和-javascript-原生代码" class="headerlink" title="必要时组合 jQuery 和 javascript 原生代码"></a>必要时组合 jQuery 和 javascript 原生代码</h3><p>如上所述，jQuery 就是 javascript ，这意味着用 jQuery 能做的事情，同样可以用原生代码来做。原生代码（或 <a href="http://google.urlshare.cn/" target="_blank" rel="noopener">vanilla</a>）的可读性和可维护性可能不如 jQuery，而且代码更长。但也意味着更高效（通常更接近底层代码可读性越差，性能越高，例如：汇编，当然需要更强大的人才可以）。牢记没有任何框架能比原生代码更小，更轻，更高效（注：测试链接已失效，可上网搜索测试代码）。</p><p>鉴于 vanilla 和 jQuery 之间的性能差异，我强烈建议吸收两人的精华，使用（可能的话）和 <a href="http://www.leebrimelow.com/native-methods-jQuery/" target="_blank" rel="noopener">jQuery 等价的原生代码</a>。</p><h3 id="最后忠告"><a href="#最后忠告" class="headerlink" title="最后忠告"></a>最后忠告</h3><p>最后，我记录这篇文章的目的是提高 jQuery 的性能和其他一些好的建议。如果你想深入的研究对这个话题你会发现很多乐趣。记住，jQuery 并非不可或缺，仅是一种选择。思考为什么要使用它。DOM 操作？ajax？模版？css 动画？还是选择符引擎？或许 javascript 微型框架或 jQuery 的定制版是更好的选择。</p><blockquote><p>注：本文为翻译文章，原文为 “<a href="http://flippinawesome.org/2013/11/25/writing-better-jquery-code/" target="_blank" rel="noopener">Writing Better jQuery Code</a>“。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;讨论 jQuery 和 JavaScript 性能的文章并不罕见。然而，本文我计划总结一些速度方面的技巧和我本人的一些建议，来提升你的 jQuery 和 JavaScript 代码。好的代码会带来速度的提升。快速渲染和响应意味着更好的用户体验。&lt;b
      
    
    </summary>
    
      <category term="jQuery" scheme="http://blog.xieyangogo.cn/categories/jQuery/"/>
    
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/tags/javascript/"/>
    
      <category term="jQuery" scheme="http://blog.xieyangogo.cn/tags/jQuery/"/>
    
      <category term="效率" scheme="http://blog.xieyangogo.cn/tags/%E6%95%88%E7%8E%87/"/>
    
  </entry>
  
  <entry>
    <title>初学 HTML 的 30 条最佳实践</title>
    <link href="http://blog.xieyangogo.cn/2014/11/20/%E5%88%9D%E5%AD%A6-HTML-%E7%9A%84-30-%E6%9D%A1%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/"/>
    <id>http://blog.xieyangogo.cn/2014/11/20/初学-HTML-的-30-条最佳实践/</id>
    <published>2014-11-20T06:41:00.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="保持标签闭合"><a href="#保持标签闭合" class="headerlink" title="保持标签闭合"></a>保持标签闭合</h3><p>以前，经常见到类似下面的代码（译注：这是多久以前啊……）：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">li</span>&gt;</span>Some text here. </span><br><span class="line"><span class="tag">&lt;<span class="name">li</span>&gt;</span>Some new text here. </span><br><span class="line"><span class="tag">&lt;<span class="name">li</span>&gt;</span>You get the idea.</span><br></pre></td></tr></table></figure><p>注意外面包裹的 UL / OL 标签被遗漏了（谁知是故意还是无意的），而且还忘记了关闭 LI 标签。按今天的标准来看，这是很明显的糟糕做法，应该 100% 避免。总之，保持闭合标签。否则，你验证 html 标签的时候可能遇到问题。</p><p>更好的方式</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ul</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">li</span>&gt;</span>Some text here. <span class="tag">&lt;/<span class="name">li</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">li</span>&gt;</span>Some new text here. <span class="tag">&lt;/<span class="name">li</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">li</span>&gt;</span>You get the idea. <span class="tag">&lt;/<span class="name">li</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="声明正确的文档类型"><a href="#声明正确的文档类型" class="headerlink" title="声明正确的文档类型"></a>声明正确的文档类型</h3><p><img src="/blog/images/html30/0.png" alt=""></p><p>声明正确的文档类型</p><p>笔者早先曾加入过许多 CSS 论坛，每当用户遇到问题，我们会建议他首先做两件事：</p><ul><li><p>验证 CSS 文件，保证没有错误。</p></li><li><p>确认添加了正确的 doctype</p></li></ul><p>DOCTYPE 出现在HTML标签之前，它告诉浏览器这个页面包含的是 HTML，XHTML，还是两者混合，这样浏览器才能正确解析。</p><p>通常有四种文档类型可供选择：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&lt;!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&lt;!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&lt;!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'&gt;</span></span><br></pre></td></tr></table></figure><p>关于该使用什么样的文档类型声明，一直有不同的说法。通常认为使用最严格的声明是最佳选择，但研究表明，大部分浏览器会使用普通的方式解析这种声明，所以很多人选择使用 HTML4.01 标准。选择声明的底线是，它是不是真的适合你，所以你要综合考虑来选择适合你得项目的声明。</p><h3 id="永远不要使用内联样式"><a href="#永远不要使用内联样式" class="headerlink" title="永远不要使用内联样式"></a>永远不要使用内联样式</h3><p>当你在埋头写代码时，可能会经常顺手或偷懒的加上一点行内 css 代码，就像这样：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">p</span> <span class="attr">style</span>=<span class="string">"color: red;"</span>&gt;</span></span><br><span class="line">    I'm going to make this text red so that it really stands out and makes people take notice! </span><br><span class="line"><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这样看起来即方便又没有问题。然而，这在你的编码实践中是个错误。</p><p>在你写代码时，在内容结构完成之前最好不要加入样式代码。</p><p>这样的编码方式就像打游击，是一种很山寨的做法。——Chris Coyier</p><p>更好的做法是，完成标签部分后，再把这个P的样式定义在外部样式表文件里。</p><p>建议</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#someElement</span> &gt; <span class="selector-tag">p</span> &#123; <span class="attribute">color</span>: red; &#125;</span><br></pre></td></tr></table></figure><h3 id="将所有外部css文件放入head标签内"><a href="#将所有外部css文件放入head标签内" class="headerlink" title="将所有外部css文件放入head标签内"></a>将所有外部css文件放入head标签内</h3><p>理论上讲，你可以在任何位置引入 CSS 样式表，但 HTML 规范建议在网页的 head 标记中引入，这样可以加快页面的渲染速度。</p><p>雅虎的开发过程中，我们发现，在 head 标签中引入样式表，会加快网页加载速度，因为这样可以使页面逐步渲染。 —— ySlow 团队</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>My Favorites Kinds of Corn<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">type</span>=<span class="string">"text/css"</span> <span class="attr">media</span>=<span class="string">"screen"</span> <span class="attr">href</span>=<span class="string">"path/to/file.css"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">type</span>=<span class="string">"text/css"</span> <span class="attr">media</span>=<span class="string">"screen"</span> <span class="attr">href</span>=<span class="string">"path/to/anotherFile.css"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="javascript文件放在底部"><a href="#javascript文件放在底部" class="headerlink" title="javascript文件放在底部"></a>javascript文件放在底部</h3><p><img src="/blog/images/html30/1.png" alt=""></p><p>要记住一个原则，就是让页面以最快的速度呈现在用户面前。当加载一个脚本时，页面会暂停加载，直到脚本完全载入并执行完成。因此会浪费用户更多的时间。</p><p>如果你的JS文件只是要实现某些功能，（比如点击按钮事件），那就放心的在body底部引入它，这绝对是最佳的方法。</p><p>建议</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span>And now you know my favorite kinds of corn. <span class="tag">&lt;/<span class="name">p</span>&gt;</span> </span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"path/to/file.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"path/to/anotherFile.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="永远不要使用内联-javascript-。现在不是-1996-年了！"><a href="#永远不要使用内联-javascript-。现在不是-1996-年了！" class="headerlink" title="永远不要使用内联 javascript 。现在不是 1996 年了！"></a>永远不要使用内联 javascript 。现在不是 1996 年了！</h3><p>许多年以前，还存在一种这样的方式，就是直接将 JS 代码加入到 HTML 标签中。尤其是在简单的图片相册中非常常见。本质上讲，一个 “onclick” 事件是附加在 标签上的，其效果等同于一些JS代码。不需要讨论太多，非常不应该使用这样的方式，应该把代码转移到一个外部 JS 文件中，然后使用 “addEventListener / attachEvent” 加入事件监听器。或者使用 <a href="http://www.kuqin.com/book/327879.html" target="_blank" rel="noopener">jquery</a> 等框架，只需要使用 “click” 方法。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'a#moreCornInfoLink'</span>).click(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    alert(<span class="string">'Want to learn more about corn?'</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="边开发，边验证"><a href="#边开发，边验证" class="headerlink" title="边开发，边验证"></a>边开发，边验证</h3><p><img src="/blog/images/html30/2.png" alt=""></p><p>很多人并不真正理解标准验证的意义和价值，笔者在一篇博客中详细分析了这个问题。一句话，验证是为你服务的，不是给你找麻烦的。如果你刚开始从事网页制作，那强烈建议你下载 <a href="https://addons.mozilla.org/en-US/firefox/addon/60" target="_blank" rel="noopener">Web Developer Toolbar</a>（chrome 用户请自行在应用商店搜索，ie 用户可能就杯具了） ，并在编码过程中随时使用“HTML 标准验证” 和 “CSS 标准验证”。如果你认为 CSS 是一种非常好学的语言，那么它会把你整的死去活来。你不严谨的代码会让你的页面漏洞百出，问题不断，一个好的方法就是—— 验证，验证，再验证。</p><h3 id="下载firebug"><a href="#下载firebug" class="headerlink" title="下载firebug"></a>下载firebug</h3><p><img src="/blog/images/html30/3.png" alt=""></p><p>Firebug 是当之无愧的网页开发最佳插件，它不但可以调试 JavaScript，还可以直观的让你了解页面标记的属性和位置。不用多说，<a href="https://addons.mozilla.org/en-US/firefox/addon/1843" target="_blank" rel="noopener">下载</a>！</p><h3 id="使用firebug"><a href="#使用firebug" class="headerlink" title="使用firebug"></a>使用firebug</h3><p><img src="/blog/images/html30/4.png" alt=""></p><p>据笔者观察，大部分的使用者仅仅使用了 Firebug 20% 的功能，那真是太浪费了，你不妨花几个小时的时间来系统学习这个工具，相信会让你事半功倍。</p><p>资源：</p><ul><li><a href="http://michaelsync.net/2007/09/08/firebug-tutorial-overview-of-firebug" target="_blank" rel="noopener">Overview of Firebug</a></li><li><a href="http://www.digitalmediaminute.com/screencast/firebug-js/" target="_blank" rel="noopener">Debug Javascript With Firebug – video tutorial</a></li></ul><h3 id="保持标签名小写"><a href="#保持标签名小写" class="headerlink" title="保持标签名小写"></a>保持标签名小写</h3><p>理论上讲，html不区分大小写，你可以随意书写，例如：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">DIV</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">P</span>&gt;</span>Here's an interesting fact about corn. <span class="tag">&lt;/<span class="name">P</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">DIV</span>&gt;</span></span><br></pre></td></tr></table></figure><p>但最好不要这样写，费力气输入大些字母没有任何用处，并且会让代码很难看.</p><p>建议</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>Here's an interesting fact about corn. <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="使用-H1-H6-标签"><a href="#使用-H1-H6-标签" class="headerlink" title="使用 H1-H6 标签"></a>使用 H1-H6 标签</h3><p>笔者建议你在网页中使用其中全部六种标记，虽然大部分人只会用到前四个，但使用最多的H会有很多好处，比如设备友好、搜索引擎友好等，不妨把你的P标签都替换成 H6。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>This is a really important corn fact! <span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h6</span>&gt;</span>Small, but still significant corn fact goes here. <span class="tag">&lt;/<span class="name">h6</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="写博客时，请将H1留给文章标题"><a href="#写博客时，请将H1留给文章标题" class="headerlink" title="写博客时，请将H1留给文章标题"></a>写博客时，请将H1留给文章标题</h3><p><img src="/blog/images/html30/5.jpg" alt=""></p><p>今天笔者在 <a href="http://www.twitter.com/nettuts" target="_blank" rel="noopener">Twitter</a> 上发起一次讨论：是该把 H1 定义到 LOGO 上还是定义到文章标题上，有 80% 的人选择了后者。</p><p>当然具体如何使用要看你的需求，但我建议你在建立博客的时候，将文章题目定为 H1，这对搜索引擎优化（seo）是非常有好处的。</p><h3 id="下载ySlow"><a href="#下载ySlow" class="headerlink" title="下载ySlow"></a>下载ySlow</h3><p><img src="/blog/images/html30/6.png" alt=""></p><p>在过去几年里，雅虎的团队在前端开发领域做了许多伟大的工作。前不久，它们发布了一个叫 ySlow 的 Firebug 扩展，它会分析你的网页，并返回 一个“成绩单”，上面细致分析了这个网页的方方面面，提出需要改进的地方，虽然它有点苛刻，但它绝对会对你有所帮助，强烈推荐 —— <a href="http://developer.yahoo.com/yslow/" target="_blank" rel="noopener">ySlow</a>！</p><h3 id="使用无序列表（UL）包裹导航菜单"><a href="#使用无序列表（UL）包裹导航菜单" class="headerlink" title="使用无序列表（UL）包裹导航菜单"></a>使用无序列表（UL）包裹导航菜单</h3><p><img src="/blog/images/html30/7.png" alt=""></p><p>通常网站都会有导航菜单，你可以用这样的方式定义：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"nav"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span>&gt;</span>Home <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span>&gt;</span>About <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span>&gt;</span>Contact <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>如果你想书写优美的代码，那最好不要用这种方式。</p><p>为什么要用 UL 布局导航菜单？ ——因为UL生来就是为定义列表准备的<br>最好这样定义：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ul</span> <span class="attr">id</span>=<span class="string">"nav"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">li</span>&gt;</span><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span>&gt;</span>Home<span class="tag">&lt;/<span class="name">a</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">li</span>&gt;</span><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span>&gt;</span>About<span class="tag">&lt;/<span class="name">a</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">li</span>&gt;</span><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span>&gt;</span>Contact<span class="tag">&lt;/<span class="name">a</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="学习如何应对-IE"><a href="#学习如何应对-IE" class="headerlink" title="学习如何应对 IE"></a>学习如何应对 IE</h3><p>IE一直以来都是前端开发人员的噩梦！</p><p>如果你的 CSS 样式表基本定型了，那么可以为 IE 单独建立一个样式表，然后这样仅对IE生效：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--[if lt IE 7]&gt;</span></span><br><span class="line"><span class="comment">    &lt;link rel="stylesheet" type="text/css" media="screen" href="path/to/ie.css" /&gt;</span></span><br><span class="line"><span class="comment">&lt;![endif]--&gt;</span></span><br></pre></td></tr></table></figure><p>这些代码的意思是：如果用户浏览器是 IE6 及以下，那这段代码才会生效。如果你想把 IE7 也包含进来，那么就把 “[if lt IE 7]” 改为 “[if lte IE 7]”。</p><h3 id="选择合适的IDE"><a href="#选择合适的IDE" class="headerlink" title="选择合适的IDE"></a>选择合适的IDE</h3><p>不论你是Windows还是Mac用户，这里都有很多优秀的编辑器供你选择：</p><ul><li><p>Mac 用户：<br>– <a href="http://www.panic.com/coda/" target="_blank" rel="noopener">Coda</a><br>– <a href="http://macrabbit.com/espresso/" target="_blank" rel="noopener">Espresso</a><br>– <a href="http://www.kuqin.com/book/326184.html" target="_blank" rel="noopener">TextMate</a><br>– <a href="http://www.aptana.com/" target="_blank" rel="noopener">Aptana</a><br>– <a href="http://www.adobe.com/products/dreamweaver.html" target="_blank" rel="noopener">DreamWeaver CS4</a></p></li><li><p>PC 用户：<br>– <a href="http://intype.info/stain.html" target="_blank" rel="noopener">InType</a><br>– <a href="http://www.e-texteditor.com/" target="_blank" rel="noopener">E-Text Editor</a><br>– <a href="http://notepad-plus-plus.org/" target="_blank" rel="noopener">Notepad++</a><br>– <a href="http://www.aptana.com/" target="_blank" rel="noopener">Aptana</a><br>– <a href="http://www.adobe.com/products/dreamweaver.html" target="_blank" rel="noopener">Dreamweaver CS4</a></p></li></ul><h3 id="上线前，压缩代码"><a href="#上线前，压缩代码" class="headerlink" title="上线前，压缩代码"></a>上线前，压缩代码</h3><p><img src="/blog/images/html30/8.png" alt=""></p><p>通过压缩您的 CSS 和 Javascript 文件，您可以减少总大小的 25% 左右，但在开发过程中不必压缩，然而，一旦网站完成后，利用一些网络压缩程序或多或少节省一些带宽。下面列出一些工具。</p><ul><li><p>Javascript 压缩服务：<br>– <a href="http://javascriptcompressor.com/" target="_blank" rel="noopener">Javascript Compressor</a><br>– <a href="http://www.xmlforasp.net/JSCompressor.aspx" target="_blank" rel="noopener">JS Compressor</a></p></li><li><p>CSS Compression Services：<br>– <a href="http://cssoptimiser.com/" target="_blank" rel="noopener">CSS Optimiser</a><br>– <a href="http://www.cssdrive.com/index.php/main/csscompressor/" target="_blank" rel="noopener">CSS Compressor</a><br>– <a href="http://www.cleancss.com/" target="_blank" rel="noopener">Clean CSS</a></p></li></ul><h3 id="精简，精简，再精简"><a href="#精简，精简，再精简" class="headerlink" title="精简，精简，再精简"></a>精简，精简，再精简</h3><p>回望我们大多数人写的第一个页面，一定会发现严重的 “DIV 癖”( divitis )，通常初学者的本能就是把一个段落用 DIV 包起来，然后为了控制定位而套上更多的 DIV。 —— 其实这是一种低效而有害的做法。</p><blockquote><p>网页写完后，一定要多次回头检查，尽量的减少元素的数量。能用 UL 布局的列表就不要用一个个的 DIV 去布局。</p></blockquote><p>正如写文章的关键是 “缩减，缩减，缩减” 一样，写页面也要遵循这个标准。</p><h3 id="给所有图片加上-“alt”-属性"><a href="#给所有图片加上-“alt”-属性" class="headerlink" title="给所有图片加上 “alt” 属性"></a>给所有图片加上 “alt” 属性</h3><p>为图片加上 alt 属性的好处是不言而喻的 —— 这样可以让禁用图片或者使用特殊设备的用户无障碍得了解你的网页信息，并且对图像搜索引擎友好。</p><p>糟糕的做法</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">IMG</span> <span class="attr">SRC</span>=<span class="string">"cornImage.jpg"</span> /&gt;</span></span><br></pre></td></tr></table></figure><p>更好的做法</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">"cornImage.jpg"</span> <span class="attr">alt</span>=<span class="string">"A corn field I visited."</span> /&gt;</span></span><br></pre></td></tr></table></figure><h3 id="通宵达旦"><a href="#通宵达旦" class="headerlink" title="通宵达旦"></a>通宵达旦</h3><p>我经常不知不觉的学习工作到凌晨，我认为这是个很好的状况。</p><p>我的 “啊～哈！” 时间（ “AH-HA” moments，指柳暗花明或豁然开朗的时刻）通常都发生在深夜，比如我彻底理解 JavaScript 的 “闭包” 概念，就是在这样一种情况下。如果你还没有感受过这种奇妙的时刻，那就马上试试吧！</p><h3 id="查看源代码"><a href="#查看源代码" class="headerlink" title="查看源代码"></a>查看源代码</h3><p><img src="/blog/images/html30/9.png" alt=""></p><p>没有什么比模仿你的偶像能让你更快的学习HTML。起初，我们都要甘做复印机，然后慢慢得发展自己的风格。研究你喜欢的网站页面代码，看看他们是怎么实现的。这是高手的必经之路，你一定要试一下。注意：只是学习和模仿他们的编码风格，而不是抄袭和照搬！</p><p>留意网络上各种炫酷的 JavaScript 效果，如果看上去是使用了插件，那根据它源码中head标签内的文件名，就可以找到这个插件名称，然后就可以学习它据为己用。</p><h3 id="为所有的元素定义样式"><a href="#为所有的元素定义样式" class="headerlink" title="为所有的元素定义样式"></a>为所有的元素定义样式</h3><p>这一条在你制作其他公司企业网站时尤为必要。你自己不使用 blockquote 标记？那用户可能会用，你自己不使用 OL？用户也可能会。花时间做一个页面，显示出 ul, ol, p, h1-h6, blockquotes, 等等元素的样式，检查一下是否有遗漏。</p><h3 id="使用第三方服务"><a href="#使用第三方服务" class="headerlink" title="使用第三方服务"></a>使用第三方服务</h3><p>现在互联网上流行着许多可以免费加在网页中的API，这些工具非常强大。它可以帮你实现许多巧妙的功能，更重要的是可以帮你宣传网站。</p><h3 id="掌握Photoshop"><a href="#掌握Photoshop" class="headerlink" title="掌握Photoshop"></a>掌握Photoshop</h3><p><img src="/blog/images/html30/10.png" alt=""></p><p>Photoshop 是前端工程师的一个重要工具，如果你已经熟练掌握 HTML 和 CSS ，那不妨多学习一下 Photshop。</p><ol><li>观看 Psdtuts+ 上的<a href="http://design.tutsplus.com/categories/videos" target="_blank" rel="noopener">视频课程</a>。</li><li>花费每月 25$ 注册成为 <a href="http://www.lynda.com/" target="_blank" rel="noopener">Lynda.com</a> 的会员，海量精品课程。</li><li>推荐 “<a href="http://www.mydamnchannel.com/You_Suck_at_Photoshop/Season_1/1DistortWarpandLayerEffects_1373" target="_blank" rel="noopener">You Suck at Photoshop</a>” 系列</li><li>花费几个小时记住尽可能多的 PS 快捷键。</li></ol><h3 id="学习每一个HTML标签"><a href="#学习每一个HTML标签" class="headerlink" title="学习每一个HTML标签"></a>学习每一个HTML标签</h3><p>虽然有些 HTML 标签很少用到，但你依然应该了解他们。比如 “abbr”、“cite” 等，你必须学习它们以备不时之需。</p><p>顺便说下，如果你不熟悉上面两个标签，可以看下下面的解释：</p><ul><li>abbr 和你估计的差不多，它是abbreviation的缩写，“Blvd” 能用 &lt;abbr&gt; 标签包裹，因为他是 “boulevard” 的缩写。</li><li>cite 被用来作为引用内容的标题（blockquote）。例如，如果你在你的博客中引用本篇文章，你可以将 “初学 HTML 的 30 条最佳实践” 用 &lt;cite&gt; 包裹，注意它不应该被用来包裹引用的作者，这是常见的误区。</li></ul><h3 id="参与社区讨论"><a href="#参与社区讨论" class="headerlink" title="参与社区讨论"></a>参与社区讨论</h3><p>网络上有许许多多优秀的资源，而社区中也隐藏着许多高手，这里你既可以自学，也能请教经验丰富的开发者。</p><h3 id="使用-reset-css"><a href="#使用-reset-css" class="headerlink" title="使用 reset.css"></a>使用 reset.css</h3><p>Css Reset 也就 Reset Css，就是重置一些HTML标签样式，或者说默认的样式。</p><p>关于是否应该使用 CSS Reset，网上也有激烈的争论，笔者是建议使用的。你可以先选用一些成熟的 CSS Reset，然后慢慢演变成适合自己的。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">html</span>, <span class="selector-tag">body</span>, <span class="selector-tag">div</span>, <span class="selector-tag">span</span>, <span class="selector-tag">h1</span>, <span class="selector-tag">h2</span>, <span class="selector-tag">h3</span>, <span class="selector-tag">h4</span>, <span class="selector-tag">h5</span>, <span class="selector-tag">h6</span>, <span class="selector-tag">p</span>, <span class="selector-tag">blockquote</span>, <span class="selector-tag">pre</span>, <span class="selector-tag">a</span>, <span class="selector-tag">abbr</span>, <span class="selector-tag">acronym</span>, <span class="selector-tag">address</span>, <span class="selector-tag">big</span>, <span class="selector-tag">cite</span>, <span class="selector-tag">code</span>, <span class="selector-tag">img</span>, <span class="selector-tag">ins</span>, <span class="selector-tag">kbd</span>, <span class="selector-tag">q</span>, <span class="selector-tag">s</span>, <span class="selector-tag">samp</span>, <span class="selector-tag">small</span>, <span class="selector-tag">strike</span>, <span class="selector-tag">strong</span>, <span class="selector-tag">dl</span>, <span class="selector-tag">dt</span>, <span class="selector-tag">dd</span>, <span class="selector-tag">ol</span>, <span class="selector-tag">ul</span>, <span class="selector-tag">li</span>, <span class="selector-tag">fieldset</span>, <span class="selector-tag">form</span>, <span class="selector-tag">label</span>, <span class="selector-tag">legend</span>, <span class="selector-tag">table</span>, <span class="selector-tag">caption</span>, <span class="selector-tag">tbody</span>, <span class="selector-tag">tfoot</span>, <span class="selector-tag">thead</span>, <span class="selector-tag">tr</span>, <span class="selector-tag">th</span>, <span class="selector-tag">td</span> &#123; </span><br><span class="line">    <span class="attribute">margin</span>: <span class="number">0</span>; </span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0</span>; </span><br><span class="line">    <span class="attribute">border</span>: <span class="number">0</span>; </span><br><span class="line">    <span class="attribute">outline</span>: <span class="number">0</span>; </span><br><span class="line">    <span class="attribute">font-size</span>: <span class="number">100%</span>; </span><br><span class="line">    <span class="attribute">vertical-align</span>: baseline; </span><br><span class="line">    <span class="attribute">background</span>: transparent; </span><br><span class="line">&#125; </span><br><span class="line"><span class="selector-tag">body</span> &#123; </span><br><span class="line">    <span class="attribute">line-height</span>: <span class="number">1</span>; </span><br><span class="line">&#125; </span><br><span class="line"><span class="selector-tag">ol</span>, <span class="selector-tag">ul</span> &#123; </span><br><span class="line">    <span class="attribute">list-style</span>: none; </span><br><span class="line">&#125; </span><br><span class="line"><span class="selector-tag">blockquote</span>, <span class="selector-tag">q</span> &#123; </span><br><span class="line">    <span class="attribute">quotes</span>: none; </span><br><span class="line">&#125; </span><br><span class="line"><span class="selector-tag">blockquote</span><span class="selector-pseudo">:before</span>, </span><br><span class="line"><span class="selector-tag">blockquote</span><span class="selector-pseudo">:after</span>, </span><br><span class="line"><span class="selector-tag">q</span><span class="selector-pseudo">:before</span>, </span><br><span class="line"><span class="selector-tag">q</span><span class="selector-pseudo">:after</span> &#123; </span><br><span class="line">    <span class="attribute">content</span>: <span class="string">''</span>; </span><br><span class="line">    <span class="attribute">content</span>: none; </span><br><span class="line">&#125; </span><br><span class="line"><span class="selector-tag">table</span> &#123; </span><br><span class="line">    <span class="attribute">border-collapse</span>: collapse; </span><br><span class="line">    <span class="attribute">border-spacing</span>: <span class="number">0</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="对齐元素"><a href="#对齐元素" class="headerlink" title="对齐元素"></a>对齐元素</h3><p><img src="/blog/images/html30/11.png" alt=""></p><p>简单来说，你应该尽可能的对齐你的网页元素。可以观察一下你喜欢的网站，它们的LOGO、标题、图表、段落肯定是对得非常整齐的。否则就会显得混乱和不专业。</p><h3 id="关于PSD切片"><a href="#关于PSD切片" class="headerlink" title="关于PSD切片"></a>关于PSD切片</h3><p><img src="/blog/images/html30/12.jpg" alt=""></p><p>现在你已经掌握了HTML、CSS、Photoshop知识，那么你还需要学习如何把PSD转换为网页上的图片和背景，下面有两个不错的教程：</p><ul><li><a href="http://code.tutsplus.com/" target="_blank" rel="noopener">Slice and Dice that PSD</a></li><li><a href="http://code.tutsplus.com/" target="_blank" rel="noopener">From PSD to HTML/CSS</a></li></ul><h3 id="不要随意使用框架"><a href="#不要随意使用框架" class="headerlink" title="不要随意使用框架"></a>不要随意使用框架</h3><p>Javascript 和 CSS 都有许多优秀的框架，但如果你是初学者，不要急于使用它们。如果你还没能熟练的驾驭 CSS，使用框架会混淆你的知识体系。尽管你可能能会说 javascript 和 jQuery 是可以同时学习的，但这对 css 并不适合。我个人提倡 960 CSS 网格框架（英文网站），并且我经常使用它。还是那句话，如果你是 css 的初学者，学习框架只会让你更加困惑。</p><p>CSS 框架是为熟练开发者设计的，这样会节省它们大量的时间。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;保持标签闭合&quot;&gt;&lt;a href=&quot;#保持标签闭合&quot; class=&quot;headerlink&quot; title=&quot;保持标签闭合&quot;&gt;&lt;/a&gt;保持标签闭合&lt;/h3&gt;&lt;p&gt;以前，经常见到类似下面的代码（译注：这是多久以前啊……）：&lt;/p&gt;
&lt;figure class=&quot;highl
      
    
    </summary>
    
      <category term="HTML" scheme="http://blog.xieyangogo.cn/categories/HTML/"/>
    
    
      <category term="HTML" scheme="http://blog.xieyangogo.cn/tags/HTML/"/>
    
      <category term="效率" scheme="http://blog.xieyangogo.cn/tags/%E6%95%88%E7%8E%87/"/>
    
      <category term="入门" scheme="http://blog.xieyangogo.cn/tags/%E5%85%A5%E9%97%A8/"/>
    
  </entry>
  
  <entry>
    <title>初学 javascript 的 24 条最佳实践</title>
    <link href="http://blog.xieyangogo.cn/2014/11/20/%E5%88%9D%E5%AD%A6-javascript-%E7%9A%84-24-%E6%9D%A1%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/"/>
    <id>http://blog.xieyangogo.cn/2014/11/20/初学-javascript-的-24-条最佳实践/</id>
    <published>2014-11-20T03:50:00.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>注：本文多次用到 Firebug 的 console 对象，请参考 <a href="http://getfirebug.com/console.html" target="_blank" rel="noopener">Firebug Console API</a>。关于 Firebug 的更详细介绍，请猛击这里。</p></blockquote><h3 id="使用-“-”-代替-“-”"><a href="#使用-“-”-代替-“-”" class="headerlink" title="使用 “===” 代替 “==”"></a>使用 “===” 代替 “==”</h3><p>JavaScript 里有两种不同的相等运算符：=== | !== 和 == | != 。相比之下，前者更值得推荐。请尽量使用前者。</p><blockquote><p>“如果两边的操作数具有相同的类型和值，=== 返回true，!== 返回 false。” —— JavaScript语言精粹</p></blockquote><p>&ensp;<br>不过，如果使用 == 和 != ，在操作不同数据类型时, 你可能会遇到一些意想不到的问题。在进行相等判断前，JavaScript 会试图将它们转换为字符串、数字或 Boolean 量。</p><h3 id="Eval-Bad（避免使用eval函数，参考）"><a href="#Eval-Bad（避免使用eval函数，参考）" class="headerlink" title="Eval = Bad（避免使用eval函数，参考）"></a>Eval = Bad（避免使用eval函数，<a href="http://www.w3school.com.cn/js/jsref_eval.asp" target="_blank" rel="noopener">参考</a>）</h3><p>起初不太熟悉时，“eval” 让我们能够访问 JavaScript 的编译器（译注：这看起来很强大）。从本质上讲,我们可以将字符串传递给 eval 作为参数，把字符串作为 javascript 代码执行，返回结果。</p><p>这不仅大幅降低脚本的性能（译注：JIT 编译器无法预知字符串内容，而无法预编译和优化），而且这也会带来巨大的安全风险，因为这样付给要执行的文本太高的权限，千万别用！</p><h3 id="省略未必省事"><a href="#省略未必省事" class="headerlink" title="省略未必省事"></a>省略未必省事</h3><p>从技术上讲，你可以省略大多数花括号和分号。大多数浏览器都能正确理解下面的代码:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (someVariableExists)</span><br><span class="line">    x = <span class="literal">false</span></span><br></pre></td></tr></table></figure><p>然后，如果像下面这样：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (someVariableExists)</span><br><span class="line">    x = <span class="literal">false</span> anotherFunctionCall();</span><br></pre></td></tr></table></figure><p>有人可能会认为上面的代码等价于下面这样:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (someVariableExists) &#123;</span><br><span class="line">    x = <span class="literal">false</span>;</span><br><span class="line">    anotherFunctionCall();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不幸的是，这种理解是错误的。实际上的意思如下:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (someVariableExists) &#123;</span><br><span class="line">    x = <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line">anotherFunctionCall();</span><br></pre></td></tr></table></figure><p>你可能注意到了，上面的缩进容易给人花括号的假象。无可非议，这是一种可怕的实践，应不惜一切代价避免。仅有一种情况下，即只有一行的时候，花括号是可以省略的，但这点也是饱受争议的。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="number">2</span> + <span class="number">2</span> === <span class="number">4</span>) <span class="keyword">return</span> <span class="string">'nicely done'</span>;</span><br></pre></td></tr></table></figure><blockquote><p>建议：</p><p>很可能，有一天你需要在 if 语句块中添加更多的语句。这样的话，你必须重写这段代码。底线——省略是雷区。到底还得加上。</p></blockquote><p>&ensp;</p><h3 id="使用JSLint"><a href="#使用JSLint" class="headerlink" title="使用JSLint"></a>使用JSLint</h3><p><a href="http://www.jslint.com/" target="_blank" rel="noopener">JSLint</a> 是由大名鼎鼎的<a href="http://www.crockford.com/" target="_blank" rel="noopener">道格拉斯</a>（Douglas Crockford）编写的调试器。简单的将你的代码粘贴进JSLint中，它会迅速找出代码中明显的问题和错误。</p><blockquote><p>“JSLint扫面输入的源代码。如果发现一个问题，它返回一条描述问题和一个代码中的所在位置的消息。问题并不一定是语法错误，尽管通常是这样。JSLint还会查看一些编码风格和程序结构问题。这并不能保证你的程序是正确的。它只是提供了另一双帮助发现问题的眼睛。” ——JSLing 文档</p></blockquote><p>&ensp;<br>部署脚本之前，运行JSLint，只是为了确保你没有做出任何愚蠢的错误。</p><h3 id="将脚本放在页面的底部"><a href="#将脚本放在页面的底部" class="headerlink" title="将脚本放在页面的底部"></a>将脚本放在页面的底部</h3><p>在本系列前面的文章里已经提到过这个技巧，我粘贴信息在这里。</p><p><img src="/blog/images/html30/1.png" alt=""></p><p>请记住 —— 我们要千方百计保证客户端的页面载入速度尽可能的快。而脚本没载入完成，浏览器就没法加载页面的剩余部分。</p><p>如果你的JS文件只是添加一些额外功能 —— 例如，为点击某链接绑定事件——那大可以等页面加载基本完成后再做。把JS文件放到页面最后，body的结束标签之前，这样做最好了。</p><p>建议</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span>And now you know my favorite kinds of corn. <span class="tag">&lt;/<span class="name">p</span>&gt;</span> </span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"path/to/file.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"path/to/anotherFile.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="避免在For语句内声明变量"><a href="#避免在For语句内声明变量" class="headerlink" title="避免在For语句内声明变量"></a>避免在For语句内声明变量</h3><p>当需要执行冗长的for语句时，不要让JavaScript引擎每次都重复那些没有必要的操作。例如：</p><p>糟糕</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; someArray.length; i++) &#123;</span><br><span class="line">    <span class="keyword">var</span> container = <span class="built_in">document</span>.getElementById(<span class="string">'container'</span>);</span><br><span class="line">    container.innerHtml += <span class="string">'my number: '</span> + i;</span><br><span class="line">    <span class="built_in">console</span>.log(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码每次都重新定义数组长度，每次都在遍历DOM寻找container元素 —— 太傻了！</p><p>建议</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> container = <span class="built_in">document</span>.getElementById(<span class="string">'container'</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, len = someArray.length; i &lt; len; i++) &#123;</span><br><span class="line">    container.innerHtml += <span class="string">'my number: '</span> + i;</span><br><span class="line">    <span class="built_in">console</span>.log(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="构建字符串的最优方法"><a href="#构建字符串的最优方法" class="headerlink" title="构建字符串的最优方法"></a>构建字符串的最优方法</h3><p>当你需要遍历数组或对象的时候，不要总想着 “for” 语句，要有创造性，总能找到更好的办法，例如，像下面这样。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="string">'item 1'</span>, <span class="string">'item 2'</span>, <span class="string">'item 3'</span>, ...];</span><br><span class="line"><span class="keyword">var</span> list = <span class="string">'&lt;ul&gt;&lt;li&gt;'</span> + arr.join(<span class="string">'&lt;/li&gt;&lt;li&gt;'</span>) + <span class="string">'&lt;/li&gt;&lt;/ul&gt;'</span>;</span><br></pre></td></tr></table></figure><p><strong>我不是你心中神，但请你相信我（不信你自己测试） —— 这是迄今为止最快的方法！</strong></p><p>使用原生代码（如 join()），不管系统内部做了什么，通常比非原生快很多。 —— James Padolsey, james.padolsey.com</p><h3 id="减少全局变量"><a href="#减少全局变量" class="headerlink" title="减少全局变量"></a>减少全局变量</h3><p>只要把多个全局变量都整理在一个名称空间下，将显著降低与其他应用程序、组件或类库之间产生糟糕的相互影响的可能性。 —— Douglas Crockford</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">'Jeffrey'</span>;</span><br><span class="line"><span class="keyword">var</span> lastName = <span class="string">'Way'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params"></span>) </span>&#123;...&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(name); <span class="comment">// Jeffrey -- 或 window.name</span></span><br></pre></td></tr></table></figure><p>更好的做法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> DudeNameSpace = &#123;</span><br><span class="line">    name: <span class="string">'Jeffrey'</span>,</span><br><span class="line">    lastName: <span class="string">'Way'</span>,</span><br><span class="line">    doSomething: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;...&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(DudeNameSpace.name); <span class="comment">// Jeffrey</span></span><br></pre></td></tr></table></figure><blockquote><p>注：这里只是简单命名为 “DudeNameSpace”，实际当中要取更合理的名字。</p></blockquote><p>&ensp;</p><h3 id="给代码添加注释"><a href="#给代码添加注释" class="headerlink" title="给代码添加注释"></a>给代码添加注释</h3><p>可能一开始你会觉得并无必要，但相信我，你将来会主动想要尽可能写好代码的注释的。当你几个月后再回看某项目时，结果却发现很难想起当时写某句东西时脑子在想的什么了，是不是很让人沮丧呢？或者，如果有同事要修订你的代码呢？一定，一定要为你代码里的重要部分加上注释。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 循环数组，输出每项名字（译者注：这样的注释似乎有点多余吧）</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, len = array.length; i &lt; len; i++) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(array[i]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="试试渐进增强"><a href="#试试渐进增强" class="headerlink" title="试试渐进增强"></a>试试渐进增强</h3><p>一定要记得为未启用 JavaScript 的情况提供替代方案。大家可能会认为，“大部分我的访客都启用了JavaScript的，我才不用担心”。这样的话，你可就大错特错了！</p><p>你有没有试过看看禁用JavaScript后你那漂亮的滑动器都成啥样了？（你可以下载 <a href="https://addons.mozilla.org/en-US/firefox/addon/60" target="_blank" rel="noopener">Web Developer ToolBar</a> 轻松完成这项任务。）禁用之后你的网站可能就彻底失去了可用性！经验之谈：开发初期总是按照没有JavaScript来设计你的网站，之后再进行渐进地功能增强，小心翼翼地改变你地布局。</p><h3 id="不要给-“setInterval”-或-“setTimeout”-传递字符串参数"><a href="#不要给-“setInterval”-或-“setTimeout”-传递字符串参数" class="headerlink" title="不要给 “setInterval” 或 “setTimeout” 传递字符串参数"></a>不要给 “setInterval” 或 “setTimeout” 传递字符串参数</h3><p>考虑下面的代码:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setInterval(<span class="string">"document.getElementById('container').innerHTML += 'My new number: ' + i"</span>, <span class="number">3000</span>);</span><br></pre></td></tr></table></figure><p>不仅执行不高效，而且和 eval 函数有着同样的高风险。千万不要把字串传递给 setInterval 和 setTimeout。恰当的做法是，传递一个函数名：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setInterval(someFunction, <span class="number">3000</span>);</span><br></pre></td></tr></table></figure><h3 id="不要使用-“with”-语句"><a href="#不要使用-“with”-语句" class="headerlink" title="不要使用 “with” 语句"></a>不要使用 “with” 语句</h3><p>初识之下，“with” 语句似乎还挺好用的，它用于设置代码在特定对象中的作用域。其基本用法是提供深入到对象中处理元素的快速写法。例如：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">with</span> (being.person.man.bodyparts) &#123;</span><br><span class="line">    arms = <span class="literal">true</span>;</span><br><span class="line">    legs = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>而不是像下面这样：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">being.person.man.bodyparts.arms = <span class="literal">true</span>;</span><br><span class="line">being.person.man.bodyparts.legs = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><p>不幸的是，测试表明，若你要为对象插入新成员，with的表现非常糟糕，它的执行速度非常缓慢。替代方案是声明一个变量：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o = being.person.man.bodyparts;</span><br><span class="line">o.arms = <span class="literal">true</span>;</span><br><span class="line">o.legs = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><h3 id="使用-代替-new-Ojbect"><a href="#使用-代替-new-Ojbect" class="headerlink" title="使用 {} 代替 new Ojbect()"></a>使用 {} 代替 new Ojbect()</h3><p>在JavaScript有多种方式能新建对象，最传统的方法是 new 语句，如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line">o.name = <span class="string">'Jeffrey'</span>;</span><br><span class="line">o.lastName = <span class="string">'Way'</span>;</span><br><span class="line">o.someFunction = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不过，这一方法读起来却比较糟糕。我强烈建议你采用下面这种在文字样式上更为强健的写法：</p><p>更好的做法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o = &#123;</span><br><span class="line">    name: <span class="string">'Jeffrey'</span>,</span><br><span class="line">    lastName: <span class="string">'Way'</span>,</span><br><span class="line">    someFunction: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>注意，如果你只是想创建一个空对象，{} 更好。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o = &#123;&#125;;</span><br></pre></td></tr></table></figure><p>“对象字面量使我们能够编写更具特色的代码，而且相对简单的多。不需要直接调用构造函数或维持传递给函数的参数的正确顺序，等” —— dyn-web.com</p><h3 id="使用-代替-new-Array"><a href="#使用-代替-new-Array" class="headerlink" title="使用 [] 代替 new Array()"></a>使用 [] 代替 new Array()</h3><p>这同样适用于创建一个新的数组。</p><p>例如：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">a[<span class="number">0</span>] = <span class="string">"Joe"</span>;</span><br><span class="line">a[<span class="number">1</span>] = <span class="string">'Plumber'</span>;</span><br></pre></td></tr></table></figure><p>更好的做法：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = [<span class="string">'Joe'</span>, <span class="string">'Plumber'</span>];</span><br></pre></td></tr></table></figure><p>“javascript 程序中常见的错误是在需要对象的时候使用数组，而需要数组的时候却使用对象。规则很简单：当属性名是连续的整数时，你应该使用数组。否则，请使用对象” —— Douglas Crockford</p><h3 id="定义多个变量时，省略-var-关键字，用逗号代替"><a href="#定义多个变量时，省略-var-关键字，用逗号代替" class="headerlink" title="定义多个变量时，省略 var 关键字，用逗号代替"></a>定义多个变量时，省略 var 关键字，用逗号代替</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> someItem = <span class="string">'some string'</span>; </span><br><span class="line"><span class="keyword">var</span> anotherItem = <span class="string">'another string'</span>; </span><br><span class="line"><span class="keyword">var</span> oneMoreItem = <span class="string">'one more string'</span>;</span><br></pre></td></tr></table></figure><p>更好的做法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> someItem = <span class="string">'some string'</span>, </span><br><span class="line">    anotherItem = <span class="string">'another string'</span>, </span><br><span class="line">    oneMoreItem = <span class="string">'one more string'</span>;</span><br></pre></td></tr></table></figure><p>…不言自明。我不知道这样做能否提升代码执行速度，但是确实让你的代码干净许多。</p><h3 id="谨记，不要省略分号"><a href="#谨记，不要省略分号" class="headerlink" title="谨记，不要省略分号"></a>谨记，不要省略分号</h3><p>从技术上讲，大多数浏览器允许你省略分号。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> someItem = <span class="string">'some string'</span> <span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params"></span>) </span>&#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="string">'something'</span> </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>之前已经说过，这样做会造成潜在的更大、更难以发现的问题：</p><p>更好的做法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> someItem = <span class="string">'some string'</span>; <span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params"></span>) </span>&#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="string">'something'</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="“For-in”-语句"><a href="#“For-in”-语句" class="headerlink" title="“For in” 语句"></a>“For in” 语句</h3><p>遍历对象时，你可能会发现你还需要获取方法函数。所以遇到这种情况时，请一定记得给你的代码包一层 if 语句，用以过滤信息。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (key <span class="keyword">in</span> object) &#123;</span><br><span class="line">    <span class="keyword">if</span> (object.hasOwnProperty(key)) &#123;</span><br><span class="line">        ...</span><br><span class="line">        then</span><br><span class="line">        <span class="keyword">do</span> something...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>参考 JavaScript：语言精粹，道格拉斯（Douglas Crockford）。</p></blockquote><p>&ensp;</p><h3 id="使用-Firebug-的-“timer”-功能优化你的代码"><a href="#使用-Firebug-的-“timer”-功能优化你的代码" class="headerlink" title="使用 Firebug 的 “timer” 功能优化你的代码"></a>使用 Firebug 的 “timer” 功能优化你的代码</h3><p>想要轻松地快速了解某项操作的用时吗？使用Firebug的timer功能来记录结果好了。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">TimeTracker</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.time(<span class="string">"MyTimer"</span>);</span><br><span class="line">    <span class="keyword">for</span> (x = <span class="number">5000</span>; x &gt; <span class="number">0</span>; x--) &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">console</span>.timeEnd(<span class="string">"MyTimer"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="阅读，阅读，反复阅读"><a href="#阅读，阅读，反复阅读" class="headerlink" title="阅读，阅读，反复阅读"></a>阅读，阅读，反复阅读</h3><p>虽然我是 Web 开发博客（就像这个！）的超级粉丝，但吃饭和睡觉前除了看书好像也别无选择~ 在你的床头柜上摆一本 Web 开发的好书吧！下列书单都是我的最爱：</p><p><a href="http://www.packtpub.com/object-oriented-javascript-applications-libraries/book" target="_blank" rel="noopener">Object-Oriented JavaScript</a>（<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3DRTedFwhY5ykcQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMeupzlnrjcvD1aH1Hk3GeOiLbAnOMKhNmUKQIyb6a3ytbepvpTpVkkGwOnkIiEYnhxRRelJrXu3g" target="_blank" rel="noopener">JavaScript面向对象编程指南</a> <a href="http://ishare.iask.sina.com.cn/f/18963277.html" target="_blank" rel="noopener">pdf</a>）<br><a href="http://oreilly.com/catalog/9780596517748/" target="_blank" rel="noopener">JavaScript：The Good Parts</a>（<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3DR57sZUeY%2Bd8cQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMTkkQUThmjGRt4hWD5k2kjOLbAnOMKhNmUKQIyb6a3ytQ3ve%2Fl8eVw5iZOsdwOXcw9FIO2YfYLJG" target="_blank" rel="noopener">JavaScript语言精粹 修订版</a> <a href="http://pan.baidu.com/share/link?shareid=381250&amp;uk=4043705155&amp;fid=1352735570" target="_blank" rel="noopener">pdf</a>）<br><a href="http://net.tutsplus.com/tutorials/JavaScript-ajax/24-JavaScript-best-practices-for-beginners/www.packtpub.com/learning-jquery-1.3/boo" target="_blank" rel="noopener">Learning jQuery 1.3</a>（<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3D4OykraCsZmQcQipKwQzePOeEDrYVVa64LKpWJ%2Bin0XJRAdhuF14FMbOLn3WkI14k1aH1Hk3GeOiLbAnOMKhNmUKQIyb6a3ytlvVWKrpGG8BlboWVDkT64ko1h03ocqnd" target="_blank" rel="noopener">jQuery基础教程 第4版</a> <a href="http://download.csdn.net/detail/wang5yong1fei5/4631965" target="_blank" rel="noopener">pdf</a>）<br><a href="http://oreilly.com/catalog/9780596527464/" target="_blank" rel="noopener">Learning JavaScript</a>（<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3DrfLwyl5bYj0cQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMXFrLRwCQreut4hWD5k2kjOLbAnOMKhNmUKQIyb6a3ytK49eyT2J3GnLe30VI4uWF7UMVP8sn%2BEI" target="_blank" rel="noopener">JavaScript学习指南</a> <a href="http://pan.baidu.com/share/link?shareid=180605&amp;uk=839021066&amp;fid=1732362737" target="_blank" rel="noopener">pdf</a>）</p><p>读了他们……多次。我仍将继续!</p><h3 id="自执行函数"><a href="#自执行函数" class="headerlink" title="自执行函数"></a>自执行函数</h3><p>相比于调用函数，让函数在页面载入或者某一父函数被调用时自动执行，是十分简单方便的做法。你只需要把你的函数包在父辈之内，然后添上一个额外的括号，本质上这括号就触发了你定义的函数（<a href="http://20032334.javaeye.com/blog/288989" target="_blank" rel="noopener">了解更多</a>）。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        name: <span class="string">'jeff'</span>,</span><br><span class="line">        lastName: <span class="string">'way'</span></span><br><span class="line">    &#125;;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h3 id="原生代码永远比库快"><a href="#原生代码永远比库快" class="headerlink" title="原生代码永远比库快"></a>原生代码永远比库快</h3><p>诸如 jQuery 和 Mootools 这样的 JavaScript 库，能为你写代码的过程省下不少时间——尤其是当需要 AJAX 操作时。不过你可得记住，只要你的代码写得恰当，原生 JavaScript 总是会比利用代码库的写法执行得快一些。</p><p>jQuery 的 “each” 方法对于循环操作十分便利，但是使用原生态的for语句总归会快很多。</p><h3 id="Crockford（道格拉斯）的-JSON-Parse"><a href="#Crockford（道格拉斯）的-JSON-Parse" class="headerlink" title="Crockford（道格拉斯）的 JSON.Parse"></a>Crockford（道格拉斯）的 JSON.Parse</h3><p>尽管 JavaScript 2 会内建 JSON 处理器，但写这篇文章之时，我们还是需要自己实现。Douglas Crockford，JSON 的创建者，已经为我们创作出能直接使用的处理器了。<a href="http://www.json.org/" target="_blank" rel="noopener">查看相关信息</a>。</p><p>导入这段代码，你就能新建 JSON 全局对象，然后处理你的 .json 文件。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> response = <span class="built_in">JSON</span>.parse(xhr.responseText);</span><br><span class="line"><span class="keyword">var</span> container = <span class="built_in">document</span>.getElementById(<span class="string">'container'</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, len = response.length; i &lt; len; i++) &#123;</span><br><span class="line">    container.innerHTML += <span class="string">'&lt;li&gt;'</span> + response[i].name + <span class="string">' : '</span> + response[i].email + <span class="string">'&lt;/li&gt;'</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="移除-“language”-属性"><a href="#移除-“language”-属性" class="headerlink" title="移除 “language” 属性"></a>移除 “language” 属性</h3><p>很多年前，language还是每段script标签必备属性：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">language</span>=<span class="string">"javascript"</span>&gt;</span><span class="undefined"> </span></span><br><span class="line"><span class="undefined">    ... </span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然而，这个属性早已被弃用，所以请移除（译者注：html5 中已废弃，但如果你喜欢，你仍然可以添加）。</p><blockquote><p>本文为翻译文章，原文为“<a href="http://net.tutsplus.com/tutorials/JavaScript-ajax/24-JavaScript-best-practices-for-beginners/" target="_blank" rel="noopener">24 JavaScript Best Practices for Beginners</a>”，2009年</p></blockquote><p>关于#20 的补充，下面是译者认为的一些好书，有兴趣的读者可以留言讨论</p><ol><li>javascript模式（和上面JavaScript面向对象编程指南同一作者，这本书更好）</li><li>javascript设计模式</li><li>编写可维护的javascript（尼古拉斯新书）</li><li>高性能javascript（尼古拉斯 已绝版）</li><li>javascript语言精髓与编程实践</li><li>javascript高级程序设计（尼古拉斯）</li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;注：本文多次用到 Firebug 的 console 对象，请参考 &lt;a href=&quot;http://getfirebug.com/console.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Firebug Consol
      
    
    </summary>
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/categories/javascript/"/>
    
    
      <category term="javascript" scheme="http://blog.xieyangogo.cn/tags/javascript/"/>
    
      <category term="效率" scheme="http://blog.xieyangogo.cn/tags/%E6%95%88%E7%8E%87/"/>
    
      <category term="入门" scheme="http://blog.xieyangogo.cn/tags/%E5%85%A5%E9%97%A8/"/>
    
  </entry>
  
  <entry>
    <title>jQuery 事件</title>
    <link href="http://blog.xieyangogo.cn/2014/06/23/jQuery-%E4%BA%8B%E4%BB%B6/"/>
    <id>http://blog.xieyangogo.cn/2014/06/23/jQuery-事件/</id>
    <published>2014-06-23T05:16:56.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="移除事件"><a href="#移除事件" class="headerlink" title="移除事件"></a>移除事件</h3><p>unbind(type [, data]), data 是要移除的函数</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'#btn'</span>).unbind(<span class="string">"click"</span>);    <span class="comment">//移除click</span></span><br><span class="line">$(<span class="string">'#btn'</span>).unbind();    <span class="comment">//移除所有</span></span><br></pre></td></tr></table></figure><p>对于只需要触发一次的，随后就要立即解除绑定的情况，用one()</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'#btn'</span>).one(<span class="string">"click"</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; </span><br><span class="line">    ...</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="模拟操作"><a href="#模拟操作" class="headerlink" title="模拟操作"></a>模拟操作</h3><p>可以用 trigger() 方法完成模拟操作。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'#btn'</span>).trigger(<span class="string">"click"</span>); </span><br><span class="line">$(<span class="string">'#btn'</span>).click();</span><br></pre></td></tr></table></figure><h3 id="触发自定义事件"><a href="#触发自定义事件" class="headerlink" title="触发自定义事件"></a>触发自定义事件</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'#btn'</span>).bind(<span class="string">"myclick"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">$(<span class="string">'#btn'</span>).trigger(<span class="string">"myclick"</span>);</span><br></pre></td></tr></table></figure><h3 id="传递数据"><a href="#传递数据" class="headerlink" title="传递数据"></a>传递数据</h3><p>trigger(type [, data]);</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'#btn'</span>).bind(<span class="string">"myclick"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event, message1, message2</span>) </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">$(<span class="string">'#btn'</span>).trigger(<span class="string">"myclick"</span>, [<span class="string">"传给message1"</span>, <span class="string">"传给message2"</span>]);</span><br></pre></td></tr></table></figure><h3 id="执行默认操作"><a href="#执行默认操作" class="headerlink" title="执行默认操作"></a>执行默认操作</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"input"</span>).trigger(<span class="string">"focus"</span>);       <span class="comment">//不仅会触发input元素绑定的focus事件，还会触发默认操作——得到焦点。</span></span><br><span class="line"></span><br><span class="line">$(<span class="string">"input"</span>).triggerHandler(<span class="string">"focus"</span>);      <span class="comment">//只触发绑定事件，不执行浏览器默认操作</span></span><br></pre></td></tr></table></figure><h3 id="其他用法"><a href="#其他用法" class="headerlink" title="其他用法"></a>其他用法</h3><h4 id="绑定多个事件类型"><a href="#绑定多个事件类型" class="headerlink" title="绑定多个事件类型"></a>绑定多个事件类型</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"div"</span>).bind(<span class="string">"mouseover mouseout"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="添加事件命名空间"><a href="#添加事件命名空间" class="headerlink" title="添加事件命名空间"></a>添加事件命名空间</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"div"</span>).bind(<span class="string">"click.plugin"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>在所绑定的事件类型后面添加命名空间，这样在删除事件时只需要指定命名空间即可。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"div"</span>).unbind(<span class="string">".plugin"</span>);   <span class="comment">//删除空间内的事件</span></span><br><span class="line"></span><br><span class="line">$(<span class="string">"div"</span>).trigger(<span class="string">"click!"</span>);    <span class="comment">//触发所有不包含在命名空间中的click方法</span></span><br></pre></td></tr></table></figure><p>如果包含在命名空间的也要触发：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"div"</span>).trigger(<span class="string">"click"</span>);</span><br></pre></td></tr></table></figure><h3 id="另外摘录"><a href="#另外摘录" class="headerlink" title="另外摘录"></a>另外摘录</h3><p>===</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'div'</span>).bind(<span class="string">'click'</span>, RecommandProduct);   <span class="comment">//为div绑定RecommandProduct 函数</span></span><br><span class="line">$(<span class="string">'div'</span>).unbind(<span class="string">'click'</span>, RecommandProduct);   <span class="comment">//取消RecommandProduct 函数</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;移除事件&quot;&gt;&lt;a href=&quot;#移除事件&quot; class=&quot;headerlink&quot; title=&quot;移除事件&quot;&gt;&lt;/a&gt;移除事件&lt;/h3&gt;&lt;p&gt;unbind(type [, data]), data 是要移除的函数&lt;/p&gt;
&lt;figure class=&quot;highlig
      
    
    </summary>
    
      <category term="jQuery" scheme="http://blog.xieyangogo.cn/categories/jQuery/"/>
    
    
      <category term="jQuery" scheme="http://blog.xieyangogo.cn/tags/jQuery/"/>
    
      <category term="事件" scheme="http://blog.xieyangogo.cn/tags/%E4%BA%8B%E4%BB%B6/"/>
    
  </entry>
  
  <entry>
    <title>canvas画饼图</title>
    <link href="http://blog.xieyangogo.cn/2013/08/28/canvas%E7%94%BB%E9%A5%BC%E5%9B%BE/"/>
    <id>http://blog.xieyangogo.cn/2013/08/28/canvas画饼图/</id>
    <published>2013-08-28T09:36:44.000Z</published>
    <updated>2018-06-16T04:45:35.000Z</updated>
    
    <content type="html"><![CDATA[<p>不得不说，HTML5与CSS3的推出，将推翻老一代的网页制作者。特别在当今浏览器标准不断统一的情况下，新生的网页制作者几乎想不到在过去的时间里制作网页调兼容性是一件非常痛苦的事。</p><p>过去我们要实现圆角只能切图，如今在CSS3里只需一条代码搞定。而CSS3的功能远不止这些，配合HTML5还能画饼图数据分析。</p><p>然而，HTML5与CSS3严格意义上不具备编程语言的思想，所以还要借助JS去“画”它。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"bingtu"</span>  <span class="attr">width</span>=<span class="string">"224"</span> <span class="attr">height</span>=<span class="string">"130"</span> &gt;</span><span class="tag">&lt;/<span class="name">canvas</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> color = [<span class="string">"#999999"</span>, <span class="string">"#333333"</span>, <span class="string">"#336799"</span>]; <span class="comment">// 画饼图所用的颜色</span></span><br><span class="line"><span class="keyword">var</span> data = [<span class="number">25</span>, <span class="number">35</span>, <span class="number">50</span>];  <span class="comment">// 数据比，全部是数据加起来是100，才能画满整个圆</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">drawCircle</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> canvas = <span class="built_in">document</span>.getElementById(<span class="string">"bingtu"</span>);</span><br><span class="line">    <span class="keyword">var</span> ctx = canvas.getContext(<span class="string">"2d"</span>);   <span class="comment">// 画2D平面图</span></span><br><span class="line">    <span class="keyword">var</span> startPoint = <span class="number">1.5</span> * <span class="built_in">Math</span>.PI;   <span class="comment">// 绘制方向起点，有逆顺之分，通常是 1.5*Math.PI 或者 0</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; data.length; i++) &#123;</span><br><span class="line">        ctx.fillStyle = color[i];  <span class="comment">// 填充颜色</span></span><br><span class="line">        ctx.strokeStyle = color[i];</span><br><span class="line">        ctx.beginPath();  <span class="comment">// 开始画</span></span><br><span class="line">        ctx.moveTo(<span class="number">112</span>, <span class="number">65</span>);  <span class="comment">//每次回到圆心，第一个值 112 是 canvas 宽的一半， 65是 canvas 高的一半。其实也可以利用js获取对象的宽高算出来就好，省该canvas宽高后还要去调整JS代码中的参数。</span></span><br><span class="line">        ctx.arc(<span class="number">112</span>, <span class="number">65</span>, <span class="number">65</span>, startPoint, startPoint - <span class="built_in">Math</span>.PI * <span class="number">2</span> * (data[i] / <span class="number">100</span>), <span class="literal">true</span>);  <span class="comment">// 开始画图，前两个参数是圆心坐标，第三个参数是半径大小，第四个参数是圆周起始位置，第五个参数是弧长，就是我们圆弧的范围，Math.PI*2就是整个圆了，Math.PI是半圆，第六个参数是个布尔值，就是确定是顺时针还是逆时针，这里false是顺时针。</span></span><br><span class="line">        ctx.fill();  <span class="comment">// 填充</span></span><br><span class="line">        ctx.stroke();  <span class="comment">// 边框</span></span><br><span class="line">        startPoint -= <span class="built_in">Math</span>.PI * <span class="number">2</span> * (data[i] / <span class="number">100</span>);  <span class="comment">// 画完一个善行以后，重新计算回到原点开始绘制下一扇，否则画出来的饼图拼起来就不是圆形了。</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">drawCircle();</span><br></pre></td></tr></table></figure><p>canvas是HTML5新增的画布标签，个人感觉这个标签在将来将大有用途，喜欢研究HTML5的童鞋可以深入研究这个标签。canvas上的高度可以自己随意设置，当然宽不能小于高，否则图形出来不是圆的。</p><p>效果：</p><p><img src="/blog/images/pie-canvas/canvas.jpg" alt=""></p><p>目前唯一的遗憾是还不能在图上加文字。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;不得不说，HTML5与CSS3的推出，将推翻老一代的网页制作者。特别在当今浏览器标准不断统一的情况下，新生的网页制作者几乎想不到在过去的时间里制作网页调兼容性是一件非常痛苦的事。&lt;/p&gt;
&lt;p&gt;过去我们要实现圆角只能切图，如今在CSS3里只需一条代码搞定。而CSS3的功能远
      
    
    </summary>
    
      <category term="html5" scheme="http://blog.xieyangogo.cn/categories/html5/"/>
    
    
      <category term="HTML5" scheme="http://blog.xieyangogo.cn/tags/HTML5/"/>
    
      <category term="canvas" scheme="http://blog.xieyangogo.cn/tags/canvas/"/>
    
      <category term="饼图" scheme="http://blog.xieyangogo.cn/tags/%E9%A5%BC%E5%9B%BE/"/>
    
  </entry>
  
</feed>
