<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>大涛子</title>
  
  <subtitle>简单、极致、不失风趣</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://www.yangtao.site/"/>
  <updated>2020-03-18T04:09:39.746Z</updated>
  <id>https://www.yangtao.site/</id>
  
  <author>
    <name>杨涛</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Hi，很高兴见到你~</title>
    <link href="https://www.yangtao.site/2048/10/24/hello-world/"/>
    <id>https://www.yangtao.site/2048/10/24/hello-world/</id>
    <published>2048-10-24T05:14:15.000Z</published>
    <updated>2020-03-18T04:09:39.746Z</updated>
    
    <content type="html"><![CDATA[<p>欢迎<a href="/2048/10/24/hello-world/">在此</a>留言，或者去<a href="/friends/">友链</a>看看大家 ~</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;欢迎&lt;a href=&quot;/2048/10/24/hello-world/&quot;&gt;在此&lt;/a&gt;留言，或者去&lt;a href=&quot;/friends/&quot;&gt;友链&lt;/a&gt;看看大家 ~&lt;/p&gt;

      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>在 Nginx 服务器上配置 HTTPS 证书</title>
    <link href="https://www.yangtao.site/2020/04/04/http-https/"/>
    <id>https://www.yangtao.site/2020/04/04/http-https/</id>
    <published>2020-04-04T13:56:50.000Z</published>
    <updated>2020-04-05T03:26:01.512Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>安装 https 证书的一些过程</p></blockquote><a id="more"></a><h2 id="下载证书"><a href="#下载证书" class="headerlink" title="下载证书"></a>下载证书</h2><p>我使用的阿里云免费个人版，有效期一年。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><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">~/Downloads ❯❯❯ <span class="built_in">cd</span> 3713633_www.yangtao.site_nginx/</span><br><span class="line">~/D/3713633_www.yangtao.site_nginx ❯❯❯ ls</span><br><span class="line">3713633_www.yangtao.site.key 3713633_www.yangtao.site.pem</span><br><span class="line">~/D/3713633_www.yangtao.site_nginx ❯❯❯</span><br><span class="line">scp 3713633_www.yangtao.site.key 3713633_www.yangtao.site.pem root@47.101.33.81:/usr/<span class="built_in">local</span>/nginx/cert/</span><br><span class="line">root@47.101.33.81<span class="string">'s password:</span></span><br><span class="line"><span class="string">Permission denied, please try again.</span></span><br><span class="line"><span class="string">root@47.101.33.81'</span>s password:</span><br><span class="line">3713633_www.yangtao.site.key            100% 1675    88.9KB/s   00:00</span><br><span class="line">3713633_www.yangtao.site.pem            100% 3675   274.2KB/s   00:00</span><br><span class="line">~/D/3713633_www.yangtao.site_nginx ❯❯❯</span><br></pre></td></tr></table></figure><figure class="highlight sh"><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">root@istaotao:/usr/<span class="built_in">local</span>/nginx/cert<span class="comment"># ls</span></span><br><span class="line">3713633_www.yangtao.site.key  3713633_www.yangtao.site.pem</span><br></pre></td></tr></table></figure><h2 id="修改配置"><a href="#修改配置" class="headerlink" title="修改配置"></a>修改配置</h2><p>按照下文中注释内容修改 nginx.conf 文件：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><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">server &#123;</span><br><span class="line">  listen 443 ssl;   <span class="comment">#SSL协议访问端口号为443。此处如未添加ssl，可能会造成Nginx无法启动。</span></span><br><span class="line">  server_name localhost;  <span class="comment">#将localhost修改为您证书绑定的域名，例如：www.example.com。</span></span><br><span class="line">  root html;</span><br><span class="line">  index index.html index.htm;</span><br><span class="line">  ssl_certificate cert/domain name.pem;   <span class="comment">#将domain name.pem替换成您证书的文件名。</span></span><br><span class="line">  ssl_certificate_key cert/domain name.key;   <span class="comment">#将domain name.key替换成您证书的密钥文件名。</span></span><br><span class="line">  ssl_session_timeout 5m;</span><br><span class="line">  ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;  <span class="comment">#使用此加密套件。</span></span><br><span class="line">  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;   <span class="comment">#使用该协议进行配置。</span></span><br><span class="line">  ssl_prefer_server_ciphers on;</span><br><span class="line">  location / &#123;</span><br><span class="line">    root html;   <span class="comment">#站点目录。</span></span><br><span class="line">    index index.html index.htm;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="强制-http-跳转"><a href="#强制-http-跳转" class="headerlink" title="强制 http 跳转"></a>强制 http 跳转</h2><p>使用 curl 命令：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><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></pre></td><td class="code"><pre><span class="line">~ ❯❯❯ curl baidu.com -v</span><br><span class="line">*   Trying 220.181.38.148...</span><br><span class="line">* TCP_NODELAY <span class="built_in">set</span></span><br><span class="line">* Connected to baidu.com (220.181.38.148) port 80 (<span class="comment">#0)</span></span><br><span class="line">&gt; GET / HTTP/1.1</span><br><span class="line">&gt; Host: baidu.com</span><br><span class="line">&gt; User-Agent: curl/7.64.1</span><br><span class="line">&gt; Accept: */*</span><br><span class="line">&gt;</span><br><span class="line">&lt; HTTP/1.1 200 OK</span><br><span class="line">&lt; Date: Sat, 04 Apr 2020 16:33:35 GMT</span><br><span class="line">&lt; Server: Apache</span><br><span class="line">&lt; Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT</span><br><span class="line">&lt; ETag: <span class="string">"51-47cf7e6ee8400"</span></span><br><span class="line">&lt; Accept-Ranges: bytes</span><br><span class="line">&lt; Content-Length: 81</span><br><span class="line">&lt; Cache-Control: max-age=86400</span><br><span class="line">&lt; Expires: Sun, 05 Apr 2020 16:33:35 GMT</span><br><span class="line">&lt; Connection: Keep-Alive</span><br><span class="line">&lt; Content-Type: text/html</span><br><span class="line">&lt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;meta http-equiv=<span class="string">"refresh"</span> content=<span class="string">"0;url=http://www.baidu.com/"</span>&gt;</span><br><span class="line">&lt;/html&gt;</span><br><span class="line">* Connection <span class="comment">#0 to host baidu.com left intact</span></span><br><span class="line">* Closing connection 0</span><br><span class="line">~ ❯❯❯</span><br></pre></td></tr></table></figure><p>可以看到百度很巧妙的利用 meta 的刷新作用，将 baidu.com 跳转到 <a href="http://www.baidu.com" target="_blank" rel="noopener">www.baidu.com</a><br>同理, 我们也可以用这个特性来实现 http 向 https 的跳转</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># index.html</span><br><span class="line"><span class="tag">&lt;<span class="name">html</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">"refresh"</span> <span class="attr">content</span>=<span class="string">"0;url=https://www.yangtao.site/"</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><figure class="highlight sh"><table><tr><td class="gutter"><pre><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">server &#123;</span><br><span class="line">  listen 80;</span><br><span class="line">  server_name yangtao.site;</span><br><span class="line"></span><br><span class="line">  location / &#123;</span><br><span class="line">      root /var/www/html/refresh/;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试 <a href="https://www.yangtao.site">yangtao.site</a>：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><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">~ ❯❯❯ curl yangtao.site -v</span><br><span class="line">*   Trying 47.101.33.81...</span><br><span class="line">* TCP_NODELAY <span class="built_in">set</span></span><br><span class="line">* Connected to yangtao.site (47.101.33.81) port 80 (<span class="comment">#0)</span></span><br><span class="line">&gt; GET / HTTP/1.1</span><br><span class="line">&gt; Host: yangtao.site</span><br><span class="line">&gt; User-Agent: curl/7.64.1</span><br><span class="line">&gt; Accept: */*</span><br><span class="line">&gt;</span><br><span class="line">&lt; HTTP/1.1 200 OK</span><br><span class="line">&lt; Server: nginx/1.14.0 (Ubuntu)</span><br><span class="line">&lt; Date: Sat, 04 Apr 2020 16:34:16 GMT</span><br><span class="line">&lt; Content-Type: text/html</span><br><span class="line">&lt; Content-Length: 91</span><br><span class="line">&lt; Last-Modified: Sat, 04 Apr 2020 16:04:03 GMT</span><br><span class="line">&lt; Connection: keep-alive</span><br><span class="line">&lt; ETag: <span class="string">"5e88aff3-5b"</span></span><br><span class="line">&lt; Accept-Ranges: bytes</span><br><span class="line">&lt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">    &lt;meta http-equiv=<span class="string">"refresh"</span> content=<span class="string">"0;url=https://www.yangtao.site/"</span>&gt;</span><br><span class="line">&lt;/html&gt;</span><br><span class="line">* Connection <span class="comment">#0 to host yangtao.site left intact</span></span><br><span class="line">* Closing connection 0</span><br></pre></td></tr></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://help.aliyun.com/document_detail/98728.html?spm=5176.2020520163.0.0.36a756a7iFDdyN" target="_blank" rel="noopener">在 Nginx/Tengine 服务器上安装证书</a></li><li><a href="https://www.cnblogs.com/chnmig/p/10343890.html" target="_blank" rel="noopener">Nginx 配置 https 证书</a></li><li><a href="https://www.jianshu.com/p/29add30461ec" target="_blank" rel="noopener">nginx 配置 http 强制跳转 https</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;安装 https 证书的一些过程&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="服务端" scheme="https://www.yangtao.site/categories/%E6%9C%8D%E5%8A%A1%E7%AB%AF/"/>
    
    
      <category term="Hexo" scheme="https://www.yangtao.site/tags/Hexo/"/>
    
      <category term="Ubuntu" scheme="https://www.yangtao.site/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript 深浅拷贝</title>
    <link href="https://www.yangtao.site/2020/04/03/code-copy/"/>
    <id>https://www.yangtao.site/2020/04/03/code-copy/</id>
    <published>2020-04-03T03:04:25.000Z</published>
    <updated>2020-04-03T03:46:22.371Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>浅拷贝技巧：数组<code>concat slice</code>，对象<code>Object.assign()</code>等；<br>深拷贝技巧：<code>JSON.parse(JSON.stringify(arr1))</code>。</p></blockquote><a id="more"></a><h2 id="数组的浅拷贝"><a href="#数组的浅拷贝" class="headerlink" title="数组的浅拷贝"></a>数组的浅拷贝</h2><p>如果数组元素是基本类型，就会拷贝一份，互不影响，而如果是对象或者数组，就会只拷贝对象和数组的引用，这样我们无论在新旧数组进行了修改，两者都会发生变化。</p><p>我们把这种复制引用的拷贝方法称之为浅拷贝，与之对应的就是深拷贝，深拷贝就是指完全的拷贝一个对象，即使嵌套了对象，两者也相互分离，修改一个对象的属性，也不会影响另一个。</p><p>比如，数组的一些方法：<code>concat、slice</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="string">"old"</span>, <span class="number">1</span>, <span class="literal">true</span>, <span class="literal">null</span>, <span class="literal">undefined</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> newArr = arr.concat();</span><br><span class="line">newArr.shift();</span><br><span class="line"><span class="built_in">console</span>.log(arr); <span class="comment">// [ 'old', 1, true, null, undefined ]</span></span><br><span class="line"><span class="built_in">console</span>.log(newArr); <span class="comment">// [ 1, true, null, undefined ]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> newArr2 = arr.slice();</span><br><span class="line"><span class="built_in">console</span>.log(newArr2); <span class="comment">// [ 'old', 1, true, null, undefined ]</span></span><br></pre></td></tr></table></figure><p>但是如果数组嵌套了对象或者数组的话，就会都受影响，比如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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> arrObj = [&#123; <span class="attr">a</span>: <span class="number">1</span> &#125;, &#123; <span class="attr">b</span>: <span class="number">2</span> &#125;];</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> newArrObj = arrObj.concat();</span><br><span class="line">newArrObj[<span class="number">0</span>].a = <span class="string">"aaa"</span>;</span><br><span class="line"><span class="built_in">console</span>.log(newArrObj); <span class="comment">// [ &#123; a: 'aaa' &#125;, &#123; b: 2 &#125; ]</span></span><br><span class="line"><span class="built_in">console</span>.log(arrObj); <span class="comment">// [ &#123; a: 'aaa' &#125;, &#123; b: 2 &#125; ]</span></span><br></pre></td></tr></table></figure><h2 id="数组的深拷贝"><a href="#数组的深拷贝" class="headerlink" title="数组的深拷贝"></a>数组的深拷贝</h2><p>使用 <code>JSON.stringify()</code>和<code>JSON.parse()</code>，不管是数组还是对象，都可以实现深拷贝，但是不能拷贝函数，会返回一个 null：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr1 = [<span class="string">"old"</span>, <span class="number">1</span>, <span class="literal">true</span>, [<span class="string">"old1"</span>, <span class="string">"old2"</span>], &#123; <span class="attr">old</span>: <span class="number">1</span> &#125;, <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> newArr1 = <span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(arr1));</span><br><span class="line">newArr1.shift();</span><br><span class="line"><span class="built_in">console</span>.log(arr1); <span class="comment">// [ 'old', 1, true, [ 'old1', 'old2' ], &#123; old: 1 &#125;, [Function] ]</span></span><br><span class="line"><span class="built_in">console</span>.log(newArr1); <span class="comment">// [ 1, true, [ 'old1', 'old2' ], &#123; old: 1 &#125;, null ]</span></span><br></pre></td></tr></table></figure><h2 id="Object-assign"><a href="#Object-assign" class="headerlink" title="Object.assign"></a>Object.assign</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign" target="_blank" rel="noopener">Object.assign()</a> 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// target 目标对象，sources 源对象</span></span><br><span class="line"><span class="built_in">Object</span>.assign(target, ...sources);</span><br></pre></td></tr></table></figure><p><code>Object.assign</code> 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的<code>[[Get]]</code>和目标对象的<code>[[Set]]</code>，所以它会调用相关 getter 和 setter。</p><p>String 类型和 Symbol 类型的属性都会被拷贝。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 赋值一个对象</span></span><br><span class="line"><span class="keyword">const</span> obj = &#123; <span class="attr">a</span>: <span class="number">1</span> &#125;;</span><br><span class="line"><span class="keyword">const</span> copy = <span class="built_in">Object</span>.assign(&#123;&#125;, obj);</span><br><span class="line"><span class="built_in">console</span>.log(copy); <span class="comment">// &#123; a: 1 &#125;</span></span><br></pre></td></tr></table></figure><p>针对深拷贝，需要使用其他办法，如借助 JSON，因为 <code>Object.assign()</code>拷贝的是属性值。假如源对象的属性值是一个对象的引用，那么它也只指向那个引用。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">obj1 = &#123; <span class="attr">a</span>: <span class="number">0</span>, <span class="attr">b</span>: &#123; <span class="attr">c</span>: <span class="number">0</span> &#125; &#125;;</span><br><span class="line"><span class="keyword">let</span> obj3 = <span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(obj1));</span><br><span class="line">obj1.a = <span class="number">4</span>;</span><br><span class="line">obj1.b.c = <span class="number">4</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">JSON</span>.stringify(obj3));  <span class="comment">// &#123;"a":0,"b":&#123;"c":0&#125;&#125;</span></span><br></pre></td></tr></table></figure><h2 id="浅拷贝的实现"><a href="#浅拷贝的实现" class="headerlink" title="浅拷贝的实现"></a>浅拷贝的实现</h2><p>技巧型的拷贝，如上边使用的 <code>concat、slice、JSON.stringify</code>等，如果要实现一个对象或者数组的浅拷贝，该怎么实现呢？</p><p>思路：既然是浅拷贝，那就只需要遍历，把对应的属性及属性值添加到新的对象，并返回。</p><p>代码实现：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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> shallowCopy = <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> obj !== <span class="string">"object"</span>) <span class="keyword">return</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> newObj = obj <span class="keyword">instanceof</span> <span class="built_in">Array</span> ? [] : &#123;&#125;;</span><br><span class="line">  <span class="comment">// 遍历obj，并且判断是obj的属性才拷贝</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> obj) &#123;</span><br><span class="line">    <span class="keyword">if</span> (obj.hasOwnProperty(key)) &#123;</span><br><span class="line">      newObj[key] = obj[key];</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> newObj;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> arr20 = [<span class="string">"old"</span>, <span class="number">1</span>, <span class="literal">true</span>, [<span class="string">"old1"</span>, <span class="string">"old2"</span>], &#123; <span class="attr">old</span>: <span class="number">1</span> &#125;, <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> newArr20 = shallowCopy(arr20);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(&#123; newArr20 &#125;);</span><br><span class="line"><span class="comment">// [ 'old', 1, true, [ 'old1', 'old2' ], &#123; old: 1 &#125;, [Function] ]</span></span><br></pre></td></tr></table></figure><h2 id="深拷贝的实现"><a href="#深拷贝的实现" class="headerlink" title="深拷贝的实现"></a>深拷贝的实现</h2><p>思路：如果是对象，通过递归调用拷贝函数</p><p>代码实现：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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">var</span> deepCopy = <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> obj !== <span class="string">"object"</span>) <span class="keyword">return</span>;</span><br><span class="line">  <span class="keyword">var</span> newObj = obj <span class="keyword">instanceof</span> <span class="built_in">Array</span> ? [] : &#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> obj) &#123;</span><br><span class="line">    <span class="keyword">if</span> (obj.hasOwnProperty(key)) &#123;</span><br><span class="line">      newObj[key] =</span><br><span class="line">        <span class="keyword">typeof</span> obj[key] !== <span class="string">"object"</span> ? obj[key] : deepCopy(obj[key]);</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> newObj;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj = &#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">  b: &#123;</span><br><span class="line">    name: <span class="string">"Tony"</span>,</span><br><span class="line">    age: <span class="number">10</span></span><br><span class="line">  &#125;,</span><br><span class="line">  c: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> newObj = deepCopy(obj);</span><br><span class="line"><span class="built_in">console</span>.log(newObj);</span><br><span class="line"><span class="comment">// &#123; a: [Function: a],</span></span><br><span class="line"><span class="comment">// b: &#123; name: 'Tony', age: 10 &#125;,</span></span><br><span class="line"><span class="comment">// c: [ 1, 2, 3 ] &#125;</span></span><br></pre></td></tr></table></figure><h2 id="模拟-jQuery-的-extend"><a href="#模拟-jQuery-的-extend" class="headerlink" title="模拟 jQuery 的 extend"></a>模拟 jQuery 的 extend</h2><p><strong>extend</strong> 的用法：合并两个或者更多的对象的内容到第一个对象中。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jQuery.extend( [deep], target, object1 [, objectN ] )</span><br></pre></td></tr></table></figure><ul><li><strong>deep</strong>，布尔值，如果为 true，进行深拷贝；false 做浅拷贝，target 就往后移动到第二个参数</li><li><strong>target</strong>，表示要拓展的目标，我们就称它为目标对象吧。</li><li>后面的参数，都传入对象，内容都会复制到目标对象中，我们就称它们为待复制对象吧。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> toString = class2type.toString;</span><br><span class="line"><span class="keyword">var</span> hasOwn = class2type.hasOwnProperty;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isPlainObject</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> proto, Ctor;</span><br><span class="line">  <span class="keyword">if</span> (!obj || toString.call(obj) !== <span class="string">"[object Object]"</span>) &#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">  proto = <span class="built_in">Object</span>.getPrototypeOf(obj);</span><br><span class="line">  <span class="keyword">if</span> (!proto) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  Ctor = hasOwn.call(proto, <span class="string">"constructor"</span>) &amp;&amp; proto.constructor;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="keyword">typeof</span> Ctor === <span class="string">"function"</span> &amp;&amp;</span><br><span class="line">    hasOwn.toString.call(Ctor) === hasOwn.toString.call(<span class="built_in">Object</span>)</span><br><span class="line">  );</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">extend</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 默认不进行深拷贝</span></span><br><span class="line">  <span class="keyword">var</span> deep = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">var</span> name, options, src, copy, clone, copyIsArray;</span><br><span class="line">  <span class="keyword">var</span> length = <span class="built_in">arguments</span>.length;</span><br><span class="line">  <span class="comment">// 记录要复制的对象的下标</span></span><br><span class="line">  <span class="keyword">var</span> i = <span class="number">1</span>;</span><br><span class="line">  <span class="comment">// 第一个参数不传布尔值的情况下，target 默认是第一个参数</span></span><br><span class="line">  <span class="keyword">var</span> target = <span class="built_in">arguments</span>[<span class="number">0</span>] || &#123;&#125;;</span><br><span class="line">  <span class="comment">// 如果第一个参数是布尔值，第二个参数是 target</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> target == <span class="string">"boolean"</span>) &#123;</span><br><span class="line">    deep = target;</span><br><span class="line">    target = <span class="built_in">arguments</span>[i] || &#123;&#125;;</span><br><span class="line">    i++;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 如果target不是对象，我们是无法进行复制的，所以设为 &#123;&#125;</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> target !== <span class="string">"object"</span> &amp;&amp; !isFunction(target)) &#123;</span><br><span class="line">    target = &#123;&#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="keyword">for</span> (; i &lt; length; i++) &#123;</span><br><span class="line">    <span class="comment">// 获取当前对象</span></span><br><span class="line">    options = <span class="built_in">arguments</span>[i];</span><br><span class="line">    <span class="comment">// 要求不能为空 避免 extend(a,,b) 这种情况</span></span><br><span class="line">    <span class="keyword">if</span> (options != <span class="literal">null</span>) &#123;</span><br><span class="line">      <span class="keyword">for</span> (name <span class="keyword">in</span> options) &#123;</span><br><span class="line">        <span class="comment">// 目标属性值</span></span><br><span class="line">        src = target[name];</span><br><span class="line">        <span class="comment">// 要复制的对象的属性值</span></span><br><span class="line">        copy = options[name];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 解决循环引用</span></span><br><span class="line">        <span class="keyword">if</span> (target === copy) &#123;</span><br><span class="line">          <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 要递归的对象必须是 plainObject 或者数组</span></span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">          deep &amp;&amp;</span><br><span class="line">          copy &amp;&amp;</span><br><span class="line">          (isPlainObject(copy) || (copyIsArray = <span class="built_in">Array</span>.isArray(copy)))</span><br><span class="line">        ) &#123;</span><br><span class="line">          <span class="comment">// 要复制的对象属性值类型需要与目标属性值相同</span></span><br><span class="line">          <span class="keyword">if</span> (copyIsArray) &#123;</span><br><span class="line">            copyIsArray = <span class="literal">false</span>;</span><br><span class="line">            clone = src &amp;&amp; <span class="built_in">Array</span>.isArray(src) ? src : [];</span><br><span class="line">          &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            clone = src &amp;&amp; isPlainObject(src) ? src : &#123;&#125;;</span><br><span class="line">          &#125;</span><br><span class="line"></span><br><span class="line">          target[name] = extend(deep, clone, copy);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (copy !== <span class="literal">undefined</span>) &#123;</span><br><span class="line">          target[name] = copy;</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">return</span> target;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="学习资料"><a href="#学习资料" class="headerlink" title="学习资料"></a>学习资料</h2><ul><li><a href="https://github.com/mqyqingfeng/Blog/issues/32" target="_blank" rel="noopener">JavaScript 专题之深浅拷贝</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/33" target="_blank" rel="noopener">JavaScript 专题之从零实现 jQuery 的 extend</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;浅拷贝技巧：数组&lt;code&gt;concat slice&lt;/code&gt;，对象&lt;code&gt;Object.assign()&lt;/code&gt;等；&lt;br&gt;深拷贝技巧：&lt;code&gt;JSON.parse(JSON.stringify(arr1))&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="面试" scheme="https://www.yangtao.site/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript 防抖、节流</title>
    <link href="https://www.yangtao.site/2020/04/03/code-debounce-throttle/"/>
    <id>https://www.yangtao.site/2020/04/03/code-debounce-throttle/</id>
    <published>2020-04-03T02:19:34.000Z</published>
    <updated>2020-04-03T02:29:34.877Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>性能优化中对于频繁触发事件，常用的两种解决方式：debounce 和 throttle。</p></blockquote><a id="more"></a><p>在前端开发中会遇到一些频繁的事件触发，比如：</p><ul><li>window 的 resize、scroll</li><li>mousedown、mousemove</li><li>keyup、keydown</li></ul><h2 id="防抖"><a href="#防抖" class="headerlink" title="防抖"></a>防抖</h2><p>防抖的原理就是：</p><p>你尽管触发事件，但是我一定在事件触发 n 秒后才执行，如果你在一个事件触发的 n 秒内又触发了这个事件，那我就以新的事件的时间为准，n 秒后才执行。</p><p>总之，就是要等你触发完事件 n 秒内不再触发事件，我才执行!</p><h3 id="起步"><a href="#起步" class="headerlink" title="起步"></a>起步</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">debounce</span>(<span class="params">func, wait</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> timer;</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">    clearTimeout(timer);</span><br><span class="line">    timer = setTimeout(func, wait);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="this、arguments、返回值"><a href="#this、arguments、返回值" class="headerlink" title="this、arguments、返回值"></a>this、arguments、返回值</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">debounce</span>(<span class="params">func, wait</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> timer;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> _this = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">var</span> args = <span class="built_in">arguments</span>;</span><br><span class="line"></span><br><span class="line">    clearTimeout(timer);</span><br><span class="line">    timer = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> result = func.apply(_this, args);</span><br><span class="line">    &#125;, wait);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</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>这个需求就是：</p><p>我不希望非要等到事件停止触发后才执行，我希望立刻执行函数，然后等到停止触发 n 秒后，才可以重新触发执行。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">debounce</span>(<span class="params">func, wait, immediate</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> timerId, result;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> _this = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">var</span> _args = <span class="built_in">arguments</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (timerId) clearTimeout(timerId);</span><br><span class="line">    <span class="keyword">if</span> (immediate) &#123;</span><br><span class="line">      <span class="comment">// 触发 func 从队尾提到队前。记住：func同步执行，timer异步执行</span></span><br><span class="line">      <span class="comment">// 1. callNow 初始值是 true, 同步立即执行；随后 timer 才开始执行</span></span><br><span class="line">      <span class="comment">// 2. wait 期间，timer 是一个 ID 数字，所以 callNow 为 false，func 在此期间永远不会执行</span></span><br><span class="line">      <span class="comment">// 3. wait 之后，timer 赋值 null，callNow 为 true，func 又开始立即执行。</span></span><br><span class="line">      <span class="comment">// 依次循环</span></span><br><span class="line">      <span class="keyword">var</span> callNow = !timerId;</span><br><span class="line">      timerId = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        timerId = <span class="literal">null</span>;</span><br><span class="line">      &#125;, wait);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (callNow) &#123;</span><br><span class="line">        result = func.apply(_this, _args);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      timerId = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        result = func.apply(_this, _args);</span><br><span class="line">      &#125;, wait);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>需要理解：</p><ul><li><code>timeId</code> 是闭包变量，初始化时是 <code>undefined</code></li><li><code>setTimeout</code> 返回的是定时器的 id ，一个 &gt; 0 的数字</li><li><code>clearTimeout</code> 不会改变 <code>timeId</code> 的值</li><li>若 <code>timeId</code> 经历过赋值，即执行过 <code>setTimeout</code> ，则 <code>!timeId</code> 为假</li></ul><h3 id="取消防抖"><a href="#取消防抖" class="headerlink" title="取消防抖"></a>取消防抖</h3><p>比如说我 debounce 的时间间隔是 10 秒钟，immediate 为 true，这样的话，我只有等 10 秒后才能重新触发事件，现在我希望有一个按钮，点击后，取消防抖，这样我再去触发，就可以又立刻执行啦。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">debounce</span>(<span class="params">func, wait, immediate</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> timerId, result;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> debounced = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> _this = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">var</span> _args = <span class="built_in">arguments</span>;</span><br><span class="line">    <span class="keyword">if</span> (timerId) clearTimeout(timerId);</span><br><span class="line">    <span class="keyword">if</span> (immediate) &#123;</span><br><span class="line">      <span class="keyword">var</span> callNow = !timerId;</span><br><span class="line">      timerId = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        timerId = <span class="literal">null</span>;</span><br><span class="line">      &#125;, wait);</span><br><span class="line">      <span class="keyword">if</span> (callNow) &#123;</span><br><span class="line">        result = func.apply(_this, _args);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      timerId = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        result = func.apply(_this, _args);</span><br><span class="line">      &#125;, wait);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  debounced.cancel = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    clearTimeout(timerId);</span><br><span class="line">    timerId = <span class="literal">null</span>;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> debounced;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>用法：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> setUseAction = debounce(getUserAction, <span class="number">10000</span>, <span class="literal">true</span>);</span><br><span class="line">container.onmousemove = setUseAction;</span><br><span class="line">button.addEventListener(<span class="string">"click"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  setUseAction.cancel();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="节流"><a href="#节流" class="headerlink" title="节流"></a>节流</h2><p>节流的原理很简单：如果你持续触发事件，每隔一段时间，只执行一次事件。</p><p>根据首次是否执行以及结束后是否执行，效果有所不同，实现的方式也有所不同。<br>我们用 leading 代表首次是否执行，trailing 代表结束后是否再执行一次。</p><p>关于节流的实现，有两种主流的实现方式，一种是使用<strong>时间戳</strong>，一种是设置<strong>定时器</strong>。</p><h3 id="时间戳"><a href="#时间戳" class="headerlink" title="时间戳"></a>时间戳</h3><p>当触发事件的时候，我们取出当前的时间戳，然后减去之前的时间戳(最一开始值设为 0 )，如果大于设置的时间周期，就执行函数，然后更新时间戳为当前的时间戳，如果小于，就不执行。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">throttle</span>(<span class="params">func, wait</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> context, args;</span><br><span class="line">  <span class="keyword">var</span> previous = <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="keyword">var</span> now = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">    context = <span class="keyword">this</span>;</span><br><span class="line">    args = <span class="built_in">arguments</span>;</span><br><span class="line">    <span class="keyword">if</span> (now - previous &gt; wait) &#123;</span><br><span class="line">      func.apply(context, args);</span><br><span class="line">      previous = now;</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="定时器"><a href="#定时器" class="headerlink" title="定时器"></a>定时器</h3><p>当触发事件的时候，我们设置一个定时器，再触发事件的时候，如果定时器存在，就不执行，直到定时器执行，然后执行函数，清空定时器，这样就可以设置下个定时器。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">throttle</span>(<span class="params">func, wait</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> context, args, timeout;</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">    context = <span class="keyword">this</span>;</span><br><span class="line">    args = <span class="built_in">arguments</span>;</span><br><span class="line">    <span class="keyword">if</span> (!timeout) &#123;</span><br><span class="line">      timeout = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        timeout = <span class="literal">null</span>;</span><br><span class="line">        func.apply(context, args);</span><br><span class="line">      &#125;, wait);</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><ul><li>第一种事件会立刻执行，第二种事件会在 n 秒后第一次执行</li><li>第一种事件停止触发后没有办法再执行事件，第二种事件停止触发后依然会再执行一次事件</li></ul><h3 id="双剑合璧"><a href="#双剑合璧" class="headerlink" title="双剑合璧"></a>双剑合璧</h3><p>鼠标移入能立刻执行，停止触发的时候还能再执行一次！</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">throttle</span>(<span class="params">func, wait</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> context, args, timeout, result;</span><br><span class="line">  <span class="keyword">var</span> previous = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> later = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    previous = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">    timeout = <span class="literal">null</span>;</span><br><span class="line">    result = func.apply(context, args);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> throttled = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> now = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">    <span class="keyword">var</span> remaining = wait - (now - previous);</span><br><span class="line">    context = <span class="keyword">this</span>;</span><br><span class="line">    args = <span class="built_in">arguments</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (remaining &lt;= <span class="number">0</span> || remaining &gt; wait) &#123;</span><br><span class="line">      <span class="keyword">if</span> (timeout) &#123;</span><br><span class="line">        clearTimeout(timeout);</span><br><span class="line">        timeout = <span class="literal">null</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      result = func.apply(context, args);</span><br><span class="line">      previous = now;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!timeout) &#123;</span><br><span class="line">      timeout = setTimeout(later, remaining);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> throttled;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h3><p>设置个 options 作为第三个参数，然后根据传的值判断到底哪种效果，我们约定:</p><ul><li><code>leading：false</code> 表示禁用第一次执行</li><li><code>trailing: false</code> 表示禁用停止触发的回调</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">throttle</span>(<span class="params">func, wait, options</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> context, args, timeout, result;</span><br><span class="line">  <span class="keyword">var</span> previous = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (!options) &#123;</span><br><span class="line">    options = &#123;&#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> later = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    previous = options.leading ? <span class="number">0</span> : <span class="keyword">new</span> <span class="built_in">Date</span>().getTime();</span><br><span class="line">    timeout = <span class="literal">null</span>;</span><br><span class="line">    result = func.apply(context, args);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> throttled = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> now = <span class="keyword">new</span> <span class="built_in">Date</span>().getTime();</span><br><span class="line">    <span class="keyword">if</span> (!previous &amp;&amp; options.leading === <span class="literal">false</span>) &#123;</span><br><span class="line">      previous = now;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> remaining = wait - (now - previous);</span><br><span class="line">    context = <span class="keyword">this</span>;</span><br><span class="line">    args = <span class="built_in">arguments</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (remaining &lt;= <span class="number">0</span> || remaining &gt; wait) &#123;</span><br><span class="line">      <span class="keyword">if</span> (timeout) &#123;</span><br><span class="line">        clearTimeout(timeout);</span><br><span class="line">        timeout = <span class="literal">null</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      result = func.apply(context, args);</span><br><span class="line">      previous = now;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!timeout &amp;&amp; options.trailing !== <span class="literal">false</span>) &#123;</span><br><span class="line">      timeout = setTimeout(later, remaining);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</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">  throttled.cancel = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    clearTimeout(timeout);</span><br><span class="line">    previous = <span class="number">0</span>;</span><br><span class="line">    timeout = <span class="literal">null</span>;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> throttled;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意：就是 <code>leading：false</code> 和 <code>trailing: false</code> 不能同时设置。</p><h2 id="学习资料"><a href="#学习资料" class="headerlink" title="学习资料"></a>学习资料</h2><ul><li><a href="https://github.com/mqyqingfeng/Blog/issues/22" target="_blank" rel="noopener">JavaScript 专题之跟着 underscore 学防抖</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/26" target="_blank" rel="noopener">JavaScript 专题之跟着 underscore 学节流</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;性能优化中对于频繁触发事件，常用的两种解决方式：debounce 和 throttle。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="面试" scheme="https://www.yangtao.site/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>关于 Array 方法的用法及 Polyfill</title>
    <link href="https://www.yangtao.site/2020/04/01/handwriting-array/"/>
    <id>https://www.yangtao.site/2020/04/01/handwriting-array/</id>
    <published>2020-04-01T09:49:51.000Z</published>
    <updated>2020-04-01T14:30:41.941Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>主要有：isArray reduce flat forEach some every filter map 等 Array 方法。</p></blockquote><a id="more"></a><h2 id="map-filter-reduce-各自有什么作用"><a href="#map-filter-reduce-各自有什么作用" class="headerlink" title="map, filter, reduce 各自有什么作用"></a>map, filter, reduce 各自有什么作用</h2><p><strong>map</strong> 的作用是生成一个新数组，遍历原数组，将每个元素拿出来做一些变换然后放入到新的数组中。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].map(<span class="function"><span class="params">v</span> =&gt;</span> v + <span class="number">1</span>) <span class="comment">// -&gt; [2, 3, 4]</span></span><br></pre></td></tr></table></figure><p>另外 map 的回调函数接受三个参数，分别是<strong>当前索引元素，索引，原数组</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">'1'</span>,<span class="string">'2'</span>,<span class="string">'3'</span>].map(<span class="built_in">parseInt</span>)</span><br></pre></td></tr></table></figure><ul><li>第一轮遍历 <code>parseInt(&#39;1&#39;, 0) -&gt; 1</code></li><li>第二轮遍历 <code>parseInt(&#39;2&#39;, 1) -&gt; NaN</code></li><li>第三轮遍历 <code>parseInt(&#39;3&#39;, 2) -&gt; NaN</code></li></ul><p><strong>filter</strong> 的作用也是生成一个新数组，在遍历数组的时候将返回值为 true 的元素放入新数组，我们可以利用这个函数删除一些不需要的元素</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> array = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>]</span><br><span class="line"><span class="keyword">let</span> newArray = array.filter(<span class="function"><span class="params">item</span> =&gt;</span> item !== <span class="number">6</span>)</span><br><span class="line"><span class="built_in">console</span>.log(newArray) <span class="comment">// [1, 2, 4]</span></span><br></pre></td></tr></table></figure><p>和 map 一样，filter 的回调函数也接受三个参数，用处也相同。</p><p><strong>reduce</strong> 可以将数组中的元素通过回调函数最终转换为一个值。</p><p>对于 reduce 来说，它接受两个参数，分别是回调函数和初始值。</p><p><strong>回调函数</strong>接受四个参数，分别为<strong>累计值、当前元素、当前索引、原数组</strong>。</p><p>接下来我们就通过 reduce 来实现 map 函数：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="keyword">const</span> mapArray = arr.map(<span class="function"><span class="params">value</span> =&gt;</span> value * <span class="number">2</span>)</span><br><span class="line"><span class="keyword">const</span> reduceArray = arr.reduce(<span class="function">(<span class="params">acc, current</span>) =&gt;</span> &#123;</span><br><span class="line">  acc.push(current * <span class="number">2</span>)</span><br><span class="line">  <span class="keyword">return</span> acc</span><br><span class="line">&#125;, [])</span><br><span class="line"><span class="built_in">console</span>.log(mapArray, reduceArray) <span class="comment">// [2, 4, 6]</span></span><br></pre></td></tr></table></figure><h2 id="Array-isArray-实现"><a href="#Array-isArray-实现" class="headerlink" title="Array.isArray 实现"></a>Array.isArray 实现</h2><p>可以通过 <strong>toString()</strong> 来获取每个对象的类型。为了每个对象都能通过 <code>Object.prototype.toString()</code> 来检测，需要以 <code>Function.prototype.call()</code> 或者 <code>Function.prototype.apply()</code> 的形式来调用，传递要检查的对象作为第一个参数。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.myIsArray = <span class="function"><span class="keyword">function</span>(<span class="params">o</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.prototype.toString.call(o) === <span class="string">"[object Array]"</span>;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Array</span>.myIsArray([])); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><h2 id="Array-prototype-reduce-实现"><a href="#Array-prototype-reduce-实现" class="headerlink" title="Array.prototype.reduce 实现"></a>Array.prototype.reduce 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce" target="_blank" rel="noopener">reduce()</a> 方法对数组中的每个元素执行一个由您提供的<code>reducer</code>函数(升序执行)，将其结果汇总为单个返回值。由 reduce 返回的值将是最后一次回调返回值。语法：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])</span><br></pre></td></tr></table></figure><ul><li>callback<ul><li>accumulator 累计器</li><li>currentValue 当前值</li><li>currentIndex 当前索引，可选</li><li>array 数组，可选</li></ul></li><li>initialValue 可选，作为第一次调用 callback 函数时的第一个参数的值。 如果没有提供初始值，则将使用数组中的第一个元素。</li></ul><h3 id="栗子"><a href="#栗子" class="headerlink" title="栗子"></a>栗子</h3><p>求数组之和：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> array1 = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line"><span class="keyword">let</span> result = array1.reduce(<span class="function">(<span class="params">prev, curr</span>) =&gt;</span> prev + curr); <span class="comment">//10</span></span><br></pre></td></tr></table></figure><p>数组去重：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">let</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">4</span>, <span class="number">4</span>, <span class="number">4</span>];</span><br><span class="line"><span class="keyword">let</span> result = arr.sort().reduce(<span class="function">(<span class="params">init, current</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (init.length === <span class="number">0</span> || init[init.length - <span class="number">1</span>] !== current) &#123;</span><br><span class="line">    init.push(current);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> init;</span><br><span class="line">&#125;, []);</span><br><span class="line"><span class="built_in">console</span>.log(result); <span class="comment">//[1,2,3,4,5]</span></span><br></pre></td></tr></table></figure><p>按顺序运行 Promise：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">runPromiseInSequence</span>(<span class="params">arr, input</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> arr.reduce(</span><br><span class="line">    (promiseChain, currentFunction) =&gt; promiseChain.then(currentFunction),</span><br><span class="line">    <span class="built_in">Promise</span>.resolve(input)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// p1, p2, f3, p4 is promise function or normal function</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">p1</span>(<span class="params">a</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    resolve(a * <span class="number">5</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="keyword">const</span> promiseArr = [p1, p2, f3, p4];</span><br><span class="line">runPromiseInSequence(promiseArr, <span class="number">10</span>).then(<span class="built_in">console</span>.log);</span><br></pre></td></tr></table></figure><p>功能型函数管道：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="comment">// Building-blocks to use for composition</span></span><br><span class="line"><span class="keyword">const</span> double = <span class="function"><span class="params">x</span> =&gt;</span> x + x;</span><br><span class="line"><span class="keyword">const</span> triple = <span class="function"><span class="params">x</span> =&gt;</span> <span class="number">3</span> * x;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Function composition enabling pipe functionality</span></span><br><span class="line"><span class="keyword">const</span> pipe = <span class="function">(<span class="params">...functions</span>) =&gt;</span> input =&gt;</span><br><span class="line">  functions.reduce(<span class="function">(<span class="params">acc, fn</span>) =&gt;</span> fn(acc), input);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里其实就是</span></span><br><span class="line"><span class="keyword">const</span> pipe = <span class="function"><span class="keyword">function</span>(<span class="params">...functions</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">input</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> functions.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">acc, fn</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> fn(acc);</span><br><span class="line">    &#125;, input);</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">// Composed functions for multiplication of specific values</span></span><br><span class="line"><span class="keyword">const</span> multiply6 = pipe(double, triple);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Usage</span></span><br><span class="line">multiply6(<span class="number">6</span>); <span class="comment">// 36</span></span><br></pre></td></tr></table></figure><h3 id="模拟实现"><a href="#模拟实现" class="headerlink" title="模拟实现"></a>模拟实现</h3><p>第一版：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="built_in">Array</span>.prototype.myreduce = <span class="function"><span class="keyword">function</span>(<span class="params">callback, initialValue</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 判断第一次调用 callback 函数时的第一个参数的值。</span></span><br><span class="line">  <span class="comment">// 如果没有提供初始值，则将使用数组中的第一个元素。</span></span><br><span class="line">  <span class="keyword">const</span> _this = <span class="keyword">this</span>;</span><br><span class="line">  <span class="keyword">let</span> accumulator = initialValue ? initialValue : <span class="keyword">this</span>[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = initialValue ? <span class="number">0</span> : <span class="number">1</span>; i &lt; <span class="keyword">this</span>.length; i++) &#123;</span><br><span class="line">    accumulator = callback(accumulator, <span class="keyword">this</span>[i], i, _this);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> accumulator;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>第二版：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prototype.myreduce = <span class="function"><span class="keyword">function</span> <span class="title">reduce</span>(<span class="params">callbackfn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> O = <span class="keyword">this</span>; <span class="comment">// 保存原数组</span></span><br><span class="line">  <span class="keyword">const</span> len = O.length &gt;&gt;&gt; <span class="number">0</span>; <span class="comment">// typeof ('1' &gt;&gt;&gt; 0) -&gt; number</span></span><br><span class="line">  <span class="keyword">let</span> k = <span class="number">0</span>, <span class="comment">// 下标值初始化</span></span><br><span class="line">    accumulator = <span class="literal">undefined</span>, <span class="comment">// 累加器初始化</span></span><br><span class="line">    kPresent = <span class="literal">false</span>, <span class="comment">// k下标对应的值是否存在</span></span><br><span class="line">    initialValue = <span class="built_in">arguments</span>.length &gt; <span class="number">1</span> ? <span class="built_in">arguments</span>[<span class="number">1</span>] : <span class="literal">undefined</span>; <span class="comment">// 初始值</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> callbackfn !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    thrownewTypeError(callbackfn + <span class="string">" is not a function"</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="keyword">if</span> (len === <span class="number">0</span> &amp;&amp; <span class="built_in">arguments</span>.length &lt; <span class="number">2</span>) &#123;</span><br><span class="line">    thrownewTypeError(<span class="string">"Reduce of empty array with no initial value"</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="keyword">if</span> (<span class="built_in">arguments</span>.length &gt; <span class="number">1</span>) &#123;</span><br><span class="line">    accumulator = initialValue;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    accumulator = O[k++];</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> (k &lt; len) &#123;</span><br><span class="line">    <span class="comment">// 判断是否为 empty [,,,]</span></span><br><span class="line">    kPresent = O.hasOwnProperty(k);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (kPresent) &#123;</span><br><span class="line">      <span class="keyword">const</span> kValue = O[k]; <span class="comment">// 当前值</span></span><br><span class="line">      <span class="comment">// 调用 callbackfn</span></span><br><span class="line">      accumulator = callbackfn(accumulator, kValue, k, O);</span><br><span class="line">    &#125;</span><br><span class="line">    k++;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> accumulator;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="Array-prototype-flat-实现"><a href="#Array-prototype-flat-实现" class="headerlink" title="Array.prototype.flat 实现"></a>Array.prototype.flat 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat" target="_blank" rel="noopener">flat()</a> 方法会按照一个可指定的深度递归遍历数组，并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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> newArray = arr.flat([depth]);</span><br><span class="line"><span class="comment">// depth 可选</span></span><br><span class="line"><span class="comment">// 指定要提取嵌套数组的结构深度，默认值为 1</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></pre></td></tr></table></figure><p>例子：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">var</span> arr = [<span class="number">1</span>, <span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>]];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 展开一层数组</span></span><br><span class="line">arr.flat();</span><br><span class="line"><span class="comment">// 等效于</span></span><br><span class="line">arr.reduce(<span class="function">(<span class="params">acc, val</span>) =&gt;</span> acc.concat(val), []);</span><br><span class="line"><span class="comment">// [1, 2, 3, 4]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用扩展运算符 ...</span></span><br><span class="line"><span class="keyword">const</span> flattened = <span class="function"><span class="params">arr</span> =&gt;</span> [].concat(...arr);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 展开两层数组</span></span><br><span class="line">arr.flat(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 Infinity，可展开任意深度的嵌套数组</span></span><br><span class="line">arr.flat(<span class="literal">Infinity</span>);</span><br></pre></td></tr></table></figure><p>使用 <strong>reduce</strong>、<strong>concat</strong> 和<strong>递归</strong>展开无限多层嵌套的数组：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">flatDeep</span>(<span class="params">arr, d = <span class="number">1</span></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> d &gt; <span class="number">0</span></span><br><span class="line">    ? arr.reduce(</span><br><span class="line">        (acc, val) =&gt;</span><br><span class="line">          acc.concat(<span class="built_in">Array</span>.isArray(val) ? flatDeep(val, d - <span class="number">1</span>) : val),</span><br><span class="line">        []</span><br><span class="line">      )</span><br><span class="line">    : arr.slice();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>forEach</strong>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// forEach 遍历数组会自动跳过空元素</span></span><br><span class="line"><span class="keyword">const</span> eachFlat = <span class="function">(<span class="params">arr = [], depth = <span class="number">1</span></span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> result = []; <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">flat</span>(<span class="params">arr, depth</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// forEach 会自动去除数组空位</span></span><br><span class="line">    arr.forEach(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// 控制递归深度</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(item) &amp;&amp; depth &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 递归数组</span></span><br><span class="line">        flat(item, depth - <span class="number">1</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 缓存元素</span></span><br><span class="line">        result.push(item);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;)(arr, depth);</span><br><span class="line">  <span class="comment">// 返回递归结果</span></span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">eachFlat([<span class="number">1</span>, <span class="number">2</span>, [<span class="number">3</span>, [<span class="number">4</span>, [<span class="number">5</span>]]]], <span class="literal">Infinity</span>); <span class="comment">//[1, 2, 3, 4, 5]</span></span><br></pre></td></tr></table></figure><p><strong>for…of</strong>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// for of 循环不能去除数组空位，需要手动去除</span></span><br><span class="line"><span class="keyword">const</span> forFlat = <span class="function">(<span class="params">arr = [], depth = <span class="number">1</span></span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> result = [];</span><br><span class="line">  (<span class="function"><span class="keyword">function</span> <span class="title">flat</span>(<span class="params">arr, depth</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> arr) &#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(item) &amp;&amp; depth &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        flat(item, depth - <span class="number">1</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 去除空元素，添加非undefined元素</span></span><br><span class="line">        item !== <span class="keyword">void</span> <span class="number">0</span> &amp;&amp; result.push(item);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)(arr, depth);</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>其他方法见<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat" target="_blank" rel="noopener">MDN</a>。</p><h2 id="Array-prototype-forEach-实现"><a href="#Array-prototype-forEach-实现" class="headerlink" title="Array.prototype.forEach 实现"></a>Array.prototype.forEach 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach" target="_blank" rel="noopener">forEach()</a> 方法对数组的每个元素执行一次给定的函数。语法：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arr.forEach(callback(currentValue [, index [, array]])[, thisArg])</span><br></pre></td></tr></table></figure><p><code>forEach()</code> 为每个数组元素执行一次 callback 函数；与 <code>map()</code> 或者 <code>reduce()</code> 不同的是，它总是返回 undefined 值，并且不可链式调用。</p><p>例子：对象复制器函数</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">copy</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> copy = <span class="built_in">Object</span>.create(<span class="built_in">Object</span>.getPrototypeOf(obj));</span><br><span class="line">  <span class="comment">// propNames is ["a", "b"]</span></span><br><span class="line">  <span class="keyword">const</span> propNames = <span class="built_in">Object</span>.getOwnPropertyNames(obj);</span><br><span class="line"></span><br><span class="line">  propNames.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// desc is &#123;value: 1, writable: true, enumerable: true, configurable: true&#125;</span></span><br><span class="line">    <span class="keyword">const</span> desc = <span class="built_in">Object</span>.getOwnPropertyDescriptor(obj, name);</span><br><span class="line">    <span class="built_in">Object</span>.defineProperty(copy, name, desc);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> copy;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> obj1 = &#123; <span class="attr">a</span>: <span class="number">1</span>, <span class="attr">b</span>: <span class="number">2</span> &#125;;</span><br><span class="line"><span class="keyword">const</span> obj2 = copy(obj1); <span class="comment">// 现在 obj2 看起来和 obj1 一模一样了</span></span><br></pre></td></tr></table></figure><p>模拟实现：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="built_in">Array</span>.prototype.myforEach = <span class="function"><span class="keyword">function</span>(<span class="params">callback, thisArg</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> k, T;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">this</span> == <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">" this is null or not defined"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">var</span> O = <span class="built_in">Object</span>(<span class="keyword">this</span>);</span><br><span class="line">  <span class="keyword">var</span> len = O.length &gt;&gt;&gt; <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> callback !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(callback + <span class="string">" is not a function"</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">arguments</span>.length &gt; <span class="number">1</span>) &#123;</span><br><span class="line">    T = thisArg;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  k = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> (k &lt; len) &#123;</span><br><span class="line">    <span class="keyword">var</span> kValue;</span><br><span class="line">    <span class="comment">// 已删除或者未初始化的项将被跳过</span></span><br><span class="line">    <span class="keyword">if</span> (k <span class="keyword">in</span> O) &#123;</span><br><span class="line">      kValue = O[k];</span><br><span class="line">      callback.call(T, kValue, k, O);</span><br><span class="line">    &#125;</span><br><span class="line">    k++;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="Array-prototype-every-实现"><a href="#Array-prototype-every-实现" class="headerlink" title="Array.prototype.every 实现"></a>Array.prototype.every 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/every" target="_blank" rel="noopener">every()</a> 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">if</span> (!<span class="built_in">Array</span>.prototype.every) &#123;</span><br><span class="line">  <span class="built_in">Array</span>.prototype.every = <span class="function"><span class="keyword">function</span>(<span class="params">callbackfn, thisArg</span>) </span>&#123;</span><br><span class="line"><span class="meta">    "use strict"</span>;</span><br><span class="line">    <span class="keyword">var</span> T, k;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span> == <span class="literal">null</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"this is null or not defined"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> O = <span class="built_in">Object</span>(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">var</span> len = O.length &gt;&gt;&gt; <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> callbackfn !== <span class="string">"function"</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">arguments</span>.length &gt; <span class="number">1</span>) &#123;</span><br><span class="line">      T = thisArg;</span><br><span class="line">    &#125;</span><br><span class="line">    k = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (k &lt; len) &#123;</span><br><span class="line">      <span class="keyword">var</span> kValue;</span><br><span class="line">      <span class="keyword">if</span> (k <span class="keyword">in</span> O) &#123;</span><br><span class="line">        kValue = O[k];</span><br><span class="line">        <span class="keyword">var</span> testResult = callbackfn.call(T, kValue, k, O);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!testResult) &#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">      &#125;</span><br><span class="line">      k++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Array-prototype-some-实现"><a href="#Array-prototype-some-实现" class="headerlink" title="Array.prototype.some 实现"></a>Array.prototype.some 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some" target="_blank" rel="noopener">some()</a> 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!<span class="built_in">Array</span>.prototype.some) &#123;</span><br><span class="line">  <span class="built_in">Array</span>.prototype.some = <span class="function"><span class="keyword">function</span>(<span class="params">fun <span class="regexp">/*, thisArg*/</span></span>) </span>&#123;</span><br><span class="line"><span class="meta">    "use strict"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span> == <span class="literal">null</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"Array.prototype.some called on null or undefined"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> fun !== <span class="string">"function"</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> t = <span class="built_in">Object</span>(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">var</span> len = t.length &gt;&gt;&gt; <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> thisArg = <span class="built_in">arguments</span>.length &gt;= <span class="number">2</span> ? <span class="built_in">arguments</span>[<span class="number">1</span>] : <span class="keyword">void</span> <span class="number">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; len; i++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (i <span class="keyword">in</span> t &amp;&amp; fun.call(thisArg, t[i], i, t)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></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">&#125;</span><br></pre></td></tr></table></figure><h2 id="Array-prototype-map-实现"><a href="#Array-prototype-map-实现" class="headerlink" title="Array.prototype.map 实现"></a>Array.prototype.map 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map" target="_blank" rel="noopener">map()</a> 方法创建一个新数组，其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。</p><p>map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值（包括 undefined）组合起来形成一个新数组。</p><p>用法：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">"1"</span>, <span class="string">"2"</span>, <span class="string">"3"</span>].map(<span class="built_in">parseInt</span>); <span class="comment">// [1, NaN, NaN]</span></span><br><span class="line"><span class="comment">// parseInt(string, radix) -&gt; map(parseInt(value, index))</span></span><br><span class="line"></span><br><span class="line">[<span class="string">"1"</span>, <span class="string">"2"</span>, <span class="string">"3"</span>].map(<span class="function"><span class="params">str</span> =&gt;</span> <span class="built_in">parseInt</span>(str)); <span class="comment">// [1, 2, 3]</span></span><br></pre></td></tr></table></figure><p>Plyfill:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!<span class="built_in">Array</span>.prototype.map) &#123;</span><br><span class="line">  <span class="built_in">Array</span>.prototype.map = <span class="function"><span class="keyword">function</span>(<span class="params">callback <span class="regexp">/*, thisArg*/</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> T, A, k;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span> == <span class="literal">null</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"this is null or not defined"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> O = <span class="built_in">Object</span>(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">var</span> len = O.length &gt;&gt;&gt; <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> callback !== <span class="string">"function"</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(callback + <span class="string">" is not a function"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">arguments</span>.length &gt; <span class="number">1</span>) &#123;</span><br><span class="line">      T = <span class="built_in">arguments</span>[<span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    A = <span class="keyword">new</span> <span class="built_in">Array</span>(len);</span><br><span class="line">    k = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (k &lt; len) &#123;</span><br><span class="line">      <span class="keyword">var</span> kValue, mappedValue;</span><br><span class="line">      <span class="keyword">if</span> (k <span class="keyword">in</span> O) &#123;</span><br><span class="line">        kValue = O[k];</span><br><span class="line">        mappedValue = callback.call(T, kValue, k, O);</span><br><span class="line">        A[k] = mappedValue;</span><br><span class="line">      &#125;</span><br><span class="line">      k++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> A;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Array-prototype-filter-实现"><a href="#Array-prototype-filter-实现" class="headerlink" title="Array.prototype.filter 实现"></a>Array.prototype.filter 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter" target="_blank" rel="noopener">filter()</a> 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。</p><p>filter 为数组中的每个元素调用一次 callback 函数，并利用所有使得 callback 返回 true 或等价于 true 的值的元素创建一个新数组。</p><p>用法：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fruits = [<span class="string">"apple"</span>, <span class="string">"banana"</span>, <span class="string">"grapes"</span>, <span class="string">"mango"</span>, <span class="string">"orange"</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> filterItems = <span class="function"><span class="params">query</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> fruits.filter(</span><br><span class="line">    el =&gt; el.toLowerCase().indexOf(query.toLowerCase()) &gt; <span class="number">-1</span></span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(filterItems(<span class="string">"ap"</span>)); <span class="comment">// ['apple', 'grapes']</span></span><br><span class="line"><span class="built_in">console</span>.log(filterItems(<span class="string">"an"</span>)); <span class="comment">// ['banana', 'mango', 'orange']</span></span><br></pre></td></tr></table></figure><p>Polyfill:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!<span class="built_in">Array</span>.prototype.filter) &#123;</span><br><span class="line">  <span class="built_in">Array</span>.prototype.filter = <span class="function"><span class="keyword">function</span>(<span class="params">func <span class="regexp">/*, thisArg*/</span></span>) </span>&#123;</span><br><span class="line"><span class="meta">    "use strict"</span>;</span><br><span class="line">    <span class="keyword">if</span> (!((<span class="keyword">typeof</span> func === <span class="string">"Function"</span> || <span class="keyword">typeof</span> func === <span class="string">"function"</span>) &amp;&amp; <span class="keyword">this</span>))</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> len = <span class="keyword">this</span>.length &gt;&gt;&gt; <span class="number">0</span>,</span><br><span class="line">      res = <span class="keyword">new</span> <span class="built_in">Array</span>(len), <span class="comment">// preallocate array</span></span><br><span class="line">      t = <span class="keyword">this</span>,</span><br><span class="line">      c = <span class="number">0</span>,</span><br><span class="line">      i = <span class="number">-1</span>,</span><br><span class="line">      T;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">arguments</span>.length &gt; <span class="number">1</span>) &#123;</span><br><span class="line">      T = <span class="built_in">arguments</span>[<span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (++i !== len) &#123;</span><br><span class="line">      <span class="keyword">if</span> (i <span class="keyword">in</span> <span class="keyword">this</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (func.call(T, t[i], i, t)) &#123;</span><br><span class="line">          res[c++] = t[i];</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">    res.length = c; <span class="comment">// shrink down array to proper size</span></span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Array-迭代方法"><a href="#Array-迭代方法" class="headerlink" title="Array 迭代方法"></a>Array 迭代方法</h2><ol><li><p><code>Array.prototype.forEach()</code><br>为数组中的每个元素执行一次回调函数。</p></li><li><p><code>Array.prototype.entries()</code><br>返回一个数组迭代器对象，该迭代器会包含所有数组元素的键值对。</p></li><li><p><code>Array.prototype.every()</code><br>如果数组中的每个元素都满足测试函数，则返回 true，否则返回 false。</p></li><li><p><code>Array.prototype.some()</code><br>如果数组中至少有一个元素满足测试函数，则返回 true，否则返回 false。</p></li><li><p><code>Array.prototype.filter()</code><br>将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。</p></li><li><p><code>Array.prototype.find()</code><br>找到第一个满足测试函数的元素并返回那个元素的值，如果找不到，则返回 undefined。</p></li><li><p><code>Array.prototype.findIndex()</code><br>找到第一个满足测试函数的元素并返回那个元素的索引，如果找不到，则返回 -1。</p></li><li><p><code>Array.prototype.keys()</code><br>返回一个数组迭代器对象，该迭代器会包含所有数组元素的键。</p></li><li><p><code>Array.prototype.map()</code><br>返回一个由回调函数的返回值组成的新数组。</p></li><li><p><code>Array.prototype.reduce()</code><br>从左到右为每个数组元素执行一次回调函数，并把上次回调函数的返回值放在一个暂存器中传给下次回调函数，并返回最后一次回调函数的返回值。</p></li><li><p><code>Array.prototype.reduceRight()</code><br>从右到左为每个数组元素执行一次回调函数，并把上次回调函数的返回值放在一个暂存器中传给下次回调函数，并返回最后一次回调函数的返回值。</p></li><li><p><code>Array.prototype.values()</code><br>返回一个数组迭代器对象，该迭代器会包含所有数组元素的值。</p></li><li><p><code>Array.prototype[@@iterator]()</code><br>和上面的 values() 方法是同一个函数。</p></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;主要有：isArray reduce flat forEach some every filter map 等 Array 方法。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
      <category term="手写系列" scheme="https://www.yangtao.site/tags/%E6%89%8B%E5%86%99%E7%B3%BB%E5%88%97/"/>
    
  </entry>
  
  <entry>
    <title>手写系列之 new、instanceof、class、typeAPI</title>
    <link href="https://www.yangtao.site/2020/03/30/handwriting-class/"/>
    <id>https://www.yangtao.site/2020/03/30/handwriting-class/</id>
    <published>2020-03-30T10:20:19.000Z</published>
    <updated>2020-04-01T11:42:00.302Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>主要有：new、Object.create()、Object.setPrototypeOf()、instanceof、class、type API、原型链继承等。<br><a id="more"></a></p></blockquote><h2 id="new-实现"><a href="#new-实现" class="headerlink" title="new 实现"></a>new 实现</h2><p>我们看下 new 做了什么：</p><ol><li>创建一个新对象；</li><li>将构造函数的作用域赋给新对象（因此 this 就指向了这个新对象）</li><li>执行构造函数中的代码（为这个新对象添加属性）</li><li>返回新对象。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">objectFactory</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 1. 新建一个对象 obj</span></span><br><span class="line">  <span class="keyword">const</span> obj = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 2. 取出第一个参数，就是我们要传入的构造函数 Constructor。</span></span><br><span class="line">  <span class="comment">// 此外因为 shift 会修改原数组，所以 arguments 会被去除第一个参数</span></span><br><span class="line">  <span class="keyword">const</span> Constructor = [].shift.call(<span class="built_in">arguments</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 3. 将 obj 的原型指向构造函数，这样 obj 就可以访问到构造函数原型中的属性</span></span><br><span class="line">  obj.__proto__ = Constructor.prototype;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 4. 使用 apply，改变构造函数 this 的指向到新建的对象，这样 obj 就可以访问到构造函数中的属性</span></span><br><span class="line">  <span class="comment">// 如果构造函数返回值是对象则返回这个对象，如果不是对象则返回新的实例对象</span></span><br><span class="line">  <span class="keyword">const</span> ret = Constructor.apply(obj, <span class="built_in">arguments</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 5. 返回 obj</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> ret === <span class="string">"object"</span> ? ret : obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ES6：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createNew</span>(<span class="params">Con, ...args</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.obj = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">this</span>.obj = <span class="built_in">Object</span>.create(Con.prototype);</span><br><span class="line">  <span class="comment">// Object.setPrototypeOf(this.obj, Con.prototype);</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> ret = Con.apply(<span class="keyword">this</span>.obj, args);</span><br><span class="line">  <span class="keyword">return</span> ret <span class="keyword">instanceof</span> <span class="built_in">Object</span> ? ret : <span class="keyword">this</span>.obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Object-create-实现"><a href="#Object-create-实现" class="headerlink" title="Object.create 实现"></a>Object.create 实现</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create" target="_blank" rel="noopener">Object.create()</a>方法创建一个新对象，使用现有的对象来提供新创建的对象的<code>__proto__</code>。</p><h3 id="使用-Object-create"><a href="#使用-Object-create" class="headerlink" title="使用 Object.create"></a>使用 Object.create</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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> o;</span><br><span class="line"><span class="comment">// 创建原型为 null 的空对象</span></span><br><span class="line">o = <span class="built_in">Object</span>.create(<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">o = &#123;&#125;;</span><br><span class="line"><span class="comment">// 以字面量方式创建的空对象就相当于：</span></span><br><span class="line">o = <span class="built_in">Object</span>.create(<span class="built_in">Object</span>.prototype);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Constructor</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">o = <span class="keyword">new</span> Constructor();</span><br><span class="line"><span class="comment">// 上面的一句就相当于:</span></span><br><span class="line">o = <span class="built_in">Object</span>.create(Constructor.prototype);</span><br><span class="line"><span class="comment">// 相当于</span></span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf(o, Constructor.prototype);</span><br><span class="line"><span class="comment">// 再相当于</span></span><br><span class="line">o.__proto__ === Constructor.prototype);</span><br><span class="line"><span class="comment">// 哇哦，完美</span></span><br><span class="line"></span><br><span class="line">o = <span class="built_in">Object</span>.create(<span class="built_in">Object</span>.prototype, &#123;</span><br><span class="line">  <span class="comment">// foo会成为所创建对象的数据属性</span></span><br><span class="line">  foo: &#123;</span><br><span class="line">    writable: <span class="literal">true</span>,</span><br><span class="line">    configurable: <span class="literal">true</span>,</span><br><span class="line">    value: <span class="string">"hello"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// bar会成为所创建对象的访问器属性</span></span><br><span class="line">  bar: &#123;</span><br><span class="line">    configurable: <span class="literal">false</span>,</span><br><span class="line">    get: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">10</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    set: <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">"Setting `o.bar` to"</span>, value);</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="模拟-Object-create-实现原理"><a href="#模拟-Object-create-实现原理" class="headerlink" title="模拟 Object.create 实现原理"></a>模拟 Object.create 实现原理</h3><p>采用了<strong>原型式继承</strong>：将传入的对象作为创建的对象的原型。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.mycreate = <span class="function"><span class="keyword">function</span>(<span class="params">proto</span>) </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">F</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">  F.prototype = proto;</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>详细 Polyfill，见 MDN：<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create" target="_blank" rel="noopener">Object.create()</a>，其实就多了参数的判断等信息。</p><h2 id="Object-setPrototypeOf-实现"><a href="#Object-setPrototypeOf-实现" class="headerlink" title="Object.setPrototypeOf 实现"></a>Object.setPrototypeOf 实现</h2><p><strong>Object.setPrototypeOf()</strong> 方法设置一个指定的对象的原型 ( 即, 内部<code>[[Prototype]]</code>属性）到另一个对象或 <code>null</code>。</p><p><strong>注意</strong>：由于性能问题，你应该使用 <strong>Object.create()</strong> 来创建带有你想要的<code>[[Prototype]]</code>的新对象。详情见：<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf" target="_blank" rel="noopener">MDN</a>。</p><p>使用较旧的 <code>Object.prototype.__proto__</code> 属性，我们可以很容易地定义 <code>setPrototypeOf</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 仅适用于Chrome和FireFox，在IE中不工作：</span></span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf =</span><br><span class="line">  <span class="built_in">Object</span>.setPrototypeOf ||</span><br><span class="line">  <span class="function"><span class="keyword">function</span>(<span class="params">obj, proto</span>) </span>&#123;</span><br><span class="line">    obj.__proto__ = proto;</span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure><h2 id="instanceof-实现"><a href="#instanceof-实现" class="headerlink" title="instanceof 实现"></a>instanceof 实现</h2><p><strong>用法</strong>：<code>instanceof</code> 运算符用于检测构造函数的 <code>prototype</code> 属性是否出现在某个实例对象的原型链上。</p><p><strong>原理</strong>：其实就是沿着原型链一直询问，直到<code>__proto__</code>为 <code>null</code>为止。</p><p><strong>注意</strong>：<code>Object.prototype.isPrototypeOf()</code>用于测试一个对象是否存在于另一个对象的原型链上。<code>isPrototypeOf()</code> 与 <code>instanceof</code> 运算符不同。在表达式 “<code>object instanceof AFunction</code>“中，object 的原型链是针对 <code>AFunction.prototype</code> 进行检查的，而不是针对 <code>AFunction</code> 本身。</p><p>如果你有段代码只在需要操作继承自一个特定的原型链的对象的情况下执行，同 <code>instanceof</code> 操作符一样 <code>isPrototypeOf()</code> 方法就会派上用场:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Car</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line"><span class="keyword">var</span> mycar = <span class="keyword">new</span> Car();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意下面 instanceof 和 isPrototypeOf() 之间的区别：</span></span><br><span class="line"><span class="comment">// instanceof 中的 mycar 原型链是针对 Car.prototype，而不是 Car 本身</span></span><br><span class="line"><span class="keyword">var</span> a = mycar <span class="keyword">instanceof</span> Car; <span class="comment">// 返回 true</span></span><br><span class="line"><span class="keyword">var</span> b = mycar <span class="keyword">instanceof</span> <span class="built_in">Object</span>; <span class="comment">// 返回 true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// isPrototypeOf() 也是 Car.prototype，不过是显式的，注意这点小小区别</span></span><br><span class="line"><span class="keyword">var</span> aa = Car.prototype.isPrototypeOf(mycar); <span class="comment">// true</span></span><br><span class="line"><span class="keyword">var</span> bb = <span class="built_in">Object</span>.prototype.isPrototypeOf(mycar); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>要检测对象不是某个构造函数的实例时，你可以这样做:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">if</span> (!(mycar <span class="keyword">instanceof</span> Car)) &#123;</span><br><span class="line">  <span class="comment">// Do something</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (!Car.prototype.isPrototypeOf(mycar)) &#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><strong>instanceof 模拟实现</strong>：主要是沿着<code>__proto__</code>判断：<code>L.__proto__</code>是否等于<code>R.prototype</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">myinstanceof</span>(<span class="params">L, R</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> O = R.prototype;</span><br><span class="line">  L = L.__proto__;</span><br><span class="line">  <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (L === <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">if</span> (O === L) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    L = L.__proto__;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = myinstanceof(mycar, Car); <span class="comment">// 返回 true</span></span><br><span class="line"><span class="keyword">var</span> b = myinstanceof(mycar, <span class="built_in">Object</span>); <span class="comment">// 返回 true</span></span><br></pre></td></tr></table></figure><h2 id="原型链继承实现"><a href="#原型链继承实现" class="headerlink" title="原型链继承实现"></a>原型链继承实现</h2><h3 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></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">name</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Parent.prototype.getName = <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="keyword">this</span>.name;</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">name, age</span>) </span>&#123;</span><br><span class="line">  Parent.call(<span class="keyword">this</span>, name);</span><br><span class="line">  <span class="keyword">this</span>.age = age;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Child.prototype = <span class="built_in">Object</span>.create(Parent.prototype);</span><br><span class="line">Child.prototype.constructor = Child;</span><br><span class="line"></span><br><span class="line">Child.prototype.getMsg = <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="string">`My name is <span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span>, <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span> years old.`</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>ES6:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(name) &#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">  &#125;</span><br><span class="line">  getName() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Child</span> <span class="keyword">extends</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(name, age) &#123;</span><br><span class="line">    <span class="keyword">super</span>(name);</span><br><span class="line">    <span class="keyword">this</span>.age = age;</span><br><span class="line">  &#125;</span><br><span class="line">  getMsg() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`My name is <span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span>, <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span> years old.`</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>第一版：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">prototype</span>(<span class="params">child, parent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> F = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line">  F.prototype = parent.prototype;</span><br><span class="line">  child.prototype = <span class="keyword">new</span> F();</span><br><span class="line">  child.prototype.constructor = child;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第二版：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">create</span>(<span class="params">o</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> F = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line">  F.prototype = o;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> F();</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">prototype</span>(<span class="params">child, parent</span>) </span>&#123;</span><br><span class="line">  child.prototype = create(parent.prototype);</span><br><span class="line">  child.prototype.constructor = child;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第三版：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">prototype</span>(<span class="params">child, parent</span>) </span>&#123;</span><br><span class="line">  child.prototype = <span class="built_in">Object</span>.create(parent.prototype);</span><br><span class="line">  child.prototype.constructor = child;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="使用到的几种继承方式"><a href="#使用到的几种继承方式" class="headerlink" title="使用到的几种继承方式"></a>使用到的几种继承方式</h3><p><strong>组合式继承</strong>：融合原型链继承和构造函数的优点，是 JavaScript 中最常用的继承模式。组合继承最大的缺点是会调用两次父构造函数。</p><p><strong>原型式继承</strong>：<code>Object.create</code> 的模拟实现，将传入的对象作为创建的对象的原型。</p><p><strong>寄生组合式继承</strong>：只调用了一次 Parent 构造函数，并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。</p><p>PS：其他几种继承方式见这里<a href="https://github.com/mqyqingfeng/Blog/issues/16" target="_blank" rel="noopener">JavaScript 深入之继承的多种方式和优缺点</a>。</p><p>引用《JavaScript 高级程序设计》中对<strong>寄生组合式继承</strong>的夸赞就是：</p><p>这种方式的高效率体现它只调用了一次 <code>Parent</code> 构造函数，并且因此避免了在 <code>Parent.prototype</code> 上面创建不必要的、多余的属性。与此同时，原型链还能保持不变；因此，还能够正常使用 <code>instanceof</code> 和 <code>isPrototypeOf</code>。开发人员普遍认为寄生组合式继承是<strong>引用类型</strong>最理想的继承范式。</p><h2 id="class-实现"><a href="#class-实现" class="headerlink" title="class 实现"></a>class 实现</h2><p>主要是模拟使用<code>extends</code>，并模拟<code>super</code>可以给其父构造函数传值，如 Parent 中的 opt：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(opt) &#123;</span><br><span class="line">    <span class="keyword">this</span>.name = opt.name;</span><br><span class="line">  &#125;</span><br><span class="line">  getName() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Child</span> <span class="keyword">extends</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(opt) &#123;</span><br><span class="line">    <span class="keyword">super</span>(opt);</span><br><span class="line">    <span class="keyword">this</span>.age = opt.age;</span><br><span class="line">  &#125;</span><br><span class="line">  getAge() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.age + <span class="string">" years old."</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="keyword">const</span> me = <span class="keyword">new</span> Child(&#123; <span class="attr">name</span>: <span class="string">"Yang"</span>, <span class="attr">age</span>: <span class="number">28</span> &#125;);</span><br></pre></td></tr></table></figure><p>开始模拟实现：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="function"><span class="keyword">function</span> <span class="title">_extends</span>(<span class="params">child, parent</span>) </span>&#123;</span><br><span class="line">  child.prototype = <span class="built_in">Object</span>.create(parent &amp;&amp; parent.prototype);</span><br><span class="line">  child.prototype.constructor = child;</span><br><span class="line">  <span class="built_in">Object</span>.setPrototypeOf</span><br><span class="line">    ? <span class="built_in">Object</span>.setPrototypeOf(child, parent)</span><br><span class="line">    : (child.__proto__ = parent);</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">_classCallCheck</span>(<span class="params">instance, Constructor</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (!(instance <span class="keyword">instanceof</span> Constructor)) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">"Cannot call a class as a function"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Parent = (<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">Parent</span>(<span class="params">opt</span>) </span>&#123;</span><br><span class="line">    _classCallCheck(<span class="keyword">this</span>, Parent);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">this</span>.name = opt.name;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  Parent.prototype.getName = <span class="function"><span class="keyword">function</span> <span class="title">getName</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> Parent;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Child = (<span class="function"><span class="keyword">function</span>(<span class="params">_Parent</span>) </span>&#123;</span><br><span class="line">  _extends(Child, _Parent);</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">opt</span>) </span>&#123;</span><br><span class="line">    _classCallCheck(<span class="keyword">this</span>, Child);</span><br><span class="line">    <span class="comment">// Constrctor =&gt; _Parent.call(this, opt)</span></span><br><span class="line">    <span class="keyword">var</span> _this = (_Parent != <span class="literal">null</span> &amp;&amp; _Parent.call(<span class="keyword">this</span>, opt)) || <span class="keyword">this</span>;</span><br><span class="line">    _this.age = opt.age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> _this;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  Child.prototype.getAge = <span class="function"><span class="keyword">function</span> <span class="title">getAge</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.age + <span class="string">" years old."</span>;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> Child;</span><br><span class="line">&#125;)(Parent);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> myself = <span class="keyword">new</span> Child(&#123; <span class="attr">name</span>: <span class="string">"YyY"</span>, <span class="attr">age</span>: <span class="number">18</span> &#125;);</span><br></pre></td></tr></table></figure><p>附加两篇文章：</p><ul><li><a href="https://github.com/mqyqingfeng/Blog/issues/105" target="_blank" rel="noopener">ES6 系列之 Babel 是如何编译 Class 的(上)</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/106" target="_blank" rel="noopener">ES6 系列之 Babel 是如何编译 Class 的(下)</a></li></ul><h2 id="Array-isArray-实现"><a href="#Array-isArray-实现" class="headerlink" title="Array.isArray 实现"></a>Array.isArray 实现</h2><p>可以通过 <strong>toString()</strong> 来获取每个对象的类型。为了每个对象都能通过 <code>Object.prototype.toString()</code> 来检测，需要以 <code>Function.prototype.call()</code> 或者 <code>Function.prototype.apply()</code> 的形式来调用，传递要检查的对象作为第一个参数。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.myIsArray = <span class="function"><span class="keyword">function</span>(<span class="params">o</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.prototype.toString.call(o) === <span class="string">"[object Array]"</span>;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Array</span>.myIsArray([])); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><h2 id="type-API-实现"><a href="#type-API-实现" class="headerlink" title="type API 实现"></a>type API 实现</h2><p>写一个 type 函数能检测各种类型的值，如果是基本类型，就使用 <code>typeof</code>，引用类型就使用 <code>toString</code>。</p><p>此外鉴于 <code>typeof</code> 的结果是小写，我也希望所有的结果都是小写。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> class2type = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="string">"Boolean Number String Function Array Date RegExp Object Error Null Undefined Symbol Set Map BigInt"</span></span><br><span class="line">  .split(<span class="string">" "</span>)</span><br><span class="line">  .map(<span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 格式如："[object Array]": "array"</span></span><br><span class="line">    class2type[<span class="string">"[object "</span> + item + <span class="string">"]"</span>] = item.toLowerCase();</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">type</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (obj == <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> obj + <span class="string">""</span>; <span class="comment">// IE6</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> obj === <span class="string">"object"</span> || <span class="keyword">typeof</span> obj === <span class="string">"function"</span></span><br><span class="line">    ? class2type[<span class="built_in">Object</span>.prototype.toString.call(obj)]</span><br><span class="line">    : <span class="keyword">typeof</span> obj;</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">isFunction</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> type(obj) === <span class="string">"function"</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> isArray =</span><br><span class="line">  <span class="built_in">Array</span>.isArray ||</span><br><span class="line">  <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> type(obj) === <span class="string">"array"</span>;</span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure><p>参考资料：<a href="https://github.com/mqyqingfeng/Blog/issues/28" target="_blank" rel="noopener">JavaScript 专题之类型判断(上)</a></p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://mp.weixin.qq.com/s?__biz=Mzg5ODA5NTM1Mw==&amp;mid=2247485202&amp;idx=1&amp;sn=1a668530cdce1eaf53c2ec6d796fdd7b&amp;chksm=c0668684f7110f922045c7a4539f78c51458ccafd5204ba7d89ad461ed360a1c14ed07778068&amp;mpshare=1&amp;scene=23&amp;srcid=0329UG1H5LSPIxcKsQwUWXlo&amp;sharer_sharetime=1585447184050&amp;sharer_shareid=73865875704bcba3caa8b09c62f6bd7a%23rd" target="_blank" rel="noopener">各种手写源码实现</a></li><li><a href="https://maimai.cn/article/detail?fid=1414317645&amp;efid=GX16EiGB-SbwDA5N9-zXBQ&amp;use_rn=1&amp;from=timeline&amp;isappinstalled=0" target="_blank" rel="noopener">JavaScript 中各种源码实现（前端面试笔试必备）</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/13" target="_blank" rel="noopener">JavaScript 深入之 new 的模拟实现</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/14" target="_blank" rel="noopener">JavaScript 深入之类数组对象与 arguments</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;主要有：new、Object.create()、Object.setPrototypeOf()、instanceof、class、type API、原型链继承等。&lt;br&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
      <category term="手写系列" scheme="https://www.yangtao.site/tags/%E6%89%8B%E5%86%99%E7%B3%BB%E5%88%97/"/>
    
  </entry>
  
  <entry>
    <title>手写系列之 bind、call、apply</title>
    <link href="https://www.yangtao.site/2020/03/29/handwriting-call-apply-bind/"/>
    <id>https://www.yangtao.site/2020/03/29/handwriting-call-apply-bind/</id>
    <published>2020-03-29T07:50:44.000Z</published>
    <updated>2020-03-29T07:59:31.611Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>bind() 方法会创建一个新函数。当这个新函数被调用时，bind() 的第一个参数将作为它运行时的 this，之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )</p></blockquote><a id="more"></a><h2 id="bind-实现"><a href="#bind-实现" class="headerlink" title="bind 实现"></a>bind 实现</h2><p>我们可以首先得出 bind 函数的两个特点：</p><ul><li>返回一个函数</li><li>可以传入参数</li></ul><p>如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> foo = &#123;</span><br><span class="line">  value: <span class="number">1</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">bar</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="keyword">this</span>.value);</span><br><span class="line">  <span class="built_in">console</span>.log(name);</span><br><span class="line">  <span class="built_in">console</span>.log(age);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bindFoo = bar.bind(foo, <span class="string">"yyy"</span>);</span><br><span class="line">bindFoo(<span class="string">"28"</span>);</span><br></pre></td></tr></table></figure><p><strong>第一步</strong>：返回一个函数</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.mybind1 = <span class="function"><span class="keyword">function</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> self = <span class="keyword">this</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="keyword">return</span> self.apply(context);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>第二步</strong>：可以传参，并且可以这样：函数需要传 name 和 age 两个参数，可以在 bind 的时候，只传一个 name，在执行返回的函数的时候，再传另一个参数 age!</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.mybind2 = <span class="function"><span class="keyword">function</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> self = <span class="keyword">this</span>;</span><br><span class="line">  <span class="comment">// 截取参数，如：</span></span><br><span class="line">  <span class="comment">// context = [].shift.call(arguments)</span></span><br><span class="line">  <span class="comment">// args = [].slice.call(arguments)</span></span><br><span class="line">  <span class="keyword">const</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>, <span class="number">1</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="keyword">const</span> bindArgs = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</span><br><span class="line">    <span class="keyword">return</span> self.apply(context, args.concat(bindArgs));</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>第三步</strong>：当 bind 返回的函数作为构造函数的时候，bind 时指定的 this 值会失效，但传入的参数依然生效。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.mybind3 = <span class="function"><span class="keyword">function</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> self = <span class="keyword">this</span>;</span><br><span class="line">  <span class="keyword">const</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 通过一个空函数来进行中转</span></span><br><span class="line">  <span class="keyword">const</span> fNOP = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> fBound = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> bindArgs = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</span><br><span class="line">    <span class="comment">// this 指向 实例，说明是构造函数，需要将绑定函数的 this 指向该实例，</span></span><br><span class="line">    <span class="comment">// 可以让实例获得来自绑定函数的值</span></span><br><span class="line">    <span class="comment">// this 指向 window，说明使普通函数调用，将绑定函数的 this 指向 context</span></span><br><span class="line">    <span class="keyword">return</span> self.apply(</span><br><span class="line">      <span class="keyword">this</span> <span class="keyword">instanceof</span> fNOP ? <span class="keyword">this</span> : context,</span><br><span class="line">      args.concat(bindArgs)</span><br><span class="line">    );</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  fNOP.prototype = <span class="keyword">this</span>.prototype;</span><br><span class="line">  <span class="comment">// 让实例继承绑定函数的原型(this.prototype)中的值</span></span><br><span class="line">  fBound.prototype = <span class="keyword">new</span> fNOP();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> fBound;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>最终版</strong>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="built_in">Function</span>.prototype.mybind = <span class="function"><span class="keyword">function</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span> !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(</span><br><span class="line">      <span class="string">"Function.prototype.bind - what is trying to be bound is not callable"</span></span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> self = <span class="keyword">this</span>;</span><br><span class="line">  <span class="keyword">const</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>);</span><br><span class="line">  <span class="keyword">const</span> fNOP = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> fBound = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> bindArgs = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</span><br><span class="line">    <span class="keyword">return</span> self.apply(</span><br><span class="line">      <span class="keyword">this</span> <span class="keyword">instanceof</span> fNOP ? <span class="keyword">this</span> : context,</span><br><span class="line">      args.concat(bindArgs)</span><br><span class="line">    );</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  fNOP.prototype = <span class="keyword">this</span>.prototype;</span><br><span class="line">  fBound.prototype = <span class="keyword">new</span> fNOP();</span><br><span class="line">  <span class="keyword">return</span> fBound;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="call-实现"><a href="#call-实现" class="headerlink" title="call 实现"></a>call 实现</h2><p>我们模拟的步骤可以分为：</p><ol><li>将函数设为对象的属性</li><li>执行该函数</li><li>删除该函数</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.myCall = <span class="function"><span class="keyword">function</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span> !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>&#125;</span> is not a function.`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> context != <span class="string">"object"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"Arguments error"</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> args = [];</span><br><span class="line">  <span class="keyword">var</span> result;</span><br><span class="line">  context = context || <span class="built_in">window</span>;</span><br><span class="line">  <span class="comment">// 如果本身存在 fn 属性，先保存，使用完毕，后恢复</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="string">"fn"</span> <span class="keyword">in</span> context &amp;&amp; context.hasOwnProperty(<span class="string">"fn"</span>)) &#123;</span><br><span class="line">    <span class="keyword">var</span> fn = context.fn;</span><br><span class="line">    <span class="keyword">var</span> fnFlag = <span class="literal">true</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 1. 将函数设为对象的属性</span></span><br><span class="line">  context.fn = <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>, l = <span class="built_in">arguments</span>.length; i &lt; l; i++) &#123;</span><br><span class="line">    <span class="comment">// args = ["arguments[1]", "arguments[2]"]</span></span><br><span class="line">    args.push(<span class="string">"arguments["</span> + i + <span class="string">"]"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 2. 执行该函数</span></span><br><span class="line">  result = <span class="built_in">eval</span>(<span class="string">"context.fn("</span> + args + <span class="string">")"</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (fnFlag) &#123;</span><br><span class="line">    <span class="comment">// 恢复</span></span><br><span class="line">    context.fn = fn;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 3. 删除该函数</span></span><br><span class="line">    <span class="keyword">delete</span> context.fn;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>ES6:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.myCall = <span class="function"><span class="keyword">function</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span> !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>&#125;</span> is not a function.`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> args = [...arguments].slice(<span class="number">1</span>);</span><br><span class="line">  context = context || <span class="built_in">window</span>;</span><br><span class="line">  <span class="comment">// 1. 将函数设为对象的属性</span></span><br><span class="line">  context.fn = <span class="keyword">this</span>;</span><br><span class="line">  <span class="comment">// 2. 执行该函数</span></span><br><span class="line">  <span class="keyword">const</span> result = context.fn(...args);</span><br><span class="line">  <span class="comment">// 3. 删除该函数</span></span><br><span class="line">  <span class="keyword">delete</span> context.fn;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="apply-实现"><a href="#apply-实现" class="headerlink" title="apply 实现"></a>apply 实现</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.myapply = <span class="function"><span class="keyword">function</span>(<span class="params">context, arr</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span> !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>&#125;</span> is not a function.`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  context = context || <span class="built_in">window</span>;</span><br><span class="line">  context.fn = <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> result;</span><br><span class="line">  <span class="keyword">if</span> (!arr) &#123;</span><br><span class="line">    result = context.fn();</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> args = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, l = arr.length; i &lt; l; i++) &#123;</span><br><span class="line">      args.push(<span class="string">"arr["</span> + i + <span class="string">"]"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    result = <span class="built_in">eval</span>(<span class="string">"context.fn("</span> + args + <span class="string">")"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">delete</span> context.fn;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>ES6:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.myapply = <span class="function"><span class="keyword">function</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span> !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>&#125;</span> is not a function.`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  context = context || <span class="built_in">window</span>;</span><br><span class="line">  context.fn = <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> args = <span class="built_in">arguments</span>[<span class="number">1</span>];</span><br><span class="line">  <span class="keyword">let</span> result;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (!args) &#123;</span><br><span class="line">    result = context.fn();</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    result = context.fn(...args);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">delete</span> context.fn;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://mp.weixin.qq.com/s?__biz=Mzg5ODA5NTM1Mw==&amp;mid=2247485202&amp;idx=1&amp;sn=1a668530cdce1eaf53c2ec6d796fdd7b&amp;chksm=c0668684f7110f922045c7a4539f78c51458ccafd5204ba7d89ad461ed360a1c14ed07778068&amp;mpshare=1&amp;scene=23&amp;srcid=0329UG1H5LSPIxcKsQwUWXlo&amp;sharer_sharetime=1585447184050&amp;sharer_shareid=73865875704bcba3caa8b09c62f6bd7a%23rd" target="_blank" rel="noopener">各种手写源码实现</a></li><li><a href="https://maimai.cn/article/detail?fid=1414317645&amp;efid=GX16EiGB-SbwDA5N9-zXBQ&amp;use_rn=1&amp;from=timeline&amp;isappinstalled=0" target="_blank" rel="noopener">JavaScript 中各种源码实现（前端面试笔试必备）</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/11" target="_blank" rel="noopener">JavaScript 深入之 call 和 apply 的模拟实现</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/12" target="_blank" rel="noopener">JavaScript 深入之 bind 的模拟实现</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;bind() 方法会创建一个新函数。当这个新函数被调用时，bind() 的第一个参数将作为它运行时的 this，之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
      <category term="手写系列" scheme="https://www.yangtao.site/tags/%E6%89%8B%E5%86%99%E7%B3%BB%E5%88%97/"/>
    
  </entry>
  
  <entry>
    <title>浏览器是怎么缓存资源的</title>
    <link href="https://www.yangtao.site/2020/03/27/web-cache/"/>
    <id>https://www.yangtao.site/2020/03/27/web-cache/</id>
    <published>2020-03-27T12:24:41.000Z</published>
    <updated>2020-03-27T12:29:14.430Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>对浏览器的缓存机制来做个简要的总结：首先通过 <code>Cache-Control</code> 验证强缓存是否可用；如果强缓存可用，直接使用；否则进入协商缓存，即发送 HTTP 请求，服务器通过请求头中的<code>If-Modified-Since</code>或者<code>If-None-Match</code>这些条件请求字段检查资源是否更新。若资源更新，返回资源和<strong>200 状态码</strong>；否则，返回<strong>304</strong>，告诉浏览器直接从缓存获取资源</p></blockquote><a id="more"></a><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>浏览器缓存（Brower Caching）是浏览器在本地磁盘对用户最近请求过的文档进行存储，当访问者再次访问同一页面时，浏览器就可以直接从本地磁盘加载文档。这样做的好处有：<strong>减少冗余数据传输、减少服务器负担、加快客户端加载网页的速度</strong>。</p><p>浏览器的缓存规则分为两大块：<strong>强制缓存</strong>和<strong>协商缓存</strong>。对于某个文件来说，具体是采用哪种缓存方式，由 HTTP Response Headers 设置，当然也可以通过 meta 标签，但是现在越来越多浏览器忽略设置缓存的 meta 标签，所以还是推荐通过 HTTP Response Headers 设置。</p><h2 id="强制缓存"><a href="#强制缓存" class="headerlink" title="强制缓存"></a>强制缓存</h2><p>首先客户端会检查本地缓存中是否有所要请求的数据，如果有，就直接从缓存中获取数据；如果没有，就从服务器获取数据。</p><p>HTTP Response Headers 中 <code>Cache-Control</code> 和 <code>Expires</code> 字段都表示对本资源启动强制缓存，其中 Cache-Control 是 http1.1 标准中的字段，而 Expires 是 http1.0 的字段，Cache-Control 优先级更高，使用更广泛。</p><h3 id="Expires"><a href="#Expires" class="headerlink" title="Expires"></a>Expires</h3><p><strong>Expires</strong> 即过期时间，存在于服务端返回的响应头中，告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据，无需再次请求，如：</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Expires</span>: Wed, 22 Nov 2019 08:41:00 GMT</span><br></pre></td></tr></table></figure><h3 id="Cache-Control"><a href="#Cache-Control" class="headerlink" title="Cache-Control"></a>Cache-Control</h3><p><strong>Cache-Control</strong> 的值的单位为秒，关键字 <code>max-age</code> 表示可以被缓存多长时间。</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">cache-control</span>: max-age=36029314</span><br></pre></td></tr></table></figure><p>百度首页加载的一个资源返回头中出现了 Cache-Control 字段，当 Cache-Control 值设为 <code>max-age=300</code> 时，则代表在这个请求正确返回时间（浏览器也会记录下来）的 5 分钟内再次加载资源，就会命中强缓存。</p><p>常用的设置值：</p><ul><li><code>no-cache</code>： 不使用本地缓存。需要使用缓存协商，先与服务器确认返回的响应是否被更改，如果之前的响应中存在 ETag，那么请求的时候会与服务端验证，如果资源未被更改，则可以避免重新下载。</li><li><code>no-store</code>： 直接禁止浏览器缓存数据，每次用户请求该资源，都会向服务器发送一个请求，每次都会下载完整的资源。</li><li><code>public</code>： 可以被所有的用户缓存，包括终端用户和 CDN 等中间代理服务器。</li><li><code>private</code>： 只能被终端用户的浏览器缓存，不允许 CDN 等中继缓存服务器对其缓存。</li><li><code>s-maxage</code>：这和 max-age 长得比较像，但是区别在于 s-maxage 是针对代理服务器的缓存时间。</li><li><code>must-revalidate</code>：是缓存就会有过期的时候，加上这个字段一旦缓存过期，就必须回到源服务器验证。</li></ul><p><strong>注意</strong>：当 Expires 和 Cache-Control 同时存在的时候，Cache-Control 会优先考虑。</p><p>当然，还存在一种情况，当资源缓存时间超时了，也就是强缓存失效了，接下来怎么办？没错，这样就进入到第二级屏障——协商缓存了。</p><h2 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h2><p>强缓存失效之后，浏览器在请求头中携带相应的缓存 tag 来向服务器发请求，由服务器根据这个 tag，来决定是否使用缓存，这就是协商缓存。</p><p>具体来说，这样的缓存 tag 分为两种: <strong>Last-Modified</strong> 和 <strong>ETag</strong>。这两者各有优劣，并不存在谁对谁有绝对的优势，跟上面强缓存的两个 tag 不一样。</p><h3 id="Last-Modified"><a href="#Last-Modified" class="headerlink" title="Last-Modified"></a>Last-Modified</h3><p>资源被服务器返回时，HTTP Response Headers 中的 <strong>Last-Modified</strong> 返回头标识了此资源在服务器上的最后修改时间。</p><p>浏览器再次请求服务器时，会将上次 <strong>Last-Modified</strong> 的值作为 <strong>if-Modified-Since</strong> 头的值发送，服务器收到请求后，查看最后修改时间以后资源是否被修改过：</p><ul><li>没有被修改过，就返回 <strong>304</strong>，从缓存读取</li><li>修改过，返回状态 <strong>200</strong> 以及整个资源</li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">last-modified</span>: Wed, 13 Nov 2019 15:16:37 GMT</span><br></pre></td></tr></table></figure><h3 id="Etag"><a href="#Etag" class="headerlink" title="Etag"></a>Etag</h3><p><strong>ETag</strong> 是服务器根据当前文件的内容，给文件生成的唯一标识，只要里面的内容有改动，这个值就会变。服务器通过响应头把这个值给浏览器。</p><p>浏览器接收到 ETag 的值，会在下次请求时，将这个值作为<strong>If-None-Match</strong>这个字段的内容，并放到请求头中，然后发给服务器。</p><p>服务器接收到 If-None-Match 后，会跟服务器上该资源的 ETag 进行比对：</p><ul><li>两者不一样，说明要更新了。返回新的资源，跟常规的 HTTP 请求响应的流程一样</li><li>否则返回 304，告诉浏览器直接用缓存</li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">etag</span>: W/"36BE6ECF0746FFAC5024A69C27141E08"</span><br></pre></td></tr></table></figure><h3 id="两者对比"><a href="#两者对比" class="headerlink" title="两者对比"></a>两者对比</h3><p><strong>精度上</strong>Etag 要优于 Last-Modified。Last-Modified 的时间单位是秒，如果某个文件在 1 秒内改变了多次，那么他们的 Last-Modified 其实并没有体现出来修改，但是 Etag 每次都会改变确保了精度。</p><p><strong>性能上</strong>，Last-Modified 要优于 Etag，毕竟 Last-Modified 只需要记录时间，而 Etag 需要服务器通过算法来计算出一个 hash 值。</p><p><strong>优先级上</strong>，服务器校验优先考虑 Etag。</p><h2 id="浏览器缓存"><a href="#浏览器缓存" class="headerlink" title="浏览器缓存"></a>浏览器缓存</h2><p>浏览器缓存这一块，最重要的是能区分开<strong>强制缓存</strong>和<strong>协商缓存</strong>：</p><ul><li>完全不向服务器发送请求的是强制缓存</li><li>服务器发送请求的是协商缓存，涉及到 304 的都是协商缓存</li></ul><p>总结一下浏览器缓存的全过程：</p><p>浏览器第一次加载资源，服务器返回 200，浏览器从服务器下载资源文件，并缓存住资源文件与 response header 以供下次加载时对比使用；</p><p>下一次加载资源时，由于强制缓存优先级较高，先比较当前时间与上一次返回 200 时的时间差，如果没有超过 cache-control 设置的 max-age，则没有过期，并命中<strong>强缓存</strong>，直接从本地读取资源。如果浏览器不支持 HTTP1.1，则使用 expires 头判断是否过期；</p><p>如果资源已过期，则表明强制缓存没有被命中，则开始<strong>协商缓存</strong>，向服务器发送带有 <code>If-None-Match</code> 和 <code>If-Modified-Since</code> 的请求；</p><p>服务器收到请求后，优先根据 Etag 的值判断被请求的文件有没有做修改，Etag 值一致则没有修改，命中协商缓存，返回 <strong>304</strong>；如果不一致则有改动，直接返回新的资源文件带上新的 Etag 值并返回 <strong>200</strong>；</p><p>如果服务器收到的请求没有 Etag 值，则将 <code>If-Modified-Since</code> 和被请求文件的最后修改时间做比对，一致则命中协商缓存，返回 304；不一致则返回新的 <code>last-modified</code> 和文件并返回 200。</p><h2 id="其他问题"><a href="#其他问题" class="headerlink" title="其他问题"></a>其他问题</h2><h3 id="点击刷新按钮或者按-F5"><a href="#点击刷新按钮或者按-F5" class="headerlink" title="点击刷新按钮或者按 F5"></a>点击刷新按钮或者按 F5</h3><p>浏览器直接对本地的缓存文件过期，但是会带上<code>If-Modifed-Since</code>，<code>If-None-Match</code>，这就意味着服务器会对文件检查新鲜度，返回结果可能是 304，也有可能是 200。</p><h3 id="用户按-Ctrl-F5"><a href="#用户按-Ctrl-F5" class="headerlink" title="用户按 Ctrl+F5"></a>用户按 Ctrl+F5</h3><p>浏览器不仅会对本地文件过期，而且不会带上 <code>If-Modifed-Since</code>，<code>If-None-Match</code>，相当于之前从来没有请求过，返回结果是 200。</p><h3 id="地址栏回车"><a href="#地址栏回车" class="headerlink" title="地址栏回车"></a>地址栏回车</h3><p>浏览器发起请求，按照正常流程，本地检查是否过期，然后服务器检查新鲜度，最后返回内容。</p><p>最后还可以提到，你注意到很多网站的资源后面都加了版本号，这样做的目的是：每次升级了 JS 或 CSS 文件后，为了防止浏览器进行缓存，强制改变版本号，客户端浏览器就会重新下载新的 JS 或 CSS 文件 ，以保证用户能够及时获得网站的最新更新。</p><h3 id="缓存的资源都存在哪里"><a href="#缓存的资源都存在哪里" class="headerlink" title="缓存的资源都存在哪里"></a>缓存的资源都存在哪里</h3><p>按优先级从高到低分别是：</p><ul><li><p><strong>Service Worker</strong>：运行在 JavaScript 主线程之外，虽然由于脱离了浏览器窗体无法直接访问 DOM，但是它可以完成离线缓存、消息推送、网络代理等功能。</p></li><li><p><strong>Memory Cache</strong>：就是内存缓存，它的效率最快，但是存活时间最短，你一关掉浏览器 Memory Cache 里的文件就被清空了。</p></li><li><p><strong>Disk Cache：Cache</strong> 资源被存储在硬盘上，存活时间比 Memory Cache 要持久很多。</p></li><li><p><strong>Push Cache</strong>：即推送缓存，这是浏览器缓存的最后一道防线。它是 HTTP/2 中的内容，虽然现在应用的并不广泛，但随着 HTTP/2 的推广，它的应用越来越广泛。</p></li></ul><h2 id="Node-演示"><a href="#Node-演示" class="headerlink" title="Node 演示"></a>Node 演示</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">"http"</span>);</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"><span class="keyword">const</span> md5 = <span class="built_in">require</span>(<span class="string">"md5"</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handle</span>(<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> html = fs.readFileSync(<span class="string">"index.html"</span>, <span class="string">"utf-8"</span>);</span><br><span class="line">  <span class="keyword">const</span> script = fs.readFileSync(<span class="string">"script.js"</span>, <span class="string">"utf-8"</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (req.url === <span class="string">"/"</span>) &#123;</span><br><span class="line">    res.writeHead(<span class="number">200</span>, &#123;</span><br><span class="line">      <span class="string">"Content-Type"</span>: <span class="string">"text/html"</span></span><br><span class="line">    &#125;);</span><br><span class="line">    res.write(html);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (req.url === <span class="string">"/script.js"</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> etag = md5(script);</span><br><span class="line">    <span class="keyword">const</span> lastMidified = lastUpdatedDate(<span class="string">"./script.js"</span>);</span><br><span class="line">    <span class="keyword">const</span> ifNoneMatch = req.headers[<span class="string">"if-none-match"</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> statusCode = ifNoneMatch === etag ? <span class="number">304</span> : <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    res.writeHead(statusCode, &#123;</span><br><span class="line">      <span class="string">"Content-Type"</span>: <span class="string">"text/javascript"</span>,</span><br><span class="line">      <span class="string">"Cache-Control"</span>: <span class="string">"no-cache"</span>,</span><br><span class="line">      <span class="string">"Last-Modified"</span>: lastMidified,</span><br><span class="line">      Etag: etag</span><br><span class="line">    &#125;);</span><br><span class="line">    res.write(script);</span><br><span class="line">  &#125;</span><br><span class="line">  res.end();</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">lastUpdatedDate</span>(<span class="params">file</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; mtime &#125; = fs.statSync(file);</span><br><span class="line">  <span class="keyword">return</span> mtime;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">http.createServer(handle).listen(<span class="number">8000</span>);</span><br></pre></td></tr></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.imooc.com/read/68/article/1558" target="_blank" rel="noopener">浏览器是怎么缓存资源的</a></li><li><a href="http://47.98.159.95/my_blog/perform/001.html#%E5%BC%BA%E7%BC%93%E5%AD%98" target="_blank" rel="noopener">能不能说一说前端缓存</a></li><li><a href="https://www.jianshu.com/p/4667d0425878" target="_blank" rel="noopener">浏览器缓存 cache-control etag</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;对浏览器的缓存机制来做个简要的总结：首先通过 &lt;code&gt;Cache-Control&lt;/code&gt; 验证强缓存是否可用；如果强缓存可用，直接使用；否则进入协商缓存，即发送 HTTP 请求，服务器通过请求头中的&lt;code&gt;If-Modified-Since&lt;/code&gt;或者&lt;code&gt;If-None-Match&lt;/code&gt;这些条件请求字段检查资源是否更新。若资源更新，返回资源和&lt;strong&gt;200 状态码&lt;/strong&gt;；否则，返回&lt;strong&gt;304&lt;/strong&gt;，告诉浏览器直接从缓存获取资源&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="网络知识" scheme="https://www.yangtao.site/categories/%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86/"/>
    
    
      <category term="http" scheme="https://www.yangtao.site/tags/http/"/>
    
  </entry>
  
  <entry>
    <title>5 个有趣的 Node.js 库,带你走进 彩色 Node.js 世界</title>
    <link href="https://www.yangtao.site/2020/03/26/node-5-util/"/>
    <id>https://www.yangtao.site/2020/03/26/node-5-util/</id>
    <published>2020-03-26T14:20:41.000Z</published>
    <updated>2020-03-26T14:23:51.527Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>📦 5 个有趣的 Node.js 库,带你走进 彩色 Node.js 世界 🎉 🎉 🎉</p></blockquote><a id="more"></a><h2 id="1、Chalk"><a href="#1、Chalk" class="headerlink" title="1、Chalk"></a>1、Chalk</h2><p>Github：<a href="https://github.com/chalk/chalk" target="_blank" rel="noopener">chalk</a></p><p>这是一个能给你的 log 染色的库,让你的代码靓起来 !!</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> chalk = <span class="built_in">require</span>(<span class="string">"chalk"</span>);</span><br><span class="line"><span class="keyword">const</span> log = <span class="built_in">console</span>.log;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Combine styled and normal strings</span></span><br><span class="line">log(chalk.blue(<span class="string">"Hello"</span>) + <span class="string">" World"</span> + chalk.red(<span class="string">"!"</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Compose multiple styles using the chainable API</span></span><br><span class="line">log(chalk.blue.bgRed.bold(<span class="string">"Hello world!"</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Pass in multiple arguments</span></span><br><span class="line">log(chalk.blue(<span class="string">"Hello"</span>, <span class="string">"World!"</span>, <span class="string">"Foo"</span>, <span class="string">"bar"</span>, <span class="string">"biz"</span>, <span class="string">"baz"</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Nest styles</span></span><br><span class="line">log(chalk.red(<span class="string">"Hello"</span>, chalk.underline.bgBlue(<span class="string">"world"</span>) + <span class="string">"!"</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Nest styles of the same type even (color, underline, background)</span></span><br><span class="line">log(</span><br><span class="line">  chalk.green(</span><br><span class="line">    <span class="string">"I am a green line "</span> +</span><br><span class="line">      chalk.blue.underline.bold(<span class="string">"with a blue substring"</span>) +</span><br><span class="line">      <span class="string">" that becomes green again!"</span></span><br><span class="line">  )</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES2015 template literal</span></span><br><span class="line">log(<span class="string">`</span></span><br><span class="line"><span class="string">CPU: <span class="subst">$&#123;chalk.red(<span class="string">"90%"</span>)&#125;</span></span></span><br><span class="line"><span class="string">RAM: <span class="subst">$&#123;chalk.green(<span class="string">"40%"</span>)&#125;</span></span></span><br><span class="line"><span class="string">DISK: <span class="subst">$&#123;chalk.yellow(<span class="string">"70%"</span>)&#125;</span></span></span><br><span class="line"><span class="string">`</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES2015 tagged template literal</span></span><br><span class="line">log(chalk<span class="string">`</span></span><br><span class="line"><span class="string">CPU: &#123;red <span class="subst">$&#123;cpu.totalPercent&#125;</span>%&#125;</span></span><br><span class="line"><span class="string">RAM: &#123;green <span class="subst">$&#123;(ram.used <span class="regexp">/ ram.total) * 100&#125;%&#125;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp">DISK: &#123;rgb(255,131,0) $&#123;(disk.used /</span> disk.total) * <span class="number">100</span>&#125;</span>%&#125;</span></span><br><span class="line"><span class="string">`</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Use RGB colors in terminal emulators that support it.</span></span><br><span class="line">log(chalk.keyword(<span class="string">"orange"</span>)(<span class="string">"Yay for orange colored text!"</span>));</span><br><span class="line">log(chalk.rgb(<span class="number">123</span>, <span class="number">45</span>, <span class="number">67</span>).underline(<span class="string">"Underlined reddish color"</span>));</span><br><span class="line">log(chalk.hex(<span class="string">"#DEADED"</span>).bold(<span class="string">"Bold gray!"</span>));</span><br></pre></td></tr></table></figure><h2 id="2、Inquirer-js"><a href="#2、Inquirer-js" class="headerlink" title="2、Inquirer.js"></a>2、Inquirer.js</h2><p>Github：<a href="https://github.com/SBoudrias/Inquirer.js" target="_blank" rel="noopener">Inquirer.js</a></p><p>一个非常好看的交互式命令行用户界面,用它来定制你的 CLI 吧 ！</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> inquirer = <span class="built_in">require</span>(<span class="string">"inquirer"</span>);</span><br><span class="line">inquirer</span><br><span class="line">  .prompt([</span><br><span class="line">    <span class="comment">/* Pass your questions in here */</span></span><br><span class="line">  ])</span><br><span class="line">  .then(<span class="function"><span class="params">answers</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// Use user feedback for... whatever!!</span></span><br><span class="line">  &#125;)</span><br><span class="line">  .catch(<span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (error.isTtyError) &#123;</span><br><span class="line">      <span class="comment">// Prompt couldn't be rendered in the current environment</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// Something else when wrong</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p>Checkbox list examples：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> inquirer = <span class="built_in">require</span>(<span class="string">"inquirer"</span>);</span><br><span class="line"></span><br><span class="line">inquirer</span><br><span class="line">  .prompt([</span><br><span class="line">    &#123;</span><br><span class="line">      type: <span class="string">"checkbox"</span>,</span><br><span class="line">      message: <span class="string">"Select toppings"</span>,</span><br><span class="line">      name: <span class="string">"toppings"</span>,</span><br><span class="line">      choices: [</span><br><span class="line">        <span class="keyword">new</span> inquirer.Separator(<span class="string">" = The Meats = "</span>),</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Pepperoni"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Ham"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Ground Meat"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Bacon"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="keyword">new</span> inquirer.Separator(<span class="string">" = The Cheeses = "</span>),</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Mozzarella"</span>,</span><br><span class="line">          checked: <span class="literal">true</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Cheddar"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Parmesan"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="keyword">new</span> inquirer.Separator(<span class="string">" = The usual ="</span>),</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Mushroom"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Tomato"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="keyword">new</span> inquirer.Separator(<span class="string">" = The extras = "</span>),</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Pineapple"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Olives"</span>,</span><br><span class="line">          disabled: <span class="string">"out of stock"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          name: <span class="string">"Extra cheese"</span></span><br><span class="line">        &#125;</span><br><span class="line">      ],</span><br><span class="line">      validate: <span class="function"><span class="keyword">function</span>(<span class="params">answer</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (answer.length &lt; <span class="number">1</span>) &#123;</span><br><span class="line">          <span class="keyword">return</span> <span class="string">"You must choose at least one topping."</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ])</span><br><span class="line">  .then(<span class="function"><span class="params">answers</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">JSON</span>.stringify(answers, <span class="literal">null</span>, <span class="string">"  "</span>));</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><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">? Select toppings (Press &lt;space&gt; to select, &lt;a&gt; to toggle</span><br><span class="line"> all, &lt;i&gt; to invert selection)</span><br><span class="line">  = The Meats =</span><br><span class="line">❯◯ Pepperoni</span><br><span class="line"> ◯ Ham</span><br><span class="line"> ◯ Ground Meat</span><br><span class="line"> ◯ Bacon</span><br><span class="line">  = The Cheeses =</span><br><span class="line"> ◉ Mozzarella</span><br><span class="line">(Move up and down to reveal more choices)</span><br></pre></td></tr></table></figure><h2 id="3、Ora"><a href="#3、Ora" class="headerlink" title="3、Ora"></a>3、Ora</h2><p>Github：<a href="https://github.com/sindresorhus/ora" target="_blank" rel="noopener">ora</a></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">const</span> ora = <span class="built_in">require</span>(<span class="string">"ora"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> spinner = ora(<span class="string">"Loading unicorns"</span>).start();</span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  spinner.color = <span class="string">"cyan"</span>;</span><br><span class="line">  spinner.text = <span class="string">"Loading rainbows"</span>;</span><br><span class="line">&#125;, <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  spinner.text = <span class="string">"Loading success"</span>;</span><br><span class="line">  spinner.succeed();</span><br><span class="line">  <span class="comment">// spinner.fail();</span></span><br><span class="line">&#125;, <span class="number">3000</span>);</span><br></pre></td></tr></table></figure><p>输出（icon 是有对应颜色的）：</p><figure class="highlight shell"><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">⠧ Loading rainbows</span><br><span class="line">✔ Loading success</span><br></pre></td></tr></table></figure><h2 id="4、figlet-js"><a href="#4、figlet-js" class="headerlink" title="4、figlet.js"></a>4、figlet.js</h2><p>Github：<a href="https://github.com/patorjk/figlet.js" target="_blank" rel="noopener">figlet.js</a></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> figlet = <span class="built_in">require</span>(<span class="string">"figlet"</span>);</span><br><span class="line"></span><br><span class="line">figlet(<span class="string">"Hello World!!"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (err) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Something went wrong..."</span>);</span><br><span class="line">    <span class="built_in">console</span>.dir(err);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">console</span>.log(data);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>然后输出：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><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><br><span class="line">| | | | ___| | | ___   \ \      / /__  _ __| | __| | | |</span><br><span class="line">| |_| |/ _ \ | |/ _ \   \ \ /\ / / _ \| '__| |/ _` | | |</span><br><span class="line">|  _  |  __/ | | (_) |   \ V  V / (_) | |  | | (_| |_|_|</span><br><span class="line">|_| |_|\___|_|_|\___/     \_/\_/ \___/|_|  |_|\__,_(_|_)</span><br></pre></td></tr></table></figure><h2 id="5、boxen"><a href="#5、boxen" class="headerlink" title="5、boxen"></a>5、boxen</h2><p>Github：<a href="https://github.com/sindresorhus/boxen" target="_blank" rel="noopener">boxen</a></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">const</span> boxen = <span class="built_in">require</span>(<span class="string">"boxen"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(boxen(<span class="string">"Hello World"</span>, &#123; <span class="attr">padding</span>: <span class="number">1</span>, <span class="attr">borderColor</span>: <span class="string">"yellow"</span> &#125;));</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(</span><br><span class="line">  boxen(<span class="string">"I LOVE YOU"</span>, &#123;</span><br><span class="line">    padding: <span class="number">1</span>,</span><br><span class="line">    margin: <span class="number">1</span>,</span><br><span class="line">    borderStyle: <span class="string">"classic"</span>,</span><br><span class="line">    borderColor: <span class="string">"red"</span>,</span><br><span class="line">    backgroundColor: <span class="string">"magenta"</span></span><br><span class="line">  &#125;)</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>输出效果：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><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><br><span class="line">│                 │</span><br><span class="line">│   Hello World   │</span><br><span class="line">│                 │</span><br><span class="line">└─────────────────┘</span><br><span class="line"></span><br><span class="line">   +----------------+</span><br><span class="line">   |                |</span><br><span class="line">   |   I LOVE YOU   |</span><br><span class="line">   |                |</span><br><span class="line">   +----------------+</span><br></pre></td></tr></table></figure><h2 id="资料"><a href="#资料" class="headerlink" title="资料"></a>资料</h2><ul><li><a href="https://juejin.im/post/5de4b6caf265da05fc66d9af" target="_blank" rel="noopener">📦 5 个有趣的 Node.js 库,带你走进 彩色 Node.js 世界 🎉</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;📦 5 个有趣的 Node.js 库,带你走进 彩色 Node.js 世界 🎉 🎉 🎉&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="Nodejs" scheme="https://www.yangtao.site/categories/Nodejs/"/>
    
    
      <category term="Nodejs" scheme="https://www.yangtao.site/tags/Nodejs/"/>
    
  </entry>
  
  <entry>
    <title>跨域资源共享 CORS</title>
    <link href="https://www.yangtao.site/2020/03/25/cross-cors/"/>
    <id>https://www.yangtao.site/2020/03/25/cross-cors/</id>
    <published>2020-03-25T06:43:47.000Z</published>
    <updated>2020-03-27T08:30:19.059Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>跨域资源共享(CORS) 是一种机制，它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。 — MDN</p></blockquote><a id="more"></a><h2 id="什么情况下需要-CORS"><a href="#什么情况下需要-CORS" class="headerlink" title="什么情况下需要 CORS"></a>什么情况下需要 CORS</h2><p>允许在下列场景中使用跨域 HTTP 请求：</p><ul><li>由 <code>XMLHttpRequest</code> 或 <code>Fetch</code> 发起的跨域 HTTP 请求</li><li>Web 字体 (CSS 中通过 <code>@font-face</code> 使用跨域字体资源)</li><li><code>WebGL</code> 贴图</li><li>使用 drawImage 将 Images/video 画面绘制到 <code>canvas</code></li></ul><h2 id="两种请求"><a href="#两种请求" class="headerlink" title="两种请求"></a>两种请求</h2><p>浏览器将 CORS 请求分成两类：<strong>简单请求</strong>（simple request）和<strong>非简单请求</strong>（not-so-simple request）。</p><p>只要同时满足以下两大条件，就属于简单请求（不会触发 CORS 预检请求）。</p><ul><li><p>请求方法是以下三种方法之一：<code>HEAD</code>、<code>GET</code>、<code>POST</code></p></li><li><p>HTTP 的头信息不超出以下几种字段：</p><ul><li>Accept</li><li>Accept-Language</li><li>Content-Language</li><li>Last-Event-ID</li><li>Content-Type（只限于三个值）<ul><li><code>application/x-www-form-urlencoded</code></li><li><code>multipart/form-data</code></li><li><code>text/plain</code></li></ul></li></ul></li></ul><p>凡是不同时满足上面两个条件，就属于非简单请求。</p><h2 id="CORS-如何工作"><a href="#CORS-如何工作" class="headerlink" title="CORS 如何工作"></a>CORS 如何工作</h2><p>首先，浏览器判断请求是简单请求还是复杂请求（非简单请求）。</p><p>如果是复杂请求，那么在进行真正的请求之前，浏览器会先使用 OPTIONS 方法发送一个<strong>预检请求</strong> (preflight request)，OPTIONS 是 <code>HTTP/1.1</code> 协议中定义的方法，用以从服务器获取更多信息。</p><p>该方法不会对服务器资源产生影响，预检请求中同时携带了下面两个首部字段：</p><ul><li><code>Access-Control-Request-Method</code>: 这个字段表明了请求的方法；</li><li><code>Access-Control-Request-Headers</code>: 这个字段表明了这个请求的 Headers；</li><li><code>Origin</code>: 这个字段表明了请求发出的域。</li></ul><p>服务端收到请求后，会以 <code>Access-Control-* response headers</code> 的形式对客户端进行回复：</p><ul><li><code>Access-Control-Allow-Origin</code>: 能够被允许发出这个请求的域名，也可以使用<code>*</code>来表明允许所有域名；</li><li><code>Access-Control-Allow-Methods</code>: 用逗号分隔的被允许的请求方法的列表；</li><li><code>Access-Control-Allow-Headers</code>: 用逗号分隔的被允许的请求头部字段的列表；</li><li><code>Access-Control-Max-Age</code>: 这个<strong>预检请求能被缓存的最长时间</strong>，在缓存时间内，同一个请求不会再次发出预检请求。</li></ul><h2 id="简单请求"><a href="#简单请求" class="headerlink" title="简单请求"></a>简单请求</h2><p>对于简单请求，浏览器直接发出 CORS 请求。具体来说，就是在头信息之中，自动增加一个 Origin 字段，用来说明请求来自哪个源。服务器拿到请求之后，在回应时对应地添加<code>Access-Control-Allow-Origin</code>字段，如果 Origin 不在这个字段的范围中，那么浏览器就会将响应拦截。</p><p><strong>Access-Control-Allow-Credentials</strong>。这个字段是一个布尔值，表示是否允许发送 Cookie，对于跨域请求，浏览器对这个字段默认值设为 false，而如果需要拿到浏览器的 Cookie，需要添加这个响应头并设为 true, 并且在前端也需要设置<code>withCredentials</code>属性：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">xhr.withCredentials = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><p><strong>Access-Control-Expose-Headers</strong>。这个字段是给 <code>XMLHttpRequest</code> 对象赋能，让它不仅可以拿到基本的 6 个响应头字段（包括<code>Cache-Control、Content-Language、Content-Type、Expires、Last-Modified和Pragma</code>）, 还能拿到这个字段声明的响应头字段。比如这样设置:</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Access-Control-Expose-Headers</span>: aaa</span><br></pre></td></tr></table></figure><p>那么在前端可以通过 <code>XMLHttpRequest.getResponseHeader(&#39;aaa&#39;)</code> 拿到 aaa 这个字段的值。</p><h3 id="举个栗子"><a href="#举个栗子" class="headerlink" title="举个栗子"></a>举个栗子</h3><p>比如下面开启一个端口为 8001 的服务，去请求端口为 8000 的数据：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> url = <span class="string">"http://127.0.0.1:8000"</span>;</span><br><span class="line"><span class="keyword">const</span> data = &#123; <span class="attr">username</span>: <span class="string">"example"</span> &#125;;</span><br><span class="line"><span class="keyword">const</span> myHeaders = <span class="keyword">new</span> Headers(&#123;</span><br><span class="line">  <span class="string">"Content-Type"</span>: <span class="string">"text/plain"</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">fetch(url, &#123;</span><br><span class="line">  method: <span class="string">"POST"</span>,</span><br><span class="line">  headers: myHeaders,</span><br><span class="line">  body: <span class="built_in">JSON</span>.stringify(data),</span><br><span class="line">  mode: <span class="string">"cors"</span></span><br><span class="line">&#125;)</span><br><span class="line">  .then(<span class="function"><span class="params">res</span> =&gt;</span> res.json())</span><br><span class="line">  .then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">JSON</span>.parse(res.postData)); <span class="comment">//&#123;username: "example"&#125;</span></span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p>端口为 8000 的服务端设置：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">"http"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = http.createServer(<span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  res.writeHead(<span class="number">200</span>, &#123;</span><br><span class="line">    <span class="string">"Content-Type"</span>: <span class="string">"text/plain"</span>,</span><br><span class="line">    <span class="string">"Access-Control-Allow-Origin"</span>: <span class="string">"*"</span></span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">let</span> resData = &#123;&#125;;</span><br><span class="line">  <span class="keyword">let</span> postData = [];</span><br><span class="line">  req.on(<span class="string">"data"</span>, chunk =&gt; &#123;</span><br><span class="line">    postData.push(chunk);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  req.on(<span class="string">"end"</span>, () =&gt; &#123;</span><br><span class="line">    resData.postData = Buffer.concat(postData).toString();</span><br><span class="line">    res.end(<span class="built_in">JSON</span>.stringify(resData));</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">server.listen(<span class="number">8000</span>);</span><br></pre></td></tr></table></figure><h2 id="非简单请求"><a href="#非简单请求" class="headerlink" title="非简单请求"></a>非简单请求</h2><p>非简单请求相对而言会有些不同，体现在两个方面: <strong>预检请求</strong>和<strong>响应字段</strong>。</p><h3 id="预检请求"><a href="#预检请求" class="headerlink" title="预检请求"></a>预检请求</h3><p>比如使用 PUT 请求方法：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> url = <span class="string">"http://127.0.0.1:8000"</span>;</span><br><span class="line"><span class="keyword">const</span> data = &#123; <span class="attr">username</span>: <span class="string">"example"</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> myHeaders = <span class="keyword">new</span> Headers(&#123;</span><br><span class="line">  <span class="string">"X-Custom-Header"</span>: <span class="string">"xxx"</span></span><br><span class="line">&#125;);</span><br><span class="line">fetch(url, &#123;</span><br><span class="line">  method: <span class="string">"PUT"</span>,   <span class="comment">// 改成 PUT</span></span><br><span class="line">  headers: myHeaders,</span><br><span class="line">  body: <span class="built_in">JSON</span>.stringify(data),</span><br><span class="line">  mode: <span class="string">"cors"</span></span><br><span class="line">&#125;)</span><br><span class="line">  .then(<span class="function"><span class="params">res</span> =&gt;</span> res.json())</span><br><span class="line">  .then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">JSON</span>.parse(res.postData)); <span class="comment">//&#123;username: "example"&#125;</span></span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p>Node 部分：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">res.writeHead(<span class="number">200</span>, &#123;</span><br><span class="line">  <span class="string">"Content-Type"</span>: <span class="string">"text/json"</span>,</span><br><span class="line">  <span class="string">"Access-Control-Allow-Origin"</span>: <span class="string">"*"</span>,</span><br><span class="line">  <span class="string">"Access-Control-Allow-Methods"</span>: <span class="string">"PUT, POST, GET"</span>,</span><br><span class="line">  <span class="string">"Access-Control-Allow-Headers"</span>: <span class="string">"X-Custom-Header"</span>,</span><br><span class="line">  <span class="string">"Access-Control-Max-Age"</span>: <span class="number">2000</span>,</span><br><span class="line">  <span class="string">"Access-Control-Allow-Credentials"</span>: <span class="literal">true</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>当这段代码执行后，首先会发送<strong>预检请求</strong>。这个预检请求的请求行和请求体是下面这个格式:</p><figure class="highlight http"><table><tr><td class="gutter"><pre><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">OPTIONS</span> <span class="string">/</span> HTTP/1.1</span><br><span class="line"><span class="attribute">Host</span>: 127.0.0.1:8000</span><br><span class="line"><span class="attribute">Access-Control-Request-Method</span>: PUT</span><br><span class="line"><span class="attribute">Access-Control-Request-Headers</span>: x-custom-header</span><br><span class="line"><span class="attribute">Origin</span>: http://127.0.0.1:8001</span><br></pre></td></tr></table></figure><p>预检请求的方法是<code>OPTIONS</code>，同时会加上 Origin 源地址和 Host 目标地址，这很简单。同时也会加上两个关键的字段:</p><ul><li><code>Access-Control-Request-Method</code>, 列出 CORS 请求用到哪个 HTTP 方法</li><li><code>Access-Control-Request-Headers</code>，指定 CORS 请求将要加上什么请求头</li></ul><p>这是预检请求。接下来是<strong>响应字段</strong>。</p><p>响应字段也分为两部分，一部分是对于<strong>预检请求的响应</strong>，一部分是对于<strong>CORS 请求的响应</strong>。</p><p><strong>预检请求的响应</strong>：</p><figure class="highlight http"><table><tr><td class="gutter"><pre><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">HTTP/1.1 <span class="number">200</span> OK</span><br><span class="line"><span class="attribute">Content-Type</span>: text/json</span><br><span class="line"><span class="attribute">Access-Control-Allow-Origin</span>: *</span><br><span class="line"><span class="attribute">Access-Control-Allow-Methods</span>: PUT, POST, GET</span><br><span class="line"><span class="attribute">Access-Control-Allow-Headers</span>: X-Custom-Header</span><br><span class="line"><span class="attribute">Access-Control-Max-Age</span>: 2000</span><br><span class="line"><span class="attribute">Access-Control-Allow-Credentials</span>: true</span><br><span class="line"><span class="attribute">Date</span>: Fri, 27 Mar 2020 08:16:58 GMT</span><br><span class="line"><span class="attribute">Connection</span>: keep-alive</span><br><span class="line"><span class="attribute">Transfer-Encoding</span>: chunked</span><br></pre></td></tr></table></figure><p>在预检请求的响应返回后，如果请求不满足响应头的条件，则触发<code>XMLHttpRequest</code>的<code>onerror</code>方法，当然后面真正的 CORS 请求也不会发出去了。</p><p><strong>CORS 请求的响应</strong>：现在它和简单请求的情况是一样的。浏览器自动加上 Origin 字段，服务端响应头返回 <code>Access-Control-Allow-Origin</code>。在设置的<code>Access-Control-Max-Age: 2000</code>里是不会再次发送预检请求的，除非时间过期。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://juejin.im/post/5e76bd516fb9a07cce750746#heading-67" target="_blank" rel="noopener">什么是跨域？浏览器如何拦截响应？如何解决</a></li><li>阮一峰 <a href="http://www.ruanyifeng.com/blog/2016/04/cors.html" target="_blank" rel="noopener">跨域资源共享 CORS 详解</a></li><li>MDN <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS" target="_blank" rel="noopener">HTTP 访问控制（CORS）</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch" target="_blank" rel="noopener">使用 Fetch</a></li><li><a href="https://coding.imooc.com/learn/list/225.html" target="_blank" rel="noopener">HTTP协议原理+实践Web开发工程师必学</a> - 慕课网付费课程</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;跨域资源共享(CORS) 是一种机制，它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。 — MDN&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="网络知识" scheme="https://www.yangtao.site/categories/%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86/"/>
    
    
      <category term="http" scheme="https://www.yangtao.site/tags/http/"/>
    
  </entry>
  
  <entry>
    <title>跨域解决方案 JSONP</title>
    <link href="https://www.yangtao.site/2020/03/25/cross-jsonp/"/>
    <id>https://www.yangtao.site/2020/03/25/cross-jsonp/</id>
    <published>2020-03-25T03:07:14.000Z</published>
    <updated>2020-03-27T07:39:12.082Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>JSONP 请求本质上是利用了 “Ajax 请求会受到同源策略限制，而 script 标签请求不会” 这一点来绕过同源策略。</p></blockquote><a id="more"></a><h2 id="同源策略"><a href="#同源策略" class="headerlink" title="同源策略"></a>同源策略</h2><p>什么是同源策略，其作用是什么？</p><p>同源策略指的是：协议+域名+端口三者皆相同，可以视为在同一个域，否则为不同域。同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。</p><p>作用是一个用于隔离潜在恶意文件的重要安全机制。</p><p>所限制的跨域交互包括：</p><ul><li>Cookie、LocalStorage、IndexdDB 等存储内容；</li><li>DOM 节点；</li><li>Ajax 请求。</li></ul><h2 id="Ajax-为什么不能跨域"><a href="#Ajax-为什么不能跨域" class="headerlink" title="Ajax 为什么不能跨域"></a>Ajax 为什么不能跨域</h2><p>Ajax 其实就是向服务器发送一个 GET 或 POST 请求，然后取得服务器响应结果，返回客户端。Ajax 跨域请求，在服务器端不会有任何问题，只是服务端响应数据返回给浏览器的时候，浏览器根据响应头的<code>Access-Control-Allow-Origin</code>字段的值来判断是否有权限获取数据。因此，服务端如果没有设置跨域字段设置，跨域是没有权限访问，数据被浏览器给拦截了。</p><p>所以，要解决的问题是：<strong>如何从客户端拿到返回的数据</strong>？</p><p>其实，在同源策略的基础上，选择性地为同源策略开放了一些后门。例如 img、script、style 等标签，都允许跨域引用资源。</p><p>所以， JSONP 来了。</p><h2 id="JSONP-实现"><a href="#JSONP-实现" class="headerlink" title="JSONP 实现"></a>JSONP 实现</h2><p>JSONP(JSON with Padding（填充）)是 JSON 的一种“使用模式”，本质不是 Ajax 请求，是 script 标签请求。JSONP 请求本质上是利用了 “Ajax 请求会受到同源策略限制，而 script 标签请求不会” 这一点来绕过同源策略。</p><p>简单 JSONP 实现：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Jsonp</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(req) &#123;</span><br><span class="line">    <span class="keyword">this</span>.url = req.url;</span><br><span class="line">    <span class="keyword">this</span>.callbackName = req.callbackName;</span><br><span class="line">  &#125;</span><br><span class="line">  create() &#123;</span><br><span class="line">    <span class="keyword">const</span> script = <span class="built_in">document</span>.createElement(<span class="string">"script"</span>);</span><br><span class="line">    <span class="keyword">const</span> url = <span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>.url&#125;</span>?callback=<span class="subst">$&#123;<span class="keyword">this</span>.callbackName&#125;</span>`</span>;</span><br><span class="line">    script.src = url;</span><br><span class="line">    <span class="built_in">document</span>.getElementsByTagName(<span class="string">"head"</span>)[<span class="number">0</span>].appendChild(script);</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">new</span> Jsonp(&#123;</span><br><span class="line">  url: <span class="string">"http://127.0.0.1:8000/"</span>,</span><br><span class="line">  callbackName: <span class="string">"getMsg"</span></span><br><span class="line">&#125;).create();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getMsg</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">  data = <span class="built_in">JSON</span>.parse(data);</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;data.name&#125;</span>, and <span class="subst">$&#123;data.age&#125;</span> years old.`</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>服务端（Node）:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">"http"</span>);</span><br><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">"querystring"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = http.createServer(<span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> url = req.url;</span><br><span class="line">  <span class="keyword">const</span> query = querystring.parse(url.split(<span class="string">"?"</span>)[<span class="number">1</span>]);</span><br><span class="line">  <span class="keyword">const</span> &#123; callback &#125; = query;</span><br><span class="line">  <span class="keyword">const</span> data = &#123;</span><br><span class="line">    name: <span class="string">"Yang Min"</span>,</span><br><span class="line">    age: <span class="string">"8"</span></span><br><span class="line">  &#125;;</span><br><span class="line">  res.end(<span class="string">`<span class="subst">$&#123;callback&#125;</span>('<span class="subst">$&#123;<span class="built_in">JSON</span>.stringify(data)&#125;</span>')`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">server.listen(<span class="number">8000</span>);</span><br></pre></td></tr></table></figure><p>前端利用 <code>http-server -p 8001 .</code>，开启一个服务，然后 Node 也开启一个端口为 8000 的服务，运行：</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">My name is Yang Min, and 8 years old.</span><br></pre></td></tr></table></figure><h3 id="一个-JSONP-的步骤实质"><a href="#一个-JSONP-的步骤实质" class="headerlink" title="一个 JSONP 的步骤实质"></a>一个 JSONP 的步骤实质</h3><p>客户端发送 script 请求，参数中带着处理返回数据的回调函数的名字 (通常是 callback)，如请求 script 的 url 是：</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">http://127.0.0.1:8000/?callback=getMsg</span><br></pre></td></tr></table></figure><p>服务端收到请求，以回调函数名和返回数据组成立即执行函数的字符串，比如：其中 callback 的值是客户端发来的回调函数的名字，假设回调函数的名字是 getMsg，返回脚本的内容就是：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">getMsg(<span class="string">"&#123;name: 'Yang Min', age: '8'&#125;"</span>);</span><br></pre></td></tr></table></figure><p>客户端收到 JavaScript 脚本内容后，立即执行脚本，这样就实现了获取跨域服务器数据的目的。</p><p>很明显，由于 JSONP 技术本质上利用了 script 脚本请求，所以只能实现 GET 跨域请求，这也是 JSONP 跨域的最大限制。</p><p>由于 server 产生的响应为 json 数据的包装（故称之为 jsonp，即 json padding），形如：<code>getMsg(&quot;{name: &#39;Yang Min&#39;, age: &#39;8&#39;}&quot;)</code></p><h2 id="JSONP-封装"><a href="#JSONP-封装" class="headerlink" title="JSONP 封装"></a>JSONP 封装</h2><p>客户端：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> jsonp = <span class="function">(<span class="params">&#123; url, params, callbackName &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> generateURL = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> dataStr = <span class="string">""</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">in</span> params) &#123;</span><br><span class="line">      dataStr += <span class="string">`<span class="subst">$&#123;key&#125;</span>=<span class="subst">$&#123;params[key]&#125;</span>&amp;`</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    dataStr += <span class="string">`callback=<span class="subst">$&#123;callbackName&#125;</span>`</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;url&#125;</span>?<span class="subst">$&#123;dataStr&#125;</span>`</span>;</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 初始化回调函数名称</span></span><br><span class="line">    callbackName =</span><br><span class="line">      callbackName ||</span><br><span class="line">      <span class="string">"cb"</span> +</span><br><span class="line">        <span class="built_in">Math</span>.random()</span><br><span class="line">          .toString()</span><br><span class="line">          .replace(<span class="string">"."</span>, <span class="string">""</span>);</span><br><span class="line">    <span class="keyword">let</span> scriptEle = <span class="built_in">document</span>.createElement(<span class="string">"script"</span>);</span><br><span class="line">    scriptEle.src = generateURL();</span><br><span class="line">    <span class="built_in">document</span>.body.appendChild(scriptEle);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 绑定到 window 上，为了后面调用</span></span><br><span class="line">    <span class="built_in">window</span>[callbackName] = <span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">      resolve(data);</span><br><span class="line">      <span class="comment">// script 执行完了，成为无用元素，需要清除</span></span><br><span class="line">      <span class="built_in">document</span>.body.removeChild(scriptEle);</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">jsonp(&#123;</span><br><span class="line">  url: <span class="string">"http://127.0.0.1:8000/"</span>,</span><br><span class="line">  params: &#123;</span><br><span class="line">    name: <span class="string">"Yang Min"</span>,</span><br><span class="line">    age: <span class="string">"8"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  callbackName: <span class="string">"getData"</span></span><br><span class="line">&#125;)</span><br><span class="line">  .then(<span class="function"><span class="params">data</span> =&gt;</span> <span class="built_in">JSON</span>.parse(data))</span><br><span class="line">  .then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(data); <span class="comment">// &#123;name: "Yang Min", age: "8"&#125;</span></span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p>Node 端：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">"http"</span>);</span><br><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">"querystring"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = http.createServer(<span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> url = req.url;</span><br><span class="line">  <span class="keyword">const</span> query = querystring.parse(url.split(<span class="string">"?"</span>)[<span class="number">1</span>]);</span><br><span class="line">  <span class="keyword">const</span> &#123; name, age, callback &#125; = query;</span><br><span class="line">  <span class="keyword">const</span> data = &#123;</span><br><span class="line">    name,</span><br><span class="line">    age</span><br><span class="line">  &#125;</span><br><span class="line">  res.end(<span class="string">`<span class="subst">$&#123;callback&#125;</span>('<span class="subst">$&#123;<span class="built_in">JSON</span>.stringify(data)&#125;</span>')`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">server.listen(<span class="number">8000</span>);</span><br></pre></td></tr></table></figure><h2 id="jQuery-中的-JSONP"><a href="#jQuery-中的-JSONP" class="headerlink" title="jQuery 中的 JSONP"></a>jQuery 中的 JSONP</h2><p>Node 部分不变，使用 jQuery(3.4.1) 如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getAjaxData</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  $.ajax(&#123;</span><br><span class="line">    type: <span class="string">"get"</span>,</span><br><span class="line">    <span class="keyword">async</span>: <span class="literal">false</span>,</span><br><span class="line">    url: <span class="string">"http://127.0.0.1:8000/"</span>,</span><br><span class="line">    dataType: <span class="string">"jsonp"</span>, <span class="comment">//由 JSON 改为 JSONP</span></span><br><span class="line">    jsonp: <span class="string">"callback"</span>, <span class="comment">//传递给请求处理程序或页面的，标识jsonp回调函数名(一般为:callback)</span></span><br><span class="line">    jsonpCallback: <span class="string">"getData"</span>, <span class="comment">//callback的function名称，成功就会直接走 success 方法</span></span><br><span class="line">    success: <span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">      data = <span class="built_in">JSON</span>.parse(data);</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;data.name&#125;</span>, and <span class="subst">$&#123;data.age&#125;</span> years old.`</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    error: <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">"Error"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line">getAjaxData();</span><br></pre></td></tr></table></figure><p>使用延迟对象重新写下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">getAjaxData</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> def = $.ajax(&#123;</span><br><span class="line">    type: <span class="string">"get"</span>,</span><br><span class="line">    <span class="keyword">async</span>: <span class="literal">false</span>,</span><br><span class="line">    url: <span class="string">"http://127.0.0.1:8000/"</span>,</span><br><span class="line">    dataType: <span class="string">"jsonp"</span>,</span><br><span class="line">    jsonp: <span class="string">"callback"</span>,</span><br><span class="line">    jsonpCallback: <span class="string">"getData"</span></span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  def</span><br><span class="line">    .done(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">      data = <span class="built_in">JSON</span>.parse(data);</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`My name is <span class="subst">$&#123;data.name&#125;</span>, and <span class="subst">$&#123;data.age&#125;</span> years old.`</span>);</span><br><span class="line">    &#125;)</span><br><span class="line">    .fail(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(err);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="JSONP-缺点"><a href="#JSONP-缺点" class="headerlink" title="JSONP 缺点"></a>JSONP 缺点</h2><ul><li>只支持 GET 请求</li><li>只支持跨域 HTTP 请求这种情况，不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题</li><li>调用失败的时候不会返回各种 HTTP 状态码。</li><li>安全性，万一假如提供 JSONP 的服务存在页面注入漏洞，即它返回的 javascript 的内容被人控制的。</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://juejin.im/post/5e76bd516fb9a07cce750746#heading-67" target="_blank" rel="noopener">什么是跨域？浏览器如何拦截响应？如何解决</a></li><li><a href="https://segmentfault.com/a/1190000009773724" target="_blank" rel="noopener">jsonp 跨域原理分析</a> - 分析了 Ajax 和 jsonp 之间的关系</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;JSONP 请求本质上是利用了 “Ajax 请求会受到同源策略限制，而 script 标签请求不会” 这一点来绕过同源策略。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="网络知识" scheme="https://www.yangtao.site/categories/%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86/"/>
    
    
      <category term="http" scheme="https://www.yangtao.site/tags/http/"/>
    
  </entry>
  
  <entry>
    <title>Node 中间件原理</title>
    <link href="https://www.yangtao.site/2020/03/20/node-middleware/"/>
    <id>https://www.yangtao.site/2020/03/20/node-middleware/</id>
    <published>2020-03-20T09:59:34.000Z</published>
    <updated>2020-03-20T10:06:45.464Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Koa 和 Express 的设计风格非常类似，底层也都是共用的同一套 HTTP 基础库，但是有几个显著的区别，除了上面提到的默认异步解决方案之外，主要的特点还有下面几个。</p></blockquote><a id="more"></a><h2 id="Middleware"><a href="#Middleware" class="headerlink" title="Middleware"></a>Middleware</h2><p>Koa 的中间件和 Express 不同，Koa 选择了洋葱圈模型。</p><p><img src="https://ae01.alicdn.com/kf/H6d60d151712d4e1e8dfc887f5839c7ecz.png" alt="中间件洋葱图"></p><p>所有的请求经过一个中间件的时候都会执行两次，对比 Express 形式的中间件，Koa 的模型可以非常方便的实现后置处理逻辑，对比 Koa 和 Express 的 Compress 中间件就可以明显的感受到 Koa 中间件模型的优势。</p><h2 id="Context"><a href="#Context" class="headerlink" title="Context"></a>Context</h2><p>和 Express 只有 Request 和 Response 两个对象不同，Koa 增加了一个 Context 的对象，作为这次请求的上下文对象（在 Koa 1 中为中间件的 this，在 Koa 2 中作为中间件的第一个参数传入）。</p><p>我们可以将一次请求相关的上下文都挂载到这个对象上。类似 traceId 这种需要贯穿整个请求（在后续任何一个地方进行其他调用都需要用到）的属性就可以挂载上去。相较于 request 和 response 而言更加符合语义。</p><p>同时 Context 上也挂载了 Request 和 Response 两个对象。和 Express 类似，这两个对象都提供了大量的便捷方法辅助开发，例如</p><ul><li>get request.query</li><li>get request.hostname</li><li>set response.body</li><li>set response.status</li></ul><h2 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h2><p>通过同步方式编写异步代码带来的另外一个非常大的好处就是异常处理非常自然，使用 <code>try catch</code> 就可以将按照规范编写的代码中的所有错误都捕获到。这样我们可以很便捷的编写一个自定义的错误处理中间件。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">onerror</span>(<span class="params">ctx, next</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">await</span> next();</span><br><span class="line">  &#125; <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">    ctx.app.emit(<span class="string">"error"</span>, err);</span><br><span class="line">    ctx.body = <span class="string">"server error"</span>;</span><br><span class="line">    ctx.status = err.status || <span class="number">500</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>只需要将这个中间件放在其他中间件之前，就可以捕获它们所有的同步或者异步代码中抛出的异常了。</p><p>注：以上所有皆来自<a href="https://eggjs.org/zh-cn/intro/egg-and-koa.html" target="_blank" rel="noopener">Egg.js 与 Koa</a></p><h2 id="中间件模拟"><a href="#中间件模拟" class="headerlink" title="中间件模拟"></a>中间件模拟</h2><h3 id="Express"><a href="#Express" class="headerlink" title="Express"></a>Express</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">"http"</span>);</span><br><span class="line"><span class="keyword">const</span> slice = <span class="built_in">Array</span>.prototype.slice;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LikeExpress</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">    <span class="comment">// 存放中间件的列表</span></span><br><span class="line">    <span class="keyword">this</span>.routes = &#123;</span><br><span class="line">      all: [], <span class="comment">// app.use(...)</span></span><br><span class="line">      get: [], <span class="comment">// app.get(...)</span></span><br><span class="line">      post: [] <span class="comment">// app.post(...)</span></span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  register(path) &#123;</span><br><span class="line">    <span class="keyword">const</span> info = &#123;&#125;;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> path === <span class="string">"string"</span>) &#123;</span><br><span class="line">      info.path = path;</span><br><span class="line">      <span class="comment">// 从第二个参数开始，转换为数组，存入 stack</span></span><br><span class="line">      info.stack = slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      info.path = <span class="string">"/"</span>;</span><br><span class="line">      <span class="comment">// 从第一个参数开始，转换为数组，存入 stack</span></span><br><span class="line">      info.stack = slice.call(<span class="built_in">arguments</span>, <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> info;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  use() &#123;</span><br><span class="line">    <span class="keyword">const</span> info = <span class="keyword">this</span>.register.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line">    <span class="keyword">this</span>.routes.all.push(info);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  get() &#123;</span><br><span class="line">    <span class="keyword">const</span> info = <span class="keyword">this</span>.register.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line">    <span class="keyword">this</span>.routes.get.push(info);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  post() &#123;</span><br><span class="line">    <span class="keyword">const</span> info = <span class="keyword">this</span>.register.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line">    <span class="keyword">this</span>.routes.post.push(info);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  match(method, url) &#123;</span><br><span class="line">    <span class="keyword">let</span> stack = [];</span><br><span class="line">    <span class="keyword">if</span> (url === <span class="string">"/favicon.ico"</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> stack;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取 routes</span></span><br><span class="line">    <span class="keyword">let</span> curRoutes = [];</span><br><span class="line">    curRoutes = curRoutes.concat(<span class="keyword">this</span>.routes.all);</span><br><span class="line">    curRoutes = curRoutes.concat(<span class="keyword">this</span>.routes[method]);</span><br><span class="line"></span><br><span class="line">    curRoutes.forEach(<span class="function"><span class="params">routeInfo</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (url.indexOf(routeInfo.path) === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// url === '/api/get-cookie' 且 routeInfo.path === '/'</span></span><br><span class="line">        <span class="comment">// url === '/api/get-cookie' 且 routeInfo.path === '/api'</span></span><br><span class="line">        <span class="comment">// url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie'</span></span><br><span class="line">        stack = stack.concat(routeInfo.stack);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> stack;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 核心的 next 机制</span></span><br><span class="line">  handle(req, res, stack) &#123;</span><br><span class="line">    <span class="keyword">const</span> next = <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 class="keyword">const</span> middleware = stack.shift();</span><br><span class="line">      <span class="keyword">if</span> (middleware) &#123;</span><br><span class="line">        <span class="comment">// 执行中间件函数</span></span><br><span class="line">        middleware(req, res, next);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    next();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  callback() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">      res.json = <span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">        res.setHeader(<span class="string">"Content-type"</span>, <span class="string">"application/json"</span>);</span><br><span class="line">        res.end(<span class="built_in">JSON</span>.stringify(data));</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="keyword">const</span> url = req.url;</span><br><span class="line">      <span class="keyword">const</span> method = req.method.toLowerCase();</span><br><span class="line"></span><br><span class="line">      <span class="keyword">const</span> resultList = <span class="keyword">this</span>.match(method, url);</span><br><span class="line">      <span class="keyword">this</span>.handle(req, res, resultList);</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  listen(...args) &#123;</span><br><span class="line">    <span class="keyword">const</span> server = http.createServer(<span class="keyword">this</span>.callback());</span><br><span class="line">    server.listen(...args);</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="built_in">module</span>.exports = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> LikeExpress();</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="Koa"><a href="#Koa" class="headerlink" title="Koa"></a>Koa</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">"http"</span>);</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">compose</span>(<span class="params">middlewareList</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">ctx</span>) </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">dispatch</span>(<span class="params">i</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">const</span> fn = middlewareList[i];</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(</span><br><span class="line">          fn(ctx, dispatch.bind(<span class="literal">null</span>, i + <span class="number">1</span>)) <span class="comment">// promise</span></span><br><span class="line">        );</span><br><span class="line">      &#125; <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(err);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dispatch(<span class="number">0</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="class"><span class="keyword">class</span> <span class="title">LikeKoa2</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">    <span class="keyword">this</span>.middlewareList = [];</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  use(fn) &#123;</span><br><span class="line">    <span class="keyword">this</span>.middlewareList.push(fn);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  createContext(req, res) &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = &#123;</span><br><span class="line">      req,</span><br><span class="line">      res</span><br><span class="line">    &#125;;</span><br><span class="line">    ctx.query = req.query;</span><br><span class="line">    <span class="keyword">return</span> ctx;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  handleRequest(ctx, fn) &#123;</span><br><span class="line">    <span class="keyword">return</span> fn(ctx);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  callback() &#123;</span><br><span class="line">    <span class="keyword">const</span> fn = compose(<span class="keyword">this</span>.middlewareList);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> ctx = <span class="keyword">this</span>.createContext(req, res);</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.handleRequest(ctx, fn);</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  listen(...args) &#123;</span><br><span class="line">    <span class="keyword">const</span> server = http.createServer(<span class="keyword">this</span>.callback());</span><br><span class="line">    server.listen(...args);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = LikeKoa2;</span><br></pre></td></tr></table></figure><h2 id="项目实践"><a href="#项目实践" class="headerlink" title="项目实践"></a>项目实践</h2><p><a href="https://github.com/yangtao2o/node-blog-express-koa2" target="_blank" rel="noopener">Node.js 从零开发 web server博客项目，并使用express、koa2重构</a></p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Koa 和 Express 的设计风格非常类似，底层也都是共用的同一套 HTTP 基础库，但是有几个显著的区别，除了上面提到的默认异步解决方案之外，主要的特点还有下面几个。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="Nodejs" scheme="https://www.yangtao.site/categories/Nodejs/"/>
    
    
      <category term="Nodejs" scheme="https://www.yangtao.site/tags/Nodejs/"/>
    
  </entry>
  
  <entry>
    <title>Cookie、Session、LocalStorage、SessionStorage</title>
    <link href="https://www.yangtao.site/2020/03/20/cookie-session/"/>
    <id>https://www.yangtao.site/2020/03/20/cookie-session/</id>
    <published>2020-03-20T04:08:07.000Z</published>
    <updated>2020-03-31T03:30:13.505Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Session 是在服务端保存的一个数据结构，用来跟踪用户的状态，这个数据可以保存在集群、数据库、文件中；<br>Cookie 是客户端保存用户信息的一种机制，用来记录用户的一些信息，也是实现 Session 的一种方式；<br>LocalStorage 是 Web Storage API 的一种类型，能在浏览器端存储键值对数据；<br>SessionStorage 只存储当前会话页(tab 页)的数据，一旦用户关闭当前页或者浏览器，数据就自动被清除掉了。</p></blockquote><a id="more"></a><h2 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h2><p>在网站中，http 请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后，第二次请求服务器依然不能知道当前请求是哪个用户。</p><p>cookie 的出现就是为了解决这个问题，第一次登录后服务器返回一些数据（cookie）给浏览器，然后浏览器保存在本地，当该用户发送第二次请求的时候，就会自动的把上次请求存储的 cookie 数据自动的携带给服务器，服务器通过浏览器携带的数据就能判断当前用户是哪个了。</p><p>cookie 存储的数据量有限，不同的浏览器有不同的存储大小，但一般不超过 4KB。因此使用 cookie 只能存储一些小量的数据。</p><p><strong>Cookie 的优点：</strong></p><ul><li>能用于和服务端通信</li><li>当 cookie 快要自动过期时，我们可以重新设置而不是删除</li></ul><p><strong>Cookie 缺点：</strong></p><ul><li>增加了文档传输的负载</li><li>只能存储少量的数据</li><li>只能存储字符串</li><li>潜在的 安全问题（通过服务端设置 httpOnly）</li></ul><p>解决办法：Web Storage API (Local and Session Storage)。</p><p>创建，读取，更新和删除 Cookie：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 读取</span></span><br><span class="line"><span class="built_in">document</span>.cookie;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改/更新</span></span><br><span class="line"><span class="built_in">document</span>.cookie = <span class="string">"testname=Yangtao"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除，设置expires的过期时间</span></span><br><span class="line"><span class="built_in">document</span>.cookie = <span class="string">"testname=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"</span>;</span><br></pre></td></tr></table></figure><p>客户端设置：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> getAndSetCookie = &#123;</span><br><span class="line">    init: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">this</span>.checkCookie();</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 设置cookie，同一域名都可获取</span></span><br><span class="line">    setCookie: <span class="function"><span class="keyword">function</span>(<span class="params">name, value, exdays</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> exdays = exdays || <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">var</span> d = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">      d.setTime(d.getTime() + exdays * <span class="number">24</span> * <span class="number">60</span> * <span class="number">60</span> * <span class="number">1000</span>);</span><br><span class="line">      <span class="built_in">document</span>.cookie =</span><br><span class="line">        name + <span class="string">"="</span> + <span class="built_in">escape</span>(value) + <span class="string">";path=/;expires="</span> + d.toGMTString();</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 读取cookies，判断是否存在设置的name</span></span><br><span class="line">    getCookie: <span class="function"><span class="keyword">function</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> name = name + <span class="string">"="</span>;</span><br><span class="line">      <span class="keyword">var</span> ca = <span class="built_in">document</span>.cookie.split(<span class="string">";"</span>);</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; ca.length; i++) &#123;</span><br><span class="line">        <span class="keyword">var</span> c = ca[i].trim();</span><br><span class="line">        <span class="keyword">if</span> (c.indexOf(name) == <span class="number">0</span>) &#123;</span><br><span class="line">          <span class="keyword">return</span> c.substring(name.length, c.length);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    checkCookie: <span class="function"><span class="keyword">function</span>(<span class="params">pc</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> user = <span class="keyword">this</span>.getCookie(<span class="string">"msgCookie"</span>);</span><br><span class="line">      <span class="keyword">if</span> (user == <span class="string">""</span>) &#123;</span><br><span class="line">        <span class="keyword">this</span>.setCookie(<span class="string">"msgCookie"</span>, <span class="string">"msgCookie"</span>, <span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="comment">// init</span></span><br><span class="line">  getAndSetCookie.init();</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><p>Node 设置：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取 cookie 过期时间</span></span><br><span class="line"><span class="keyword">const</span> getCookieExpires = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line"><span class="keyword">const</span> d = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">d.setTime(d.getTime() + <span class="number">24</span> * <span class="number">60</span> * <span class="number">60</span> * <span class="number">1000</span>);</span><br><span class="line"><span class="keyword">return</span> d.toGMTString();</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">res.setHeader(</span><br><span class="line"><span class="string">"Set-Cookie"</span>,</span><br><span class="line"><span class="string">`username=<span class="subst">$&#123;data.username&#125;</span>; path=/; httpOnly; expires=<span class="subst">$&#123;getCookieExpires()&#125;</span>`</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解析 cookie</span></span><br><span class="line">req.cookie = &#123;&#125;;</span><br><span class="line"><span class="keyword">const</span> cookieStr = req.headers.cookie || <span class="string">""</span>; <span class="comment">// "k1=v1; k2=v2; k3=v3"</span></span><br><span class="line">cookieStr.split(<span class="string">";"</span>).forEach(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (!item) &#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> str = item.split(<span class="string">"="</span>);</span><br><span class="line">  <span class="keyword">const</span> key = str[<span class="number">0</span>].trim();</span><br><span class="line">  <span class="keyword">const</span> val = str[<span class="number">1</span>].trim();</span><br><span class="line"></span><br><span class="line">  req.cookie[key] = val;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="Session"><a href="#Session" class="headerlink" title="Session"></a>Session</h2><p>session 和 cookie 的作用有点类似，都是为了存储用户相关的信息。不同的是，cookie 是存储在本地浏览器，而 session 存储在服务器。</p><p>存储在服务器的数据会更加的安全，不容易被窃取。但存储在服务器也有一定的弊端，就是会占用服务器的资源，所以就用到了 redis。</p><p><strong>Session 的问题：</strong></p><ul><li>session 是一个变量存在 nodejs 进程内存中</li><li>进程内存有限，访问量过大，内存容易暴增</li><li>线上多为多进程，进程间内存无法共享</li><li>可以借助 redis 解决</li></ul><h2 id="Cookie-Session"><a href="#Cookie-Session" class="headerlink" title="Cookie+Session"></a>Cookie+Session</h2><p>cookie 和 session 的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里，一般有两种存储方式：</p><p><strong>1、存储在服务端</strong></p><p>通过 cookie 存储一个 session_id，然后具体的数据则是保存在 session 中。如果用户已经登录，则服务器会在 cookie 中保存一个 session_id，下次再次请求的时候，会把该 session_id 携带上来，服务器根据 session_id 在 session 库中获取用户的 session 数据。就能知道该用户到底是谁，以及之前保存的一些状态信息。这种专业术语叫做 <code>server side session</code>。</p><p><strong>2、将 session 数据加密，然后存储在 cookie 中</strong></p><p>这种专业术语叫做 <code>client side session</code>。</p><p><strong>总结：</strong></p><ul><li>存储在浏览器的一段字符串</li><li>跨域不共享</li><li>可以存储结构化数据，格式为：”k1=v1; k2=v2; k3=v3”</li><li>浏览器可以使用 JavaScript 修改 cookie（有限制）</li><li>每次发送 http 请求，都将请求域的 cookie 一起发给 server</li><li>server 可以修改 cookie，并返给浏览器</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> SESSION_DATA = &#123;&#125;;</span><br><span class="line"><span class="keyword">let</span> needSetCookie = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> serverHandle = <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 解析 session</span></span><br><span class="line">  <span class="keyword">let</span> userId = req.cookie.userid;</span><br><span class="line">  <span class="keyword">if</span> (userId) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!SESSION_DATA[userId]) &#123;</span><br><span class="line">      SESSION_DATA[userId] = &#123;&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    needSetCookie = <span class="literal">true</span>;</span><br><span class="line">    userId = <span class="string">`<span class="subst">$&#123;<span class="built_in">Date</span>.now()&#125;</span>_<span class="subst">$&#123;<span class="built_in">Math</span>.random()&#125;</span>`</span>;</span><br><span class="line">    SESSION_DATA[userId] = &#123;&#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  req.session = SESSION_DATA[userId];</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 操作 cookie</span></span><br><span class="line">  <span class="keyword">if</span> (needSetCookie) &#123;</span><br><span class="line">    res.setHeader(</span><br><span class="line">      <span class="string">"Set-Cookie"</span>,</span><br><span class="line">      <span class="string">`userid=<span class="subst">$&#123;userId&#125;</span>; path=/; httpOnly; expires=<span class="subst">$&#123;getCookieExpires()&#125;</span>`</span></span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 获取username并赋值</span></span><br><span class="line">  <span class="keyword">if</span> (data.username) &#123;</span><br><span class="line">    req.session.username = data.username;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> SuccessModel();</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 然后返回</span></span><br><span class="line">  <span class="keyword">if</span> (req.session.username) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(</span><br><span class="line">      <span class="keyword">new</span> SuccessModel(&#123;</span><br><span class="line">        session: req.session</span><br><span class="line">      &#125;)</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>详细内容：</strong><a href="https://github.com/yangtao2o/node-blog-express-koa2/blob/master/docs/signin.md" target="_blank" rel="noopener">登录</a></p><h2 id="LocalStorage"><a href="#LocalStorage" class="headerlink" title="LocalStorage"></a>LocalStorage</h2><p>Local Storage 是 Web Storage API 的一种类型，能在浏览器端存储键值对数据。</p><p>Local Storage 因提供了更直观和安全的 API 来存储简单的数据，被视为替代 Cookies 的一种解决方案。从技术上说，尽管 Local Storage 只能存储字符串，但是它也是可以存储字符串化的 JSON 数据。这就意味着，Local Storage 能比 Cookies 存储更复杂的数据。</p><p>创建，读取，更新和删除 Local Storage：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Create</span></span><br><span class="line"><span class="keyword">const</span> user = &#123; <span class="attr">name</span>: <span class="string">"Bob"</span>, <span class="attr">age</span>: <span class="number">25</span> &#125;;</span><br><span class="line">localStorage.setItem(<span class="string">"user"</span>, <span class="built_in">JSON</span>.stringify(user));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Read (Single)</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">JSON</span>.parse(localStorage.getItem(<span class="string">"user"</span>)));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Update</span></span><br><span class="line"><span class="keyword">const</span> updatedUser = &#123; <span class="attr">name</span>: <span class="string">"Bob"</span>, <span class="attr">age</span>: <span class="number">24</span> &#125;;</span><br><span class="line"></span><br><span class="line">localStorage.setItem(<span class="string">"user"</span>, <span class="built_in">JSON</span>.stringify(updatedUser));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Delete</span></span><br><span class="line">localStorage.removeItem(<span class="string">"user"</span>);</span><br></pre></td></tr></table></figure><p><strong>Local Storage 的优点：</strong>相比于 Cookie，其提供了更直观地接口来存储数据更安全,能存储更多数据。</p><p><strong>Local Storage 的缺点：</strong>只能存储字符串数据，浏览器支持<code>IE8+/Edge/Firefox 2+/Chrome/Safari 4+/Opera 11.5+(caniuse)</code></p><h2 id="SessionStorage"><a href="#SessionStorage" class="headerlink" title="SessionStorage"></a>SessionStorage</h2><p>Session Storage 和 Local Storage 非常类似，区别是 Session Storage 只存储当前会话页(tab 页)的数据，一旦用户关闭当前页或者浏览器，数据就自动被清除掉了。</p><p>创建，读取，更新和删除 Session Storage：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Create</span></span><br><span class="line"><span class="keyword">const</span> user = &#123; <span class="attr">name</span>: <span class="string">'Bob'</span>, <span class="attr">age</span>: <span class="number">25</span> &#125;sessionStorage.setItem(<span class="string">'user'</span>, <span class="built_in">JSON</span>.stringify(user))</span><br><span class="line"></span><br><span class="line"><span class="comment">// Read (Single)</span></span><br><span class="line"><span class="built_in">console</span>.log( <span class="built_in">JSON</span>.parse(sessionStorage.getItem(<span class="string">'user'</span>)) )</span><br><span class="line"></span><br><span class="line"><span class="comment">// Update</span></span><br><span class="line"><span class="keyword">const</span> updatedUser = &#123; <span class="attr">name</span>: <span class="string">'Bob'</span>, <span class="attr">age</span>: <span class="number">24</span> &#125;</span><br><span class="line"></span><br><span class="line">sessionStorage.setItem(<span class="string">'user'</span>, <span class="built_in">JSON</span>.stringify(updatedUser))</span><br><span class="line"></span><br><span class="line"><span class="comment">// Delete</span></span><br><span class="line">sessionStorage.removeItem(<span class="string">'user'</span>)</span><br></pre></td></tr></table></figure><p>sessionStorage、localStorage、cookie 都是在浏览器端存储的数据。</p><p>其中，sessionStorage 的概念很特别，引入了一个“浏览器窗口”的概念，sessionStorage 是在同源的同窗口中，始终存在的数据，也就是说只要这个浏览器窗口没有关闭，即使刷新页面或进入同源另一个页面，数据仍然存在，关闭窗口后 sessionStorage 就会被销毁，同时“独立”打开的不同窗口，即使同一页面，sessionStorage 对象也是不同的。</p><h2 id="客户端存储方式异同"><a href="#客户端存储方式异同" class="headerlink" title="客户端存储方式异同"></a>客户端存储方式异同</h2><p><strong>cookie，sessionStorage 和 localStorage 的区别？</strong></p><ul><li>cookie 是网站为了标示用户身份而储存在用户本地终端（Client Side）上 的数据（通常经过加密）- cookie 数据始终在同源的 http 请求中携带（即使不需要），记会在浏 览器和服务器间来回传递</li><li>sessionStorage 和 localStorage 不会自动把数据发给服务器，仅在本地 保存</li></ul><p><strong>存储大小：</strong></p><ul><li>cookie 数据大小不能超过 4k</li><li>sessionStorage 和 localStorage 虽然也有存储大小的限制，但比 cookie 大得多，可以达到 5M 或更大</li></ul><p><strong>有效期（生命周期）：</strong></p><ul><li>localStorage: 存储持久数据，浏览器关闭后数据不丢失，除非主动删除数据</li><li>sessionStorage: 数据在当前浏览器窗口关闭后自动删除</li><li>cookie: 设置的 cookie 过期时间之前一直有效，即使窗口或浏览器关闭</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>自己博客项目中<a href="https://github.com/yangtao2o/node-blog-express-koa2/blob/master/docs/signin.md" target="_blank" rel="noopener">什么是 cookie</a></li><li><a href="https://www.zhihu.com/question/19786827" target="_blank" rel="noopener">COOKIE 和 SESSION 有什么区别？</a></li><li><a href="https://baijiahao.baidu.com/s?id=1652697788741886713&amp;wfr=spider&amp;for=pc" target="_blank" rel="noopener">浅谈 cookie，sessionStorage，localStorage</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Session 是在服务端保存的一个数据结构，用来跟踪用户的状态，这个数据可以保存在集群、数据库、文件中；&lt;br&gt;Cookie 是客户端保存用户信息的一种机制，用来记录用户的一些信息，也是实现 Session 的一种方式；&lt;br&gt;LocalStorage 是 Web Storage API 的一种类型，能在浏览器端存储键值对数据；&lt;br&gt;SessionStorage 只存储当前会话页(tab 页)的数据，一旦用户关闭当前页或者浏览器，数据就自动被清除掉了。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="网络知识" scheme="https://www.yangtao.site/categories/%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86/"/>
    
    
      <category term="http" scheme="https://www.yangtao.site/tags/http/"/>
    
      <category term="web" scheme="https://www.yangtao.site/tags/web/"/>
    
      <category term="cookie" scheme="https://www.yangtao.site/tags/cookie/"/>
    
  </entry>
  
  <entry>
    <title>Understanding JavaScript Bind ()</title>
    <link href="https://www.yangtao.site/2020/03/16/js-bind/"/>
    <id>https://www.yangtao.site/2020/03/16/js-bind/</id>
    <published>2020-03-16T09:09:08.000Z</published>
    <updated>2020-03-16T11:04:08.368Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>如何将“this”的上下文保留在另一个函数中？我们可能会需要使用<code>Function.prototype.bind()</code>。资料地址：<a href="https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/" target="_blank" rel="noopener">Understanding JavaScript Bind ()</a></p></blockquote><a id="more"></a>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;如何将“this”的上下文保留在另一个函数中？我们可能会需要使用&lt;code&gt;Function.prototype.bind()&lt;/code&gt;。资料地址：&lt;a href=&quot;https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Understanding JavaScript Bind ()&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>React 设计思想【转载】</title>
    <link href="https://www.yangtao.site/2020/03/16/react-design-ideass/"/>
    <id>https://www.yangtao.site/2020/03/16/react-design-ideass/</id>
    <published>2020-03-16T03:24:11.000Z</published>
    <updated>2020-03-16T03:28:38.441Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>译者序：本文是 React 核心开发者、有 React API 终结者之称的 Sebastian Markbåge 撰写，阐述了他设计 React 的初衷。阅读此文，你能站在更高的高度思考 React 的过去、现在和未来。<br>原文地址：<a href="https://github.com/reactjs/react-basic" target="_blank" rel="noopener">https://github.com/reactjs/react-basic</a></p></blockquote><a id="more"></a><p>我写此文是想正式地阐述我心中 React 的<a href="http://baike.baidu.com/view/2333986.htm" target="_blank" rel="noopener">心智模型</a>。目的是解释为什么我们会这样设计 React，同时你也可以根据这些论点反推出 React。</p><p>不可否认，此文中的部分论据或前提尚存争议，而且部分示例的设计可能存在 bug 或疏忽。这只是正式确定它的最初阶段。如果你有更好的完善它的想法可以随时提交 pull request。本文不会介绍框架细节中的奇技淫巧，相信这样能提纲挈领，让你看清 React 由简单到复杂的设计过程。</p><p>React.js 的真实实现中充满了具体问题的解决方案，渐进式的解法，算法优化，历史遗留代码，debug 工具以及其他一些可以让它真的具有高可用性的内容。这些代码可能并不稳定，因为未来浏览器的变化和功能权重的变化随时面临改变。所以具体的代码很难彻底解释清楚。</p><p>我偏向于选择一种我能完全 hold 住的简洁的心智模型来作介绍。</p><h2 id="变换（Transformation）"><a href="#变换（Transformation）" class="headerlink" title="变换（Transformation）"></a>变换（Transformation）</h2><p>设计 React 的核心前提是认为 UI 只是把数据通过映射关系变换成另一种形式的数据。同样的输入必会有同样的输出。这恰好就是纯函数。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">NameBox</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123; <span class="attr">fontWeight</span>: <span class="string">"bold"</span>, <span class="attr">labelContent</span>: name &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&apos;Sebastian Markbåge&apos; -&gt;</span><br><span class="line">&#123; fontWeight: &apos;bold&apos;, labelContent: &apos;Sebastian Markbåge&apos; &#125;;</span><br></pre></td></tr></table></figure><h2 id="抽象（Abstraction）"><a href="#抽象（Abstraction）" class="headerlink" title="抽象（Abstraction）"></a>抽象（Abstraction）</h2><p>你不可能仅用一个函数就能实现复杂的 UI。重要的是，你需要把 UI 抽象成多个隐藏内部细节，又可复用的函数。通过在一个函数中调用另一个函数来实现复杂的 UI，这就是抽象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyUserBox</span>(<span class="params">user</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    borderStyle: <span class="string">"1px solid blue"</span>,</span><br><span class="line">    childContent: [<span class="string">"Name: "</span>, NameBox(user.firstName + <span class="string">" "</span> + user.lastName)]</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123; firstName: &apos;Sebastian&apos;, lastName: &apos;Markbåge&apos; &#125; -&gt;</span><br><span class="line">&#123;</span><br><span class="line">  borderStyle: &apos;1px solid blue&apos;,</span><br><span class="line">  childContent: [</span><br><span class="line">    &apos;Name: &apos;,</span><br><span class="line">    &#123; fontWeight: &apos;bold&apos;, labelContent: &apos;Sebastian Markbåge&apos; &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="组合（Composition）"><a href="#组合（Composition）" class="headerlink" title="组合（Composition）"></a>组合（Composition）</h2><p>为了真正达到重用的特性，只重用叶子然后每次都为他们创建一个新的容器是不够的。你还需要可以包含其他抽象的容器再次进行组合。我理解的“组合”就是将两个或者多个不同的抽象合并为一个。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBox</span>(<span class="params">children</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    borderStyle: <span class="string">"1px solid blue"</span>,</span><br><span class="line">    children: children</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UserBox</span>(<span class="params">user</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox([<span class="string">"Name: "</span>, NameBox(user.firstName + <span class="string">" "</span> + user.lastName)]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="状态（State）"><a href="#状态（State）" class="headerlink" title="状态（State）"></a>状态（State）</h2><p>UI 不单单是对服务器端或业务逻辑状态的复制。实际上还有很多状态是针对具体的渲染目标。举个例子，在一个 text field 中打字。它不一定要复制到其他页面或者你的手机设备。滚动位置这个状态是一个典型的你几乎不会复制到多个渲染目标的。</p><p>我们倾向于使用不可变的数据模型。我们把可以改变 state 的函数串联起来作为原点放置在顶层。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyNameBox</span>(<span class="params">user, likes, onClick</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox([</span><br><span class="line">    <span class="string">"Name: "</span>,</span><br><span class="line">    NameBox(user.firstName + <span class="string">" "</span> + user.lastName),</span><br><span class="line">    <span class="string">"Likes: "</span>,</span><br><span class="line">    LikeBox(likes),</span><br><span class="line">    LikeButton(onClick)</span><br><span class="line">  ]);</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><br><span class="line"><span class="keyword">var</span> likes = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addOneMoreLike</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  likes++;</span><br><span class="line">  rerender();</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><br><span class="line">FancyNameBox(</span><br><span class="line">  &#123; <span class="attr">firstName</span>: <span class="string">"Sebastian"</span>, <span class="attr">lastName</span>: <span class="string">"Markbåge"</span> &#125;,</span><br><span class="line">  likes,</span><br><span class="line">  addOneMoreLike</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p><em>注意：本例更新状态时会带来副作用（addOneMoreLike 函数中）。我实际的想法是当一个“update”传入时我们返回下一个版本的状态，但那样会比较复杂。此示例待更新</em></p><h2 id="Memoization"><a href="#Memoization" class="headerlink" title="Memoization"></a>Memoization</h2><p>对于纯函数，使用相同的参数一次次调用未免太浪费资源。我们可以创建一个函数的 memorized 版本，用来追踪最后一个参数和结果。这样如果我们继续使用同样的值，就不需要反复执行它了。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">memoize</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> cachedArg;</span><br><span class="line">  <span class="keyword">var</span> cachedResult;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">arg</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (cachedArg === arg) &#123;</span><br><span class="line">      <span class="keyword">return</span> cachedResult;</span><br><span class="line">    &#125;</span><br><span class="line">    cachedArg = arg;</span><br><span class="line">    cachedResult = fn(arg);</span><br><span class="line">    <span class="keyword">return</span> cachedResult;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> MemoizedNameBox = memoize(NameBox);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">NameAndAgeBox</span>(<span class="params">user, currentTime</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox([</span><br><span class="line">    <span class="string">"Name: "</span>,</span><br><span class="line">    MemoizedNameBox(user.firstName + <span class="string">" "</span> + user.lastName),</span><br><span class="line">    <span class="string">"Age in milliseconds: "</span>,</span><br><span class="line">    currentTime - user.dateOfBirth</span><br><span class="line">  ]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="列表（Lists）"><a href="#列表（Lists）" class="headerlink" title="列表（Lists）"></a>列表（Lists）</h2><p>大部分 UI 都是展示列表数据中不同 item 的列表结构。这是一个天然的层级。</p><p>为了管理列表中的每一个 item 的 state ，我们可以创造一个 Map 容纳具体 item 的 state。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UserList</span>(<span class="params">users, likesPerUser, updateUserLikes</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> users.map(<span class="function"><span class="params">user</span> =&gt;</span></span><br><span class="line">    FancyNameBox(user, likesPerUser.get(user.id), () =&gt;</span><br><span class="line">      updateUserLikes(user.id, likesPerUser.get(user.id) + <span class="number">1</span>)</span><br><span class="line">    )</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> likesPerUser = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">updateUserLikes</span>(<span class="params">id, likeCount</span>) </span>&#123;</span><br><span class="line">  likesPerUser.set(id, likeCount);</span><br><span class="line">  rerender();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">UserList(data.users, likesPerUser, updateUserLikes);</span><br></pre></td></tr></table></figure><p><em>注意：现在我们向 FancyNameBox 传了多个不同的参数。这打破了我们的 memoization 因为我们每次只能存储一个值。更多相关内容在下面。</em></p><h2 id="连续性（Continuations）"><a href="#连续性（Continuations）" class="headerlink" title="连续性（Continuations）"></a>连续性（Continuations）</h2><p>不幸的是，自从 UI 中有太多的列表，明确的管理就需要大量的重复性样板代码。</p><p>我们可以通过推迟一些函数的执行，进而把一些模板移出业务逻辑。比如，使用“柯里化”（JavaScript 中的 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind" target="_blank" rel="noopener"><code>bind</code></a>）。然后我们可以从核心的函数外面传递 state，这样就没有样板代码了。</p><p>下面这样并没有减少样板代码，但至少把它从关键业务逻辑中剥离。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyUserList</span>(<span class="params">users</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox(UserList.bind(<span class="literal">null</span>, users));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> box = FancyUserList(data.users);</span><br><span class="line"><span class="keyword">const</span> resolvedChildren = box.children(likesPerUser, updateUserLikes);</span><br><span class="line"><span class="keyword">const</span> resolvedBox = &#123;</span><br><span class="line">  ...box,</span><br><span class="line">  children: resolvedChildren</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="State-Map"><a href="#State-Map" class="headerlink" title="State Map"></a>State Map</h2><p>之前我们知道可以使用组合避免重复执行相同的东西这样一种重复模式。我们可以把执行和传递 state 逻辑挪动到被复用很多的低层级的函数中去。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBoxWithState</span>(<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">  children,</span></span></span><br><span class="line"><span class="function"><span class="params">  stateMap,</span></span></span><br><span class="line"><span class="function"><span class="params">  updateState</span></span></span><br><span class="line"><span class="function"><span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox(</span><br><span class="line">    children.map(<span class="function"><span class="params">child</span> =&gt;</span> child.continuation(</span><br><span class="line">      stateMap.get(child.key),</span><br><span class="line">      updateState</span><br><span class="line">    ))</span><br><span class="line">  );</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">UserList</span>(<span class="params">users</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> users.map(<span class="function"><span class="params">user</span> =&gt;</span> &#123;</span><br><span class="line">    continuation: FancyNameBox.bind(<span class="literal">null</span>, user),</span><br><span class="line">    key: user.id</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyUserList</span>(<span class="params">users</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBoxWithState.bind(<span class="literal">null</span>,</span><br><span class="line">    UserList(users)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> continuation = FancyUserList(data.users);</span><br><span class="line">continuation(likesPerUser, updateUserLikes);</span><br></pre></td></tr></table></figure><h2 id="Memoization-Map"><a href="#Memoization-Map" class="headerlink" title="Memoization Map"></a>Memoization Map</h2><p>一旦我们想在一个 memoization 列表中 memoize 多个 item 就会变得很困难。因为你需要制定复杂的缓存算法来平衡调用频率和内存占有率。</p><p>还好 UI 在同一个位置会相对的稳定。相同的位置一般每次都会接受相同的参数。这样以来，使用一个集合来做 memoization 是一个非常好用的策略。</p><p>我们可以用对待 state 同样的方式，在组合的函数中传递一个 memoization 缓存。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">memoize</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">arg, memoizationCache</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (memoizationCache.arg === arg) &#123;</span><br><span class="line">      <span class="keyword">return</span> memoizationCache.result;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> result = fn(arg);</span><br><span class="line">    memoizationCache.arg = arg;</span><br><span class="line">    memoizationCache.result = result;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBoxWithState</span>(<span class="params">children, stateMap, updateState, memoizationCache</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox(</span><br><span class="line">    children.map(<span class="function"><span class="params">child</span> =&gt;</span></span><br><span class="line">      child.continuation(</span><br><span class="line">        stateMap.get(child.key),</span><br><span class="line">        updateState,</span><br><span class="line">        memoizationCache.get(child.key)</span><br><span class="line">      )</span><br><span class="line">    )</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> MemoizedFancyNameBox = memoize(FancyNameBox);</span><br></pre></td></tr></table></figure><h2 id="代数效应（Algebraic-Effects）"><a href="#代数效应（Algebraic-Effects）" class="headerlink" title="代数效应（Algebraic Effects）"></a>代数效应（Algebraic Effects）</h2><p>多层抽象需要共享琐碎数据时，一层层传递数据非常麻烦。如果能有一种方式可以在多层抽象中快捷地传递数据，同时又不需要牵涉到中间层级，那该有多好。React 中我们把它叫做“context”。</p><p>有时候数据依赖并不是严格按照抽象树自上而下进行。举个例子，在布局算法中，你需要在实现他们的位置之前了解子节点的大小。</p><p>现在，这个例子有一点超纲。我会使用 <a href="http://math.andrej.com/eff/" target="_blank" rel="noopener">代数效应</a> 这个由我发起的 <a href="https://esdiscuss.org/topic/one-shot-delimited-continuations-with-effect-handlers" target="_blank" rel="noopener">ECMAScript 新特性提议</a>。如果你对函数式编程很熟悉，它们 在避免由 monad 强制引入的仪式一样的编码。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ThemeBorderColorRequest</span>(<span class="params"></span>) </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBox</span>(<span class="params">children</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> color = raise <span class="keyword">new</span> ThemeBorderColorRequest();</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    borderWidth: <span class="string">'1px'</span>,</span><br><span class="line">    borderColor: color,</span><br><span class="line">    children: children</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">BlueTheme</span>(<span class="params">children</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">try</span> &#123;</span><br><span class="line">    children();</span><br><span class="line">  &#125; <span class="keyword">catch</span> effect ThemeBorderColorRequest -&gt; [, continuation] &#123;</span><br><span class="line">    continuation(<span class="string">'blue'</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="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> BlueTheme(</span><br><span class="line">    FancyUserList.bind(<span class="literal">null</span>, data.users)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>原文地址：<a href="https://github.com/react-guide/react-basic" target="_blank" rel="noopener">React 设计思想</a></p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;译者序：本文是 React 核心开发者、有 React API 终结者之称的 Sebastian Markbåge 撰写，阐述了他设计 React 的初衷。阅读此文，你能站在更高的高度思考 React 的过去、现在和未来。&lt;br&gt;原文地址：&lt;a href=&quot;https://github.com/reactjs/react-basic&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/reactjs/react-basic&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="前端框架" scheme="https://www.yangtao.site/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/"/>
    
    
      <category term="react" scheme="https://www.yangtao.site/tags/react/"/>
    
  </entry>
  
  <entry>
    <title>Gulp 自动化构建</title>
    <link href="https://www.yangtao.site/2020/03/14/gulp-v4/"/>
    <id>https://www.yangtao.site/2020/03/14/gulp-v4/</id>
    <published>2020-03-14T04:18:36.000Z</published>
    <updated>2020-03-14T04:25:07.814Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Gulp 自动化构建的一些过程，以及其他插件的结合使用等</p></blockquote><a id="more"></a><h2 id="起步"><a href="#起步" class="headerlink" title="起步"></a>起步</h2><ul><li><a href="https://www.gulpjs.com.cn/docs/getting-started/quick-start/" target="_blank" rel="noopener">Gulp 快速入门指南</a></li><li><a href="https://github.com/yangtao2o/gulp-project/tree/master/src/mygulp" target="_blank" rel="noopener">Github Demo</a></li></ul><h3 id="导出"><a href="#导出" class="headerlink" title="导出"></a>导出</h3><p>项目根目录创建<code>gulpfile.js</code>:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">defaultTask</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"success"</span>);</span><br><span class="line">  cb();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.default = defaultTask;</span><br></pre></td></tr></table></figure><p>gulp 命令：</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">➜  gulp-project git:(master) ✗ gulp</span><br><span class="line">[17:06:29] Using gulpfile ~/Documents/GitHub/gulp-project/gulpfile.js</span><br><span class="line">[17:06:29] Starting <span class="string">'default'</span>...</span><br><span class="line">success</span><br><span class="line">[17:06:29] Finished <span class="string">'default'</span> after 2.67 ms</span><br></pre></td></tr></table></figure><h3 id="Gulpfile-分割"><a href="#Gulpfile-分割" class="headerlink" title="Gulpfile 分割"></a>Gulpfile 分割</h3><blockquote><p>Node 的模块解析功能允许你将 gulpfile.js’ 文件替换为同样命名为 gulpfile.js 的目录，该目录中包含了一个名为 index.js 的文件，该文件被当作 gulpfile.js 使用。并且，该目录中还可以包含各个独立的任务（task）模块。 — 官方文档</p></blockquote><p>创建 <code>gulpfile.js</code>目录，<code>gulpfile.js</code>目录下新建 <code>index.js</code></p><h3 id="组合任务"><a href="#组合任务" class="headerlink" title="组合任务"></a>组合任务</h3><ul><li>series() 允许将多个独立的任务组合为一个更大的操作</li><li>对于希望以最大并发来运行的任务（tasks），可以使用 parallel() 方法将它们组合起来</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; series, parallel &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clean</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</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">cssTranspile</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</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">cssMinify</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</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">jsTranspile</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</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">jsBundle</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</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">jsMinify</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</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">publish</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</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">build</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  cb();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.default = series(</span><br><span class="line">  clean,</span><br><span class="line">  parallel(cssTranspile, series(jsTranspile, jsBundle)),</span><br><span class="line">  parallel(cssMinify, jsMinify),</span><br><span class="line">  publish</span><br><span class="line">);</span><br></pre></td></tr></table></figure><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><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">[16:50:36] Starting <span class="string">'default'</span>...</span><br><span class="line">[16:50:36] Starting <span class="string">'clean'</span>...</span><br><span class="line">[16:50:36] Finished <span class="string">'clean'</span> after 606 μs</span><br><span class="line">[16:50:36] Starting <span class="string">'cssTranspile'</span>...</span><br><span class="line">[16:50:36] Finished <span class="string">'cssTranspile'</span> after 199 μs</span><br><span class="line">[16:50:36] Starting <span class="string">'jsTranspile'</span>...</span><br><span class="line">[16:50:36] Finished <span class="string">'jsTranspile'</span> after 131 μs</span><br><span class="line">[16:50:36] Starting <span class="string">'jsBundle'</span>...</span><br><span class="line">[16:50:36] Finished <span class="string">'jsBundle'</span> after 125 μs</span><br><span class="line">[16:50:36] Starting <span class="string">'cssMinify'</span>...</span><br><span class="line">[16:50:36] Starting <span class="string">'jsMinify'</span>...</span><br><span class="line">[16:50:36] Finished <span class="string">'cssMinify'</span> after 183 μs</span><br><span class="line">[16:50:36] Finished <span class="string">'jsMinify'</span> after 218 μs</span><br><span class="line">[16:50:36] Starting <span class="string">'publish'</span>...</span><br><span class="line">[16:50:36] Finished <span class="string">'publish'</span> after 115 μs</span><br><span class="line">[16:50:36] Finished <span class="string">'default'</span> after 4.55 ms</span><br></pre></td></tr></table></figure><p>当一个组合操作执行时，这个组合中的每一个任务每次被调用时都会被执行。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">// This is INCORRECT</span></span><br><span class="line"><span class="keyword">const</span> &#123; series, parallel &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> clean = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> css = series(clean, <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> javascript = series(clean, <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">exports.default = parallel(css, javascript);</span><br></pre></td></tr></table></figure><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><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">[17:02:45] Starting <span class="string">'default'</span>...</span><br><span class="line">[17:02:45] Starting <span class="string">'clean'</span>...</span><br><span class="line">[17:02:45] Starting <span class="string">'clean'</span>...</span><br><span class="line">[17:02:45] Finished <span class="string">'clean'</span> after 850 μs</span><br><span class="line">[17:02:45] Starting <span class="string">'&lt;anonymous&gt;'</span>...</span><br><span class="line">[17:02:45] Finished <span class="string">'clean'</span> after 2.04 ms</span><br><span class="line">[17:02:45] Starting <span class="string">'&lt;anonymous&gt;'</span>...</span><br><span class="line">[17:02:45] Finished <span class="string">'&lt;anonymous&gt;'</span> after 563 μs</span><br><span class="line">[17:02:45] Finished <span class="string">'&lt;anonymous&gt;'</span> after 438 μs</span><br><span class="line">[17:02:45] Finished <span class="string">'default'</span> after 7.67 ms</span><br></pre></td></tr></table></figure><p>重构：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">// This is INCORRECT</span></span><br><span class="line"><span class="keyword">const</span> &#123; series, parallel &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> clean = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> css = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> javascript = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">exports.default = series(clean, parallel(css, javascript));</span><br></pre></td></tr></table></figure><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><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[17:04:48] Starting <span class="string">'default'</span>...</span><br><span class="line">[17:04:48] Starting <span class="string">'clean'</span>...</span><br><span class="line">[17:04:48] Finished <span class="string">'clean'</span> after 546 μs</span><br><span class="line">[17:04:48] Starting <span class="string">'css'</span>...</span><br><span class="line">[17:04:48] Starting <span class="string">'javascript'</span>...</span><br><span class="line">[17:04:48] Finished <span class="string">'css'</span> after 242 μs</span><br><span class="line">[17:04:48] Finished <span class="string">'javascript'</span> after 285 μs</span><br><span class="line">[17:04:48] Finished <span class="string">'default'</span> after 3.4 ms</span><br></pre></td></tr></table></figure><h3 id="异步执行"><a href="#异步执行" class="headerlink" title="异步执行"></a>异步执行</h3><blockquote><p>Node 库以多种方式处理异步功能。最常见的模式是 error-first callbacks，但是你还可能会遇到 streams、promises、event emitters、child processes, 或 observables。gulp 任务（task）规范化了所有这些类型的异步功能。 — 官方文档</p></blockquote><ul><li>返回 stream</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; src, dest &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">streamTask</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> src(<span class="string">"*.js"</span>).pipe(dest(<span class="string">"output"</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.default = streamTask;</span><br></pre></td></tr></table></figure><p>然后发现，根目录下会看到 output 目录，及根目录下所有以 <code>*js</code>结尾的文件，包括目录。</p><ul><li>返回 promise</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">promiseTask</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(<span class="string">"the value is ignored"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.default = promiseTask;</span><br></pre></td></tr></table></figure><h3 id="返回-event-emitter"><a href="#返回-event-emitter" class="headerlink" title="返回 event emitter"></a>返回 event emitter</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; EventEmitter &#125; = <span class="built_in">require</span>(<span class="string">"events"</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">eventEmitterTask</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> emitter = <span class="keyword">new</span> EventEmitter();</span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> emitter.emit(<span class="string">"finish"</span>), <span class="number">250</span>);</span><br><span class="line">  <span class="keyword">return</span> emitter;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.default = eventEmitterTask;</span><br></pre></td></tr></table></figure><h3 id="使用-callback"><a href="#使用-callback" class="headerlink" title="使用 callback"></a>使用 callback</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">callbackTask</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// `cb()` should be called by some async work</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.default = callbackTask;</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 js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">asyncAwaitTask</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; version &#125; = fs.readFileSync(<span class="string">"package.json"</span>);</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"version"</span>);</span><br><span class="line">  <span class="keyword">await</span> <span class="built_in">Promise</span>.resolve(<span class="string">"some result"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.default = asyncAwaitTask;</span><br></pre></td></tr></table></figure><h3 id="使用插件"><a href="#使用插件" class="headerlink" title="使用插件"></a>使用插件</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; src, dest &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"><span class="keyword">const</span> uglify = <span class="built_in">require</span>(<span class="string">"gulp-uglify"</span>);</span><br><span class="line"><span class="keyword">const</span> rename = <span class="built_in">require</span>(<span class="string">"gulp-rename"</span>);</span><br><span class="line"></span><br><span class="line">exports.default = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> src(<span class="string">"src/assets/**/*.js"</span>)</span><br><span class="line">    .pipe(uglify())</span><br><span class="line">    .pipe(rename(&#123; <span class="attr">extname</span>: <span class="string">".min.js"</span> &#125;))</span><br><span class="line">    .pipe(dest(<span class="string">"output/"</span>));</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="browsersync"><a href="#browsersync" class="headerlink" title="browsersync"></a>browsersync</h3><ul><li><a href="http://www.browsersync.cn/docs/gulp/#gulp-install" target="_blank" rel="noopener">Browsersync + Gulp.js</a></li></ul><p>下载：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install browser-sync gulp --save-dev</span><br></pre></td></tr></table></figure><p>使用：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">const</span> &#123; task, watch &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"><span class="keyword">const</span> browserSync = <span class="built_in">require</span>(<span class="string">"browser-sync"</span>).create();</span><br><span class="line"><span class="keyword">const</span> reload = browserSync.reload;</span><br><span class="line"><span class="keyword">const</span> serve = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  browserSync.init(&#123;</span><br><span class="line">    server: &#123;</span><br><span class="line">      baseDir: <span class="string">"./src"</span>,</span><br><span class="line">      https: <span class="literal">true</span>,</span><br><span class="line">      directory: <span class="literal">true</span> <span class="comment">//从与目录列表的应用程序目录中的文件即成</span></span><br><span class="line">      <span class="comment">// index: "index.html"  //从应用程序目录中提供文件，指定特定文件名为索引</span></span><br><span class="line">    &#125;,</span><br><span class="line">    port: <span class="number">8080</span>,</span><br><span class="line">    notify: <span class="literal">false</span> <span class="comment">// 开启静默模式</span></span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  watch(<span class="string">"src/*.html"</span>).on(<span class="string">"change"</span>, reload);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">task(serve);</span><br></pre></td></tr></table></figure><p>本地使用：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; src, dest, watch, series, parallel &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"><span class="keyword">const</span> uglify = <span class="built_in">require</span>(<span class="string">"gulp-uglify"</span>);</span><br><span class="line"><span class="keyword">const</span> rename = <span class="built_in">require</span>(<span class="string">"gulp-rename"</span>);</span><br><span class="line"><span class="keyword">const</span> browserSync = <span class="built_in">require</span>(<span class="string">"browser-sync"</span>).create();</span><br><span class="line"><span class="keyword">const</span> reload = browserSync.reload;</span><br><span class="line"><span class="keyword">const</span> jsWatcher = watch(<span class="string">"src/**/*.js"</span>);</span><br><span class="line"><span class="keyword">const</span> cssWatcher = watch(<span class="string">"src/**/*.css"</span>);</span><br><span class="line"><span class="keyword">const</span> htmlWatcher = watch(<span class="string">"src/**/*.html"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> clean = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> css = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> javascript = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// body omitted</span></span><br><span class="line">  cb();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> serve = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  browserSync.init(&#123;</span><br><span class="line">    server: &#123;</span><br><span class="line">      baseDir: <span class="string">"./src"</span>,</span><br><span class="line">      <span class="comment">// https: true,</span></span><br><span class="line">      directory: <span class="literal">true</span> <span class="comment">//从与目录列表的应用程序目录中的文件即成，如果要指定文件，可注释掉</span></span><br><span class="line">      <span class="comment">// index: "index.html"  //从应用程序目录中提供文件，指定特定文件名为索引</span></span><br><span class="line">    &#125;,</span><br><span class="line">    port: <span class="number">8080</span>,</span><br><span class="line">    notify: <span class="literal">false</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"><span class="comment">// 监听 globs 并在发生更改时运行任务</span></span><br><span class="line">jsWatcher.on(<span class="string">"change"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">path, stats</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`File <span class="subst">$&#123;path&#125;</span> was changed`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">cssWatcher.on(<span class="string">"change"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">path, stats</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`File <span class="subst">$&#123;path&#125;</span> was changed`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">htmlWatcher.on(<span class="string">"change"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">path, stats</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`File <span class="subst">$&#123;path&#125;</span> was changed`</span>);</span><br><span class="line">  reload(); <span class="comment">// Reloading Browsers</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">exports.build = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> src(<span class="string">"src/assets/**/*.js"</span>)</span><br><span class="line">    .pipe(uglify())</span><br><span class="line">    .pipe(rename(&#123; <span class="attr">extname</span>: <span class="string">".min.js"</span> &#125;))</span><br><span class="line">    .pipe(dest(<span class="string">"output/"</span>));</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">exports.serve = serve;</span><br><span class="line"></span><br><span class="line">exports.default = series(clean, parallel(css, javascript));</span><br></pre></td></tr></table></figure><p>然后在根目录下的<code>package.json</code>，修改：</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">"scripts": &#123;</span><br><span class="line">  "dev": "gulp",</span><br><span class="line">  "build": "gulp build",</span><br><span class="line">  "serve": "gulp serve"</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><p>启动：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run serve</span><br></pre></td></tr></table></figure><h2 id="Gulp-SASS"><a href="#Gulp-SASS" class="headerlink" title="Gulp + SASS"></a>Gulp + SASS</h2><p>-<a href="https://www.sass.hk/install/" target="_blank" rel="noopener">如何安装 Sass</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install node-sass gulp-sass --save-dev</span><br></pre></td></tr></table></figure><p><code>gulpfile.js</code> 文件主要配置如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; src, dest, watch &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"><span class="keyword">const</span> sass = <span class="built_in">require</span>(<span class="string">"gulp-sass"</span>);</span><br><span class="line">sass.compiler = <span class="built_in">require</span>(<span class="string">"node-sass"</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">scss</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> src(scssGlobSrc)</span><br><span class="line">    .pipe(sass(&#123; <span class="attr">outputStyle</span>: <span class="string">"expanded"</span> &#125;).on(<span class="string">"error"</span>, sass.logError))</span><br><span class="line">    .pipe(dest(<span class="string">"dist/sass"</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.scss = scss;</span><br></pre></td></tr></table></figure><p>启动：<code>gulp scss</code>。</p><h2 id="Gulp-Browsersync"><a href="#Gulp-Browsersync" class="headerlink" title="Gulp + Browsersync"></a>Gulp + Browsersync</h2><ul><li><a href="http://www.browsersync.cn/docs/gulp/" target="_blank" rel="noopener">Browsersync + Gulp.js</a></li></ul><p>下载：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install browser-sync gulp --save-dev</span><br></pre></td></tr></table></figure><p>使用：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">const</span> &#123; task, watch &#125; = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"><span class="keyword">const</span> browserSync = <span class="built_in">require</span>(<span class="string">"browser-sync"</span>).create();</span><br><span class="line"><span class="keyword">const</span> reload = browserSync.reload;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> serve = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  browserSync.init(&#123;</span><br><span class="line">    server: &#123;</span><br><span class="line">      baseDir: <span class="string">"./src"</span>,</span><br><span class="line">      https: <span class="literal">true</span>,</span><br><span class="line">      directory: <span class="literal">true</span> <span class="comment">//从与目录列表的应用程序目录中的文件即成</span></span><br><span class="line">      <span class="comment">// index: "index.html"  //从应用程序目录中提供文件，指定特定文件名为索引</span></span><br><span class="line">    &#125;,</span><br><span class="line">    port: <span class="number">8080</span>,</span><br><span class="line">    notify: <span class="literal">false</span> <span class="comment">// 开启静默模式</span></span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  watch(<span class="string">"src/*.html"</span>).on(<span class="string">"change"</span>, reload);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">exports.serve = serve;</span><br></pre></td></tr></table></figure><p>运行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gulp serve</span><br></pre></td></tr></table></figure><h2 id="Gulp-TypeScript"><a href="#Gulp-TypeScript" class="headerlink" title="Gulp + TypeScript"></a>Gulp + TypeScript</h2><p>根目录下初始化：<code>tsc --init</code>。</p><p>安装依赖：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i -D typescript gulp-typescript</span><br></pre></td></tr></table></figure><p>配置：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ts = <span class="built_in">require</span>(<span class="string">"gulp-typescript"</span>);</span><br><span class="line"><span class="keyword">const</span> tsProject = ts.createProject(<span class="string">"tsconfig.json"</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">typescript</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> tsProject</span><br><span class="line">    .src()</span><br><span class="line">    .pipe(tsProject())</span><br><span class="line">    .js.pipe(dest(<span class="string">"dist/ts"</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">exports.typescript = typescript;</span><br></pre></td></tr></table></figure><p>src 目录下新建文件 <code>main.ts</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><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">class</span> Student &#123;</span><br><span class="line">  fullName: <span class="built_in">string</span>;</span><br><span class="line">  <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> firstName: <span class="built_in">string</span>, <span class="keyword">public</span> lastName: <span class="built_in">string</span></span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.fullName = <span class="string">`<span class="subst">$&#123;firstName&#125;</span> <span class="subst">$&#123;lastName&#125;</span> `</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="keyword">interface</span> Person &#123;</span><br><span class="line">  firstName: <span class="built_in">string</span>;</span><br><span class="line">  lastName: <span class="built_in">string</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">greeter</span>(<span class="params">person: Person</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`hello, <span class="subst">$&#123;person.firstName&#125;</span> <span class="subst">$&#123;person.lastName&#125;</span>`</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> user = <span class="keyword">new</span> Student(<span class="string">"Yang"</span>, <span class="string">"Tao"</span>);</span><br><span class="line"></span><br><span class="line">greeter(user);</span><br><span class="line"><span class="built_in">console</span>.log(user.fullName);</span><br></pre></td></tr></table></figure><p>运行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gulp typescript</span><br></pre></td></tr></table></figure><h2 id="Riot"><a href="#Riot" class="headerlink" title="Riot"></a>Riot</h2><ul><li><a href="http://eux.baidu.com/blog/fe/riot-js-%E6%A1%86%E6%9E%B6%E6%B7%B1%E5%85%A5%E8%A7%A3%E6%9E%90" target="_blank" rel="noopener">Riot.js 框架深入解析</a></li><li><a href="https://cloud.tencent.com/developer/article/1019472" target="_blank" rel="noopener">riot.js 教程【一】简介</a></li><li><a href="https://riot.js.org/documentation/" target="_blank" rel="noopener">RIOT</a>文档</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Gulp 自动化构建的一些过程，以及其他插件的结合使用等&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="开发工具" scheme="https://www.yangtao.site/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="gulp" scheme="https://www.yangtao.site/tags/gulp/"/>
    
  </entry>
  
  <entry>
    <title>Zepto-core-1.1.6 源码注释【转载】</title>
    <link href="https://www.yangtao.site/2020/03/13/zepto-v1.6.6/"/>
    <id>https://www.yangtao.site/2020/03/13/zepto-v1.6.6/</id>
    <published>2020-03-13T06:05:37.000Z</published>
    <updated>2020-03-16T04:05:11.179Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>原文：<a href="https://www.kancloud.cn/wangfupeng/zepto-design-srouce/173692" target="_blank" rel="noopener">zepto-core-1.1.6 源码注释</a></p></blockquote><a id="more"></a><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br><span class="line">604</span><br><span class="line">605</span><br><span class="line">606</span><br><span class="line">607</span><br><span class="line">608</span><br><span class="line">609</span><br><span class="line">610</span><br><span class="line">611</span><br><span class="line">612</span><br><span class="line">613</span><br><span class="line">614</span><br><span class="line">615</span><br><span class="line">616</span><br><span class="line">617</span><br><span class="line">618</span><br><span class="line">619</span><br><span class="line">620</span><br><span class="line">621</span><br><span class="line">622</span><br><span class="line">623</span><br><span class="line">624</span><br><span class="line">625</span><br><span class="line">626</span><br><span class="line">627</span><br><span class="line">628</span><br><span class="line">629</span><br><span class="line">630</span><br><span class="line">631</span><br><span class="line">632</span><br><span class="line">633</span><br><span class="line">634</span><br><span class="line">635</span><br><span class="line">636</span><br><span class="line">637</span><br><span class="line">638</span><br><span class="line">639</span><br><span class="line">640</span><br><span class="line">641</span><br><span class="line">642</span><br><span class="line">643</span><br><span class="line">644</span><br><span class="line">645</span><br><span class="line">646</span><br><span class="line">647</span><br><span class="line">648</span><br><span class="line">649</span><br><span class="line">650</span><br><span class="line">651</span><br><span class="line">652</span><br><span class="line">653</span><br><span class="line">654</span><br><span class="line">655</span><br><span class="line">656</span><br><span class="line">657</span><br><span class="line">658</span><br><span class="line">659</span><br><span class="line">660</span><br><span class="line">661</span><br><span class="line">662</span><br><span class="line">663</span><br><span class="line">664</span><br><span class="line">665</span><br><span class="line">666</span><br><span class="line">667</span><br><span class="line">668</span><br><span class="line">669</span><br><span class="line">670</span><br><span class="line">671</span><br><span class="line">672</span><br><span class="line">673</span><br><span class="line">674</span><br><span class="line">675</span><br><span class="line">676</span><br><span class="line">677</span><br><span class="line">678</span><br><span class="line">679</span><br><span class="line">680</span><br><span class="line">681</span><br><span class="line">682</span><br><span class="line">683</span><br><span class="line">684</span><br><span class="line">685</span><br><span class="line">686</span><br><span class="line">687</span><br><span class="line">688</span><br><span class="line">689</span><br><span class="line">690</span><br><span class="line">691</span><br><span class="line">692</span><br><span class="line">693</span><br><span class="line">694</span><br><span class="line">695</span><br><span class="line">696</span><br><span class="line">697</span><br><span class="line">698</span><br><span class="line">699</span><br><span class="line">700</span><br><span class="line">701</span><br><span class="line">702</span><br><span class="line">703</span><br><span class="line">704</span><br><span class="line">705</span><br><span class="line">706</span><br><span class="line">707</span><br><span class="line">708</span><br><span class="line">709</span><br><span class="line">710</span><br><span class="line">711</span><br><span class="line">712</span><br><span class="line">713</span><br><span class="line">714</span><br><span class="line">715</span><br><span class="line">716</span><br><span class="line">717</span><br><span class="line">718</span><br><span class="line">719</span><br><span class="line">720</span><br><span class="line">721</span><br><span class="line">722</span><br><span class="line">723</span><br><span class="line">724</span><br><span class="line">725</span><br><span class="line">726</span><br><span class="line">727</span><br><span class="line">728</span><br><span class="line">729</span><br><span class="line">730</span><br><span class="line">731</span><br><span class="line">732</span><br><span class="line">733</span><br><span class="line">734</span><br><span class="line">735</span><br><span class="line">736</span><br><span class="line">737</span><br><span class="line">738</span><br><span class="line">739</span><br><span class="line">740</span><br><span class="line">741</span><br><span class="line">742</span><br><span class="line">743</span><br><span class="line">744</span><br><span class="line">745</span><br><span class="line">746</span><br><span class="line">747</span><br><span class="line">748</span><br><span class="line">749</span><br><span class="line">750</span><br><span class="line">751</span><br><span class="line">752</span><br><span class="line">753</span><br><span class="line">754</span><br><span class="line">755</span><br><span class="line">756</span><br><span class="line">757</span><br><span class="line">758</span><br><span class="line">759</span><br><span class="line">760</span><br><span class="line">761</span><br><span class="line">762</span><br><span class="line">763</span><br><span class="line">764</span><br><span class="line">765</span><br><span class="line">766</span><br><span class="line">767</span><br><span class="line">768</span><br><span class="line">769</span><br><span class="line">770</span><br><span class="line">771</span><br><span class="line">772</span><br><span class="line">773</span><br><span class="line">774</span><br><span class="line">775</span><br><span class="line">776</span><br><span class="line">777</span><br><span class="line">778</span><br><span class="line">779</span><br><span class="line">780</span><br><span class="line">781</span><br><span class="line">782</span><br><span class="line">783</span><br><span class="line">784</span><br><span class="line">785</span><br><span class="line">786</span><br><span class="line">787</span><br><span class="line">788</span><br><span class="line">789</span><br><span class="line">790</span><br><span class="line">791</span><br><span class="line">792</span><br><span class="line">793</span><br><span class="line">794</span><br><span class="line">795</span><br><span class="line">796</span><br><span class="line">797</span><br><span class="line">798</span><br><span class="line">799</span><br><span class="line">800</span><br><span class="line">801</span><br><span class="line">802</span><br><span class="line">803</span><br><span class="line">804</span><br><span class="line">805</span><br><span class="line">806</span><br><span class="line">807</span><br><span class="line">808</span><br><span class="line">809</span><br><span class="line">810</span><br><span class="line">811</span><br><span class="line">812</span><br><span class="line">813</span><br><span class="line">814</span><br><span class="line">815</span><br><span class="line">816</span><br><span class="line">817</span><br><span class="line">818</span><br><span class="line">819</span><br><span class="line">820</span><br><span class="line">821</span><br><span class="line">822</span><br><span class="line">823</span><br><span class="line">824</span><br><span class="line">825</span><br><span class="line">826</span><br><span class="line">827</span><br><span class="line">828</span><br><span class="line">829</span><br><span class="line">830</span><br><span class="line">831</span><br><span class="line">832</span><br><span class="line">833</span><br><span class="line">834</span><br><span class="line">835</span><br><span class="line">836</span><br><span class="line">837</span><br><span class="line">838</span><br><span class="line">839</span><br><span class="line">840</span><br><span class="line">841</span><br><span class="line">842</span><br><span class="line">843</span><br><span class="line">844</span><br><span class="line">845</span><br><span class="line">846</span><br><span class="line">847</span><br><span class="line">848</span><br><span class="line">849</span><br><span class="line">850</span><br><span class="line">851</span><br><span class="line">852</span><br><span class="line">853</span><br><span class="line">854</span><br><span class="line">855</span><br><span class="line">856</span><br><span class="line">857</span><br><span class="line">858</span><br><span class="line">859</span><br><span class="line">860</span><br><span class="line">861</span><br><span class="line">862</span><br><span class="line">863</span><br><span class="line">864</span><br><span class="line">865</span><br><span class="line">866</span><br><span class="line">867</span><br><span class="line">868</span><br><span class="line">869</span><br><span class="line">870</span><br><span class="line">871</span><br><span class="line">872</span><br><span class="line">873</span><br><span class="line">874</span><br><span class="line">875</span><br><span class="line">876</span><br><span class="line">877</span><br><span class="line">878</span><br><span class="line">879</span><br><span class="line">880</span><br><span class="line">881</span><br><span class="line">882</span><br><span class="line">883</span><br><span class="line">884</span><br><span class="line">885</span><br><span class="line">886</span><br><span class="line">887</span><br><span class="line">888</span><br><span class="line">889</span><br><span class="line">890</span><br><span class="line">891</span><br><span class="line">892</span><br><span class="line">893</span><br><span class="line">894</span><br><span class="line">895</span><br><span class="line">896</span><br><span class="line">897</span><br><span class="line">898</span><br><span class="line">899</span><br><span class="line">900</span><br><span class="line">901</span><br><span class="line">902</span><br><span class="line">903</span><br><span class="line">904</span><br><span class="line">905</span><br><span class="line">906</span><br><span class="line">907</span><br><span class="line">908</span><br><span class="line">909</span><br><span class="line">910</span><br><span class="line">911</span><br><span class="line">912</span><br><span class="line">913</span><br><span class="line">914</span><br><span class="line">915</span><br><span class="line">916</span><br><span class="line">917</span><br><span class="line">918</span><br><span class="line">919</span><br><span class="line">920</span><br><span class="line">921</span><br><span class="line">922</span><br><span class="line">923</span><br><span class="line">924</span><br><span class="line">925</span><br><span class="line">926</span><br><span class="line">927</span><br><span class="line">928</span><br><span class="line">929</span><br><span class="line">930</span><br><span class="line">931</span><br><span class="line">932</span><br><span class="line">933</span><br><span class="line">934</span><br><span class="line">935</span><br><span class="line">936</span><br><span class="line">937</span><br><span class="line">938</span><br><span class="line">939</span><br><span class="line">940</span><br><span class="line">941</span><br><span class="line">942</span><br><span class="line">943</span><br><span class="line">944</span><br><span class="line">945</span><br><span class="line">946</span><br><span class="line">947</span><br><span class="line">948</span><br><span class="line">949</span><br><span class="line">950</span><br><span class="line">951</span><br><span class="line">952</span><br><span class="line">953</span><br><span class="line">954</span><br><span class="line">955</span><br><span class="line">956</span><br><span class="line">957</span><br><span class="line">958</span><br><span class="line">959</span><br><span class="line">960</span><br><span class="line">961</span><br><span class="line">962</span><br><span class="line">963</span><br><span class="line">964</span><br><span class="line">965</span><br><span class="line">966</span><br><span class="line">967</span><br><span class="line">968</span><br><span class="line">969</span><br><span class="line">970</span><br><span class="line">971</span><br><span class="line">972</span><br><span class="line">973</span><br><span class="line">974</span><br><span class="line">975</span><br><span class="line">976</span><br><span class="line">977</span><br><span class="line">978</span><br><span class="line">979</span><br><span class="line">980</span><br><span class="line">981</span><br><span class="line">982</span><br><span class="line">983</span><br><span class="line">984</span><br><span class="line">985</span><br><span class="line">986</span><br><span class="line">987</span><br><span class="line">988</span><br><span class="line">989</span><br><span class="line">990</span><br><span class="line">991</span><br><span class="line">992</span><br><span class="line">993</span><br><span class="line">994</span><br><span class="line">995</span><br><span class="line">996</span><br><span class="line">997</span><br><span class="line">998</span><br><span class="line">999</span><br><span class="line">1000</span><br><span class="line">1001</span><br><span class="line">1002</span><br><span class="line">1003</span><br><span class="line">1004</span><br><span class="line">1005</span><br><span class="line">1006</span><br><span class="line">1007</span><br><span class="line">1008</span><br><span class="line">1009</span><br><span class="line">1010</span><br><span class="line">1011</span><br><span class="line">1012</span><br><span class="line">1013</span><br><span class="line">1014</span><br><span class="line">1015</span><br><span class="line">1016</span><br><span class="line">1017</span><br><span class="line">1018</span><br><span class="line">1019</span><br><span class="line">1020</span><br><span class="line">1021</span><br><span class="line">1022</span><br><span class="line">1023</span><br><span class="line">1024</span><br><span class="line">1025</span><br><span class="line">1026</span><br><span class="line">1027</span><br><span class="line">1028</span><br><span class="line">1029</span><br><span class="line">1030</span><br><span class="line">1031</span><br><span class="line">1032</span><br><span class="line">1033</span><br><span class="line">1034</span><br><span class="line">1035</span><br><span class="line">1036</span><br><span class="line">1037</span><br><span class="line">1038</span><br><span class="line">1039</span><br><span class="line">1040</span><br><span class="line">1041</span><br><span class="line">1042</span><br><span class="line">1043</span><br><span class="line">1044</span><br><span class="line">1045</span><br><span class="line">1046</span><br><span class="line">1047</span><br><span class="line">1048</span><br><span class="line">1049</span><br><span class="line">1050</span><br><span class="line">1051</span><br><span class="line">1052</span><br><span class="line">1053</span><br><span class="line">1054</span><br><span class="line">1055</span><br><span class="line">1056</span><br><span class="line">1057</span><br><span class="line">1058</span><br><span class="line">1059</span><br><span class="line">1060</span><br><span class="line">1061</span><br><span class="line">1062</span><br><span class="line">1063</span><br><span class="line">1064</span><br><span class="line">1065</span><br><span class="line">1066</span><br><span class="line">1067</span><br><span class="line">1068</span><br><span class="line">1069</span><br><span class="line">1070</span><br><span class="line">1071</span><br><span class="line">1072</span><br><span class="line">1073</span><br><span class="line">1074</span><br><span class="line">1075</span><br><span class="line">1076</span><br><span class="line">1077</span><br><span class="line">1078</span><br><span class="line">1079</span><br><span class="line">1080</span><br><span class="line">1081</span><br><span class="line">1082</span><br><span class="line">1083</span><br><span class="line">1084</span><br><span class="line">1085</span><br><span class="line">1086</span><br><span class="line">1087</span><br><span class="line">1088</span><br><span class="line">1089</span><br><span class="line">1090</span><br><span class="line">1091</span><br><span class="line">1092</span><br><span class="line">1093</span><br><span class="line">1094</span><br><span class="line">1095</span><br><span class="line">1096</span><br><span class="line">1097</span><br><span class="line">1098</span><br><span class="line">1099</span><br><span class="line">1100</span><br><span class="line">1101</span><br><span class="line">1102</span><br><span class="line">1103</span><br><span class="line">1104</span><br><span class="line">1105</span><br><span class="line">1106</span><br><span class="line">1107</span><br><span class="line">1108</span><br><span class="line">1109</span><br><span class="line">1110</span><br><span class="line">1111</span><br><span class="line">1112</span><br><span class="line">1113</span><br><span class="line">1114</span><br><span class="line">1115</span><br><span class="line">1116</span><br><span class="line">1117</span><br><span class="line">1118</span><br><span class="line">1119</span><br><span class="line">1120</span><br><span class="line">1121</span><br><span class="line">1122</span><br><span class="line">1123</span><br><span class="line">1124</span><br><span class="line">1125</span><br><span class="line">1126</span><br><span class="line">1127</span><br><span class="line">1128</span><br><span class="line">1129</span><br><span class="line">1130</span><br><span class="line">1131</span><br><span class="line">1132</span><br><span class="line">1133</span><br><span class="line">1134</span><br><span class="line">1135</span><br><span class="line">1136</span><br><span class="line">1137</span><br><span class="line">1138</span><br><span class="line">1139</span><br><span class="line">1140</span><br><span class="line">1141</span><br><span class="line">1142</span><br><span class="line">1143</span><br><span class="line">1144</span><br><span class="line">1145</span><br><span class="line">1146</span><br><span class="line">1147</span><br><span class="line">1148</span><br><span class="line">1149</span><br><span class="line">1150</span><br><span class="line">1151</span><br><span class="line">1152</span><br><span class="line">1153</span><br><span class="line">1154</span><br><span class="line">1155</span><br><span class="line">1156</span><br><span class="line">1157</span><br><span class="line">1158</span><br><span class="line">1159</span><br><span class="line">1160</span><br><span class="line">1161</span><br><span class="line">1162</span><br><span class="line">1163</span><br><span class="line">1164</span><br><span class="line">1165</span><br><span class="line">1166</span><br><span class="line">1167</span><br><span class="line">1168</span><br><span class="line">1169</span><br><span class="line">1170</span><br><span class="line">1171</span><br><span class="line">1172</span><br><span class="line">1173</span><br><span class="line">1174</span><br><span class="line">1175</span><br><span class="line">1176</span><br><span class="line">1177</span><br><span class="line">1178</span><br><span class="line">1179</span><br><span class="line">1180</span><br><span class="line">1181</span><br><span class="line">1182</span><br><span class="line">1183</span><br><span class="line">1184</span><br><span class="line">1185</span><br><span class="line">1186</span><br><span class="line">1187</span><br><span class="line">1188</span><br><span class="line">1189</span><br><span class="line">1190</span><br><span class="line">1191</span><br><span class="line">1192</span><br><span class="line">1193</span><br><span class="line">1194</span><br><span class="line">1195</span><br><span class="line">1196</span><br><span class="line">1197</span><br><span class="line">1198</span><br><span class="line">1199</span><br><span class="line">1200</span><br><span class="line">1201</span><br><span class="line">1202</span><br><span class="line">1203</span><br><span class="line">1204</span><br><span class="line">1205</span><br><span class="line">1206</span><br><span class="line">1207</span><br><span class="line">1208</span><br><span class="line">1209</span><br><span class="line">1210</span><br><span class="line">1211</span><br><span class="line">1212</span><br><span class="line">1213</span><br><span class="line">1214</span><br><span class="line">1215</span><br><span class="line">1216</span><br><span class="line">1217</span><br><span class="line">1218</span><br><span class="line">1219</span><br><span class="line">1220</span><br><span class="line">1221</span><br><span class="line">1222</span><br><span class="line">1223</span><br><span class="line">1224</span><br><span class="line">1225</span><br><span class="line">1226</span><br><span class="line">1227</span><br><span class="line">1228</span><br><span class="line">1229</span><br><span class="line">1230</span><br><span class="line">1231</span><br><span class="line">1232</span><br><span class="line">1233</span><br><span class="line">1234</span><br><span class="line">1235</span><br><span class="line">1236</span><br><span class="line">1237</span><br><span class="line">1238</span><br><span class="line">1239</span><br><span class="line">1240</span><br><span class="line">1241</span><br><span class="line">1242</span><br><span class="line">1243</span><br><span class="line">1244</span><br><span class="line">1245</span><br><span class="line">1246</span><br><span class="line">1247</span><br><span class="line">1248</span><br><span class="line">1249</span><br><span class="line">1250</span><br><span class="line">1251</span><br><span class="line">1252</span><br><span class="line">1253</span><br><span class="line">1254</span><br><span class="line">1255</span><br><span class="line">1256</span><br><span class="line">1257</span><br><span class="line">1258</span><br><span class="line">1259</span><br><span class="line">1260</span><br><span class="line">1261</span><br><span class="line">1262</span><br><span class="line">1263</span><br><span class="line">1264</span><br><span class="line">1265</span><br><span class="line">1266</span><br><span class="line">1267</span><br><span class="line">1268</span><br><span class="line">1269</span><br><span class="line">1270</span><br><span class="line">1271</span><br><span class="line">1272</span><br><span class="line">1273</span><br><span class="line">1274</span><br><span class="line">1275</span><br><span class="line">1276</span><br><span class="line">1277</span><br><span class="line">1278</span><br><span class="line">1279</span><br><span class="line">1280</span><br><span class="line">1281</span><br><span class="line">1282</span><br><span class="line">1283</span><br><span class="line">1284</span><br><span class="line">1285</span><br><span class="line">1286</span><br><span class="line">1287</span><br><span class="line">1288</span><br><span class="line">1289</span><br><span class="line">1290</span><br><span class="line">1291</span><br><span class="line">1292</span><br><span class="line">1293</span><br><span class="line">1294</span><br><span class="line">1295</span><br><span class="line">1296</span><br><span class="line">1297</span><br><span class="line">1298</span><br><span class="line">1299</span><br><span class="line">1300</span><br><span class="line">1301</span><br><span class="line">1302</span><br><span class="line">1303</span><br><span class="line">1304</span><br><span class="line">1305</span><br><span class="line">1306</span><br><span class="line">1307</span><br><span class="line">1308</span><br><span class="line">1309</span><br><span class="line">1310</span><br><span class="line">1311</span><br><span class="line">1312</span><br><span class="line">1313</span><br><span class="line">1314</span><br><span class="line">1315</span><br><span class="line">1316</span><br><span class="line">1317</span><br><span class="line">1318</span><br><span class="line">1319</span><br><span class="line">1320</span><br><span class="line">1321</span><br><span class="line">1322</span><br><span class="line">1323</span><br><span class="line">1324</span><br><span class="line">1325</span><br><span class="line">1326</span><br><span class="line">1327</span><br><span class="line">1328</span><br><span class="line">1329</span><br><span class="line">1330</span><br><span class="line">1331</span><br><span class="line">1332</span><br><span class="line">1333</span><br><span class="line">1334</span><br><span class="line">1335</span><br><span class="line">1336</span><br><span class="line">1337</span><br><span class="line">1338</span><br><span class="line">1339</span><br><span class="line">1340</span><br><span class="line">1341</span><br><span class="line">1342</span><br><span class="line">1343</span><br><span class="line">1344</span><br><span class="line">1345</span><br><span class="line">1346</span><br><span class="line">1347</span><br><span class="line">1348</span><br><span class="line">1349</span><br><span class="line">1350</span><br><span class="line">1351</span><br><span class="line">1352</span><br><span class="line">1353</span><br><span class="line">1354</span><br><span class="line">1355</span><br><span class="line">1356</span><br><span class="line">1357</span><br><span class="line">1358</span><br><span class="line">1359</span><br><span class="line">1360</span><br><span class="line">1361</span><br><span class="line">1362</span><br><span class="line">1363</span><br><span class="line">1364</span><br><span class="line">1365</span><br><span class="line">1366</span><br><span class="line">1367</span><br><span class="line">1368</span><br><span class="line">1369</span><br><span class="line">1370</span><br><span class="line">1371</span><br><span class="line">1372</span><br><span class="line">1373</span><br><span class="line">1374</span><br><span class="line">1375</span><br><span class="line">1376</span><br><span class="line">1377</span><br><span class="line">1378</span><br><span class="line">1379</span><br><span class="line">1380</span><br><span class="line">1381</span><br><span class="line">1382</span><br><span class="line">1383</span><br><span class="line">1384</span><br><span class="line">1385</span><br><span class="line">1386</span><br><span class="line">1387</span><br><span class="line">1388</span><br><span class="line">1389</span><br><span class="line">1390</span><br><span class="line">1391</span><br><span class="line">1392</span><br><span class="line">1393</span><br><span class="line">1394</span><br><span class="line">1395</span><br><span class="line">1396</span><br><span class="line">1397</span><br><span class="line">1398</span><br><span class="line">1399</span><br><span class="line">1400</span><br><span class="line">1401</span><br><span class="line">1402</span><br><span class="line">1403</span><br><span class="line">1404</span><br><span class="line">1405</span><br><span class="line">1406</span><br><span class="line">1407</span><br><span class="line">1408</span><br><span class="line">1409</span><br><span class="line">1410</span><br><span class="line">1411</span><br><span class="line">1412</span><br><span class="line">1413</span><br><span class="line">1414</span><br><span class="line">1415</span><br><span class="line">1416</span><br><span class="line">1417</span><br><span class="line">1418</span><br><span class="line">1419</span><br><span class="line">1420</span><br><span class="line">1421</span><br><span class="line">1422</span><br><span class="line">1423</span><br><span class="line">1424</span><br><span class="line">1425</span><br><span class="line">1426</span><br><span class="line">1427</span><br><span class="line">1428</span><br><span class="line">1429</span><br><span class="line">1430</span><br><span class="line">1431</span><br><span class="line">1432</span><br><span class="line">1433</span><br><span class="line">1434</span><br><span class="line">1435</span><br><span class="line">1436</span><br><span class="line">1437</span><br><span class="line">1438</span><br><span class="line">1439</span><br><span class="line">1440</span><br><span class="line">1441</span><br><span class="line">1442</span><br><span class="line">1443</span><br><span class="line">1444</span><br><span class="line">1445</span><br><span class="line">1446</span><br><span class="line">1447</span><br><span class="line">1448</span><br><span class="line">1449</span><br><span class="line">1450</span><br><span class="line">1451</span><br><span class="line">1452</span><br><span class="line">1453</span><br><span class="line">1454</span><br><span class="line">1455</span><br><span class="line">1456</span><br><span class="line">1457</span><br><span class="line">1458</span><br><span class="line">1459</span><br><span class="line">1460</span><br><span class="line">1461</span><br><span class="line">1462</span><br><span class="line">1463</span><br><span class="line">1464</span><br><span class="line">1465</span><br><span class="line">1466</span><br><span class="line">1467</span><br><span class="line">1468</span><br><span class="line">1469</span><br><span class="line">1470</span><br><span class="line">1471</span><br><span class="line">1472</span><br><span class="line">1473</span><br><span class="line">1474</span><br><span class="line">1475</span><br><span class="line">1476</span><br><span class="line">1477</span><br><span class="line">1478</span><br><span class="line">1479</span><br><span class="line">1480</span><br><span class="line">1481</span><br><span class="line">1482</span><br><span class="line">1483</span><br><span class="line">1484</span><br><span class="line">1485</span><br><span class="line">1486</span><br><span class="line">1487</span><br><span class="line">1488</span><br><span class="line">1489</span><br><span class="line">1490</span><br><span class="line">1491</span><br><span class="line">1492</span><br><span class="line">1493</span><br><span class="line">1494</span><br><span class="line">1495</span><br><span class="line">1496</span><br><span class="line">1497</span><br><span class="line">1498</span><br><span class="line">1499</span><br><span class="line">1500</span><br><span class="line">1501</span><br><span class="line">1502</span><br><span class="line">1503</span><br><span class="line">1504</span><br><span class="line">1505</span><br><span class="line">1506</span><br><span class="line">1507</span><br><span class="line">1508</span><br><span class="line">1509</span><br><span class="line">1510</span><br><span class="line">1511</span><br><span class="line">1512</span><br><span class="line">1513</span><br><span class="line">1514</span><br><span class="line">1515</span><br><span class="line">1516</span><br><span class="line">1517</span><br><span class="line">1518</span><br><span class="line">1519</span><br><span class="line">1520</span><br><span class="line">1521</span><br><span class="line">1522</span><br><span class="line">1523</span><br><span class="line">1524</span><br><span class="line">1525</span><br><span class="line">1526</span><br><span class="line">1527</span><br><span class="line">1528</span><br><span class="line">1529</span><br><span class="line">1530</span><br><span class="line">1531</span><br><span class="line">1532</span><br><span class="line">1533</span><br><span class="line">1534</span><br><span class="line">1535</span><br><span class="line">1536</span><br><span class="line">1537</span><br><span class="line">1538</span><br><span class="line">1539</span><br><span class="line">1540</span><br><span class="line">1541</span><br><span class="line">1542</span><br><span class="line">1543</span><br><span class="line">1544</span><br><span class="line">1545</span><br><span class="line">1546</span><br><span class="line">1547</span><br><span class="line">1548</span><br><span class="line">1549</span><br><span class="line">1550</span><br><span class="line">1551</span><br><span class="line">1552</span><br><span class="line">1553</span><br><span class="line">1554</span><br><span class="line">1555</span><br><span class="line">1556</span><br><span class="line">1557</span><br><span class="line">1558</span><br><span class="line">1559</span><br><span class="line">1560</span><br><span class="line">1561</span><br><span class="line">1562</span><br><span class="line">1563</span><br><span class="line">1564</span><br><span class="line">1565</span><br><span class="line">1566</span><br><span class="line">1567</span><br><span class="line">1568</span><br><span class="line">1569</span><br><span class="line">1570</span><br><span class="line">1571</span><br><span class="line">1572</span><br><span class="line">1573</span><br><span class="line">1574</span><br><span class="line">1575</span><br><span class="line">1576</span><br><span class="line">1577</span><br><span class="line">1578</span><br><span class="line">1579</span><br><span class="line">1580</span><br><span class="line">1581</span><br><span class="line">1582</span><br><span class="line">1583</span><br><span class="line">1584</span><br><span class="line">1585</span><br><span class="line">1586</span><br><span class="line">1587</span><br><span class="line">1588</span><br><span class="line">1589</span><br><span class="line">1590</span><br><span class="line">1591</span><br><span class="line">1592</span><br><span class="line">1593</span><br><span class="line">1594</span><br><span class="line">1595</span><br><span class="line">1596</span><br><span class="line">1597</span><br><span class="line">1598</span><br><span class="line">1599</span><br><span class="line">1600</span><br><span class="line">1601</span><br><span class="line">1602</span><br><span class="line">1603</span><br><span class="line">1604</span><br><span class="line">1605</span><br><span class="line">1606</span><br><span class="line">1607</span><br><span class="line">1608</span><br><span class="line">1609</span><br><span class="line">1610</span><br><span class="line">1611</span><br><span class="line">1612</span><br><span class="line">1613</span><br><span class="line">1614</span><br><span class="line">1615</span><br><span class="line">1616</span><br><span class="line">1617</span><br><span class="line">1618</span><br><span class="line">1619</span><br><span class="line">1620</span><br><span class="line">1621</span><br><span class="line">1622</span><br><span class="line">1623</span><br><span class="line">1624</span><br><span class="line">1625</span><br><span class="line">1626</span><br><span class="line">1627</span><br><span class="line">1628</span><br><span class="line">1629</span><br><span class="line">1630</span><br><span class="line">1631</span><br><span class="line">1632</span><br><span class="line">1633</span><br><span class="line">1634</span><br><span class="line">1635</span><br><span class="line">1636</span><br><span class="line">1637</span><br><span class="line">1638</span><br><span class="line">1639</span><br><span class="line">1640</span><br><span class="line">1641</span><br><span class="line">1642</span><br><span class="line">1643</span><br><span class="line">1644</span><br><span class="line">1645</span><br><span class="line">1646</span><br><span class="line">1647</span><br><span class="line">1648</span><br><span class="line">1649</span><br><span class="line">1650</span><br><span class="line">1651</span><br><span class="line">1652</span><br><span class="line">1653</span><br><span class="line">1654</span><br><span class="line">1655</span><br><span class="line">1656</span><br><span class="line">1657</span><br><span class="line">1658</span><br><span class="line">1659</span><br><span class="line">1660</span><br><span class="line">1661</span><br><span class="line">1662</span><br><span class="line">1663</span><br><span class="line">1664</span><br><span class="line">1665</span><br><span class="line">1666</span><br><span class="line">1667</span><br><span class="line">1668</span><br><span class="line">1669</span><br><span class="line">1670</span><br><span class="line">1671</span><br><span class="line">1672</span><br><span class="line">1673</span><br><span class="line">1674</span><br><span class="line">1675</span><br><span class="line">1676</span><br><span class="line">1677</span><br><span class="line">1678</span><br><span class="line">1679</span><br><span class="line">1680</span><br><span class="line">1681</span><br><span class="line">1682</span><br><span class="line">1683</span><br><span class="line">1684</span><br><span class="line">1685</span><br><span class="line">1686</span><br><span class="line">1687</span><br><span class="line">1688</span><br><span class="line">1689</span><br><span class="line">1690</span><br><span class="line">1691</span><br><span class="line">1692</span><br><span class="line">1693</span><br><span class="line">1694</span><br><span class="line">1695</span><br><span class="line">1696</span><br><span class="line">1697</span><br><span class="line">1698</span><br><span class="line">1699</span><br><span class="line">1700</span><br><span class="line">1701</span><br><span class="line">1702</span><br><span class="line">1703</span><br><span class="line">1704</span><br><span class="line">1705</span><br><span class="line">1706</span><br><span class="line">1707</span><br><span class="line">1708</span><br><span class="line">1709</span><br><span class="line">1710</span><br><span class="line">1711</span><br><span class="line">1712</span><br><span class="line">1713</span><br><span class="line">1714</span><br><span class="line">1715</span><br><span class="line">1716</span><br><span class="line">1717</span><br><span class="line">1718</span><br><span class="line">1719</span><br><span class="line">1720</span><br><span class="line">1721</span><br><span class="line">1722</span><br><span class="line">1723</span><br><span class="line">1724</span><br><span class="line">1725</span><br><span class="line">1726</span><br><span class="line">1727</span><br><span class="line">1728</span><br><span class="line">1729</span><br><span class="line">1730</span><br><span class="line">1731</span><br><span class="line">1732</span><br><span class="line">1733</span><br><span class="line">1734</span><br><span class="line">1735</span><br><span class="line">1736</span><br><span class="line">1737</span><br><span class="line">1738</span><br><span class="line">1739</span><br><span class="line">1740</span><br><span class="line">1741</span><br><span class="line">1742</span><br><span class="line">1743</span><br><span class="line">1744</span><br><span class="line">1745</span><br><span class="line">1746</span><br><span class="line">1747</span><br><span class="line">1748</span><br><span class="line">1749</span><br><span class="line">1750</span><br><span class="line">1751</span><br><span class="line">1752</span><br><span class="line">1753</span><br><span class="line">1754</span><br><span class="line">1755</span><br><span class="line">1756</span><br><span class="line">1757</span><br><span class="line">1758</span><br><span class="line">1759</span><br><span class="line">1760</span><br><span class="line">1761</span><br><span class="line">1762</span><br><span class="line">1763</span><br><span class="line">1764</span><br><span class="line">1765</span><br><span class="line">1766</span><br><span class="line">1767</span><br><span class="line">1768</span><br><span class="line">1769</span><br><span class="line">1770</span><br><span class="line">1771</span><br><span class="line">1772</span><br><span class="line">1773</span><br><span class="line">1774</span><br><span class="line">1775</span><br><span class="line">1776</span><br><span class="line">1777</span><br><span class="line">1778</span><br><span class="line">1779</span><br><span class="line">1780</span><br><span class="line">1781</span><br><span class="line">1782</span><br><span class="line">1783</span><br><span class="line">1784</span><br><span class="line">1785</span><br><span class="line">1786</span><br><span class="line">1787</span><br><span class="line">1788</span><br><span class="line">1789</span><br><span class="line">1790</span><br><span class="line">1791</span><br><span class="line">1792</span><br><span class="line">1793</span><br><span class="line">1794</span><br><span class="line">1795</span><br><span class="line">1796</span><br><span class="line">1797</span><br><span class="line">1798</span><br><span class="line">1799</span><br><span class="line">1800</span><br><span class="line">1801</span><br><span class="line">1802</span><br><span class="line">1803</span><br><span class="line">1804</span><br><span class="line">1805</span><br><span class="line">1806</span><br><span class="line">1807</span><br><span class="line">1808</span><br><span class="line">1809</span><br><span class="line">1810</span><br><span class="line">1811</span><br><span class="line">1812</span><br><span class="line">1813</span><br><span class="line">1814</span><br><span class="line">1815</span><br><span class="line">1816</span><br><span class="line">1817</span><br><span class="line">1818</span><br><span class="line">1819</span><br><span class="line">1820</span><br><span class="line">1821</span><br><span class="line">1822</span><br><span class="line">1823</span><br><span class="line">1824</span><br><span class="line">1825</span><br><span class="line">1826</span><br><span class="line">1827</span><br><span class="line">1828</span><br><span class="line">1829</span><br><span class="line">1830</span><br><span class="line">1831</span><br><span class="line">1832</span><br><span class="line">1833</span><br><span class="line">1834</span><br><span class="line">1835</span><br><span class="line">1836</span><br><span class="line">1837</span><br><span class="line">1838</span><br><span class="line">1839</span><br><span class="line">1840</span><br><span class="line">1841</span><br><span class="line">1842</span><br><span class="line">1843</span><br><span class="line">1844</span><br><span class="line">1845</span><br><span class="line">1846</span><br><span class="line">1847</span><br><span class="line">1848</span><br><span class="line">1849</span><br><span class="line">1850</span><br><span class="line">1851</span><br><span class="line">1852</span><br><span class="line">1853</span><br><span class="line">1854</span><br><span class="line">1855</span><br><span class="line">1856</span><br><span class="line">1857</span><br><span class="line">1858</span><br><span class="line">1859</span><br><span class="line">1860</span><br><span class="line">1861</span><br><span class="line">1862</span><br><span class="line">1863</span><br><span class="line">1864</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Zepto v1.1.6 - zepto event ajax form ie - zeptojs.com/license */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Zepto = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> <span class="literal">undefined</span>,</span><br><span class="line">    key,</span><br><span class="line">    $,</span><br><span class="line">    classList,</span><br><span class="line">    <span class="comment">// 获取数组的slice 和 filter（返回数组中的满足回调函数中指定的条件的元素）方法</span></span><br><span class="line">    emptyArray = [],</span><br><span class="line">    slice = emptyArray.slice,</span><br><span class="line">    filter = emptyArray.filter,</span><br><span class="line">    <span class="built_in">document</span> = <span class="built_in">window</span>.document,</span><br><span class="line">    elementDisplay = &#123;&#125;,</span><br><span class="line">    classCache = &#123;&#125;,</span><br><span class="line">    cssNumber = &#123;</span><br><span class="line">      <span class="string">"column-count"</span>: <span class="number">1</span>,</span><br><span class="line">      columns: <span class="number">1</span>,</span><br><span class="line">      <span class="string">"font-weight"</span>: <span class="number">1</span>,</span><br><span class="line">      <span class="string">"line-height"</span>: <span class="number">1</span>,</span><br><span class="line">      opacity: <span class="number">1</span>,</span><br><span class="line">      <span class="string">"z-index"</span>: <span class="number">1</span>,</span><br><span class="line">      zoom: <span class="number">1</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 取出html代码中第一个html标签（或注释），如取出 &lt;p&gt;123&lt;/p&gt;&lt;h1&gt;345&lt;/h1&gt; 中的 &lt;p&gt;</span></span><br><span class="line">    fragmentRE = <span class="regexp">/^\s*&lt;(\w+|!)[^&gt;]*&gt;/</span>,</span><br><span class="line">    <span class="comment">// 匹配 &lt;img /&gt; &lt;p&gt;&lt;/p&gt;  不匹配 &lt;img src=""/&gt; &lt;p&gt;123&lt;/p&gt;</span></span><br><span class="line">    singleTagRE = <span class="regexp">/^&lt;(\w+)\s*\/?&gt;(?:&lt;\/\1&gt;|)$/</span>,</span><br><span class="line">    <span class="comment">// 单标签</span></span><br><span class="line">    tagExpanderRE = <span class="regexp">/&lt;(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^&gt;]*)\/&gt;/gi</span>,</span><br><span class="line">    <span class="comment">// body html</span></span><br><span class="line">    rootNodeRE = <span class="regexp">/^(?:body|html)$/i</span>,</span><br><span class="line">    <span class="comment">// 大写字母</span></span><br><span class="line">    capitalRE = <span class="regexp">/([A-Z])/g</span>,</span><br><span class="line">    <span class="comment">// special attributes that should be get/set via method calls</span></span><br><span class="line">    <span class="comment">// 应该通过方法调用来设置/获取的特殊属性</span></span><br><span class="line">    methodAttributes = [</span><br><span class="line">      <span class="string">"val"</span>,</span><br><span class="line">      <span class="string">"css"</span>,</span><br><span class="line">      <span class="string">"html"</span>,</span><br><span class="line">      <span class="string">"text"</span>,</span><br><span class="line">      <span class="string">"data"</span>,</span><br><span class="line">      <span class="string">"width"</span>,</span><br><span class="line">      <span class="string">"height"</span>,</span><br><span class="line">      <span class="string">"offset"</span></span><br><span class="line">    ],</span><br><span class="line">    adjacencyOperators = [<span class="string">"after"</span>, <span class="string">"prepend"</span>, <span class="string">"before"</span>, <span class="string">"append"</span>],</span><br><span class="line">    table = <span class="built_in">document</span>.createElement(<span class="string">"table"</span>),</span><br><span class="line">    tableRow = <span class="built_in">document</span>.createElement(<span class="string">"tr"</span>),</span><br><span class="line">    <span class="comment">// 指定特殊元素的 容器</span></span><br><span class="line">    containers = &#123;</span><br><span class="line">      tr: <span class="built_in">document</span>.createElement(<span class="string">"tbody"</span>),</span><br><span class="line">      tbody: table,</span><br><span class="line">      thead: table,</span><br><span class="line">      tfoot: table,</span><br><span class="line">      td: tableRow,</span><br><span class="line">      th: tableRow,</span><br><span class="line">      <span class="comment">// 除了上面指定的，其他所有元素的容器都是 div</span></span><br><span class="line">      <span class="string">"*"</span>: <span class="built_in">document</span>.createElement(<span class="string">"div"</span>)</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// interactive ？？？</span></span><br><span class="line">    readyRE = <span class="regexp">/complete|loaded|interactive/</span>,</span><br><span class="line">    <span class="comment">// 匹配一个包括（字母、数组、下划线、-）的字符串</span></span><br><span class="line">    simpleSelectorRE = <span class="regexp">/^[\w-]*$/</span>,</span><br><span class="line">    class2type = &#123;&#125;,</span><br><span class="line">    toString = class2type.toString,</span><br><span class="line">    zepto = &#123;&#125;,</span><br><span class="line">    camelize,</span><br><span class="line">    uniq,</span><br><span class="line">    tempParent = <span class="built_in">document</span>.createElement(<span class="string">"div"</span>),</span><br><span class="line">    <span class="comment">// 属性转换为 camalCase 格式。</span></span><br><span class="line">    <span class="comment">// $.fn.prop 方法用到了</span></span><br><span class="line">    propMap = &#123;</span><br><span class="line">      tabindex: <span class="string">"tabIndex"</span>,</span><br><span class="line">      readonly: <span class="string">"readOnly"</span>,</span><br><span class="line">      <span class="keyword">for</span>: <span class="string">"htmlFor"</span>,</span><br><span class="line">      class: "className",</span><br><span class="line">      maxlength: <span class="string">"maxLength"</span>,</span><br><span class="line">      cellspacing: <span class="string">"cellSpacing"</span>,</span><br><span class="line">      cellpadding: <span class="string">"cellPadding"</span>,</span><br><span class="line">      rowspan: <span class="string">"rowSpan"</span>,</span><br><span class="line">      colspan: <span class="string">"colSpan"</span>,</span><br><span class="line">      usemap: <span class="string">"useMap"</span>,</span><br><span class="line">      frameborder: <span class="string">"frameBorder"</span>,</span><br><span class="line">      contenteditable: <span class="string">"contentEditable"</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 判断是否是arr的函数</span></span><br><span class="line">    isArray =</span><br><span class="line">      <span class="built_in">Array</span>.isArray ||</span><br><span class="line">      <span class="function"><span class="keyword">function</span>(<span class="params">object</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> object <span class="keyword">instanceof</span> <span class="built_in">Array</span>;</span><br><span class="line">      &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 上文定义 zepto = &#123;&#125;</span></span><br><span class="line">  <span class="comment">// 判断 element 是否符合 selector 的选择要求</span></span><br><span class="line">  zepto.matches = <span class="function"><span class="keyword">function</span>(<span class="params">element, selector</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// selector有值，element有值，element是普通DOM节点</span></span><br><span class="line">    <span class="keyword">if</span> (!selector || !element || element.nodeType !== <span class="number">1</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// elem.matchesSelector('.item')</span></span><br><span class="line">    <span class="comment">// 判断当前的 elem 是否符合传入的 selector 的要求</span></span><br><span class="line">    <span class="keyword">var</span> matchesSelector =</span><br><span class="line">      element.webkitMatchesSelector ||</span><br><span class="line">      element.mozMatchesSelector ||</span><br><span class="line">      element.oMatchesSelector ||</span><br><span class="line">      element.matchesSelector;</span><br><span class="line">    <span class="keyword">if</span> (matchesSelector) <span class="keyword">return</span> matchesSelector.call(element, selector);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 浏览器不支持 matchesSelector</span></span><br><span class="line">    <span class="comment">// fall back to performing a selector:</span></span><br><span class="line">    <span class="keyword">var</span> match,</span><br><span class="line">      parent = element.parentNode,</span><br><span class="line">      temp = !parent;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 上文定义 tempParent = document.createElement('div'),</span></span><br><span class="line">    <span class="comment">// 如果没有parent，parent赋值为一个div，然后将当前元素加入到这个div中</span></span><br><span class="line">    <span class="keyword">if</span> (temp) &#123;</span><br><span class="line">      parent = tempParent;</span><br><span class="line">      tempParent.appendChild(element);</span><br><span class="line">      <span class="comment">// (parent = tempParent).appendChild(element); 这种写法不易读</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 通过 qsa 获取匹配的元素，判断其中有没有 element</span></span><br><span class="line">    match = ~zepto.qsa(parent, selector).indexOf(element);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (temp) &#123;</span><br><span class="line">      <span class="comment">// 如果没有parent时，之前执行过  tempParent.appendChild(element);</span></span><br><span class="line">      <span class="comment">// 此时要移除子元素</span></span><br><span class="line">      tempParent.removeChild(element);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// temp &amp;&amp; tempParent.removeChild(element)  // 这种写法不易读</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回最终的匹配结果，经过 qsa 判断的结果</span></span><br><span class="line">    <span class="keyword">return</span> match;</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">type</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> obj == <span class="literal">null</span></span><br><span class="line">      ? <span class="built_in">String</span>(obj) <span class="comment">// null undefined</span></span><br><span class="line">      : class2type[toString.call(obj)] || <span class="string">"object"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 下文定义：</span></span><br><span class="line">    <span class="comment">// // Populate the class2type map</span></span><br><span class="line">    <span class="comment">// $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) &#123;</span></span><br><span class="line">    <span class="comment">//   class2type[ "[object " + name + "]" ] = name.toLowerCase()</span></span><br><span class="line">    <span class="comment">// &#125;)</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">isFunction</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> type(value) == <span class="string">"function"</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// window的特点：window.window === window</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">isWindow</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> obj != <span class="literal">null</span> &amp;&amp; obj == obj.window;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// document.nodeType === 9</span></span><br><span class="line">  <span class="comment">// elem.DOCUMENT_NODE 也等于 9 （这里直接判断是不是9也行？？？）</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">isDocument</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> obj != <span class="literal">null</span> &amp;&amp; obj.nodeType == obj.DOCUMENT_NODE;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">isObject</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> type(obj) == <span class="string">"object"</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 判断是否是最基本的object：Object.getPrototypeOf(obj) == Object.prototype</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">isPlainObject</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      isObject(obj) &amp;&amp;</span><br><span class="line">      !isWindow(obj) &amp;&amp;</span><br><span class="line">      <span class="built_in">Object</span>.getPrototypeOf(obj) == <span class="built_in">Object</span>.prototype</span><br><span class="line">    );</span><br><span class="line">  &#125;</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">likeArray</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">typeof</span> obj.length == <span class="string">"number"</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 筛选数组，踢出 null undefined 元素</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">compact</span>(<span class="params">array</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> filter.call(array, <span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> item != <span class="literal">null</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="comment">// $.fn = &#123;</span></span><br><span class="line">  <span class="comment">//    concat: emptyArray.concat,</span></span><br><span class="line">  <span class="comment">// $.fn.concat.apply([], array) —— 无论 array 是不是数组，都将返回一个数组，</span></span><br><span class="line">  <span class="comment">// 例如 $.fn.concat.call([], 'abc') 返回的是 ['abc']</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">flatten</span>(<span class="params">array</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> array.length &gt; <span class="number">0</span> ? $.fn.concat.apply([], array) : array;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// camelize 已在上文定义</span></span><br><span class="line">  <span class="comment">// 用于 css 的 camalCase 转换，例如 background-color 转换为 backgroundColor</span></span><br><span class="line">  camelize = <span class="function"><span class="keyword">function</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> str.replace(<span class="regexp">/-+(.)?/g</span>, <span class="function"><span class="keyword">function</span>(<span class="params">match, chr</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> chr ? chr.toUpperCase() : <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">  <span class="comment">// 将 lineHeight 转换为 line-height 格式</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">dasherize</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> str</span><br><span class="line">      .replace(<span class="regexp">/::/g</span>, <span class="string">"/"</span>)</span><br><span class="line">      .replace(<span class="regexp">/([A-Z]+)([A-Z][a-z])/g</span>, <span class="string">"$1_$2"</span>)</span><br><span class="line">      .replace(<span class="regexp">/([a-z\d])([A-Z])/g</span>, <span class="string">"$1_$2"</span>)</span><br><span class="line">      .replace(<span class="regexp">/_/g</span>, <span class="string">"-"</span>)</span><br><span class="line">      .toLowerCase();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// uniq变量已经在前面定义</span></span><br><span class="line">  <span class="comment">// 用来将 [1,1,2,2,3,3] 替换为 [1,2,3]</span></span><br><span class="line">  uniq = <span class="function"><span class="keyword">function</span>(<span class="params">array</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> filter.call(array, <span class="function"><span class="keyword">function</span>(<span class="params">item, idx</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> array.indexOf(item) == idx;</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">// 上文定义 classCache = &#123;&#125;</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">classRE</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> name <span class="keyword">in</span> classCache</span><br><span class="line">      ? classCache[name]</span><br><span class="line">      : (classCache[name] = <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">"(^|\\s)"</span> + name + <span class="string">"(\\s|$)"</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// classCache 存储的数据是这样的：</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//   abc: /(^|\s)abc(\s|$)/,  // 能匹配 'abc' 或 ' abc ' 或 ' abc' 或 'abc '</span></span><br><span class="line">    <span class="comment">//   xyz: /(^|\s)abc(\s|$)/,</span></span><br><span class="line">    <span class="comment">//   ...</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 传入一个 css 的 name 和 value，判断这个 value 是否需要增加 'px'</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">maybeAddPx</span>(<span class="params">name, value</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// dasherize(name) 将 lineHeight 转换为 line-height 格式</span></span><br><span class="line">    <span class="comment">// \!cssNumber[dasherize(name)] 判断转换出来的 css name 是否再这个数组之外</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">typeof</span> value == <span class="string">"number"</span> &amp;&amp; !cssNumber[dasherize(name)]</span><br><span class="line">      ? <span class="comment">// 如果 value 是数字，并且 name 不在 cssNumber 数组之内，就需要加 'px'，否则不需要</span></span><br><span class="line">        <span class="comment">// 例如 'width'、'font-size' 就需要加 'px'， 'font-weight' 就不需要加</span></span><br><span class="line">        value + <span class="string">"px"</span></span><br><span class="line">      : value;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 前文定义----------------------</span></span><br><span class="line">    <span class="comment">// cssNumber = &#123;</span></span><br><span class="line">    <span class="comment">//   'column-count': 1,</span></span><br><span class="line">    <span class="comment">//   'columns': 1,</span></span><br><span class="line">    <span class="comment">//   'font-weight': 1,</span></span><br><span class="line">    <span class="comment">//   'line-height': 1,</span></span><br><span class="line">    <span class="comment">//   'opacity': 1,</span></span><br><span class="line">    <span class="comment">//   'z-index': 1,</span></span><br><span class="line">    <span class="comment">//   'zoom': 1</span></span><br><span class="line">    <span class="comment">// &#125;,</span></span><br><span class="line">    <span class="comment">// function dasherize(str) &#123;</span></span><br><span class="line">    <span class="comment">//   return str.replace(/::/g, '/')</span></span><br><span class="line">    <span class="comment">//             .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')</span></span><br><span class="line">    <span class="comment">//             .replace(/([a-z\d])([A-Z])/g, '$1_$2')</span></span><br><span class="line">    <span class="comment">//             .replace(/_/g, '-')</span></span><br><span class="line">    <span class="comment">//             .toLowerCase()</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 获取一个元素的默认 display 样式值，可能的结果是：inline block inline-block table .... （none 转换为 block）</span></span><br><span class="line">  <span class="comment">// $.fn.show 方法中用到了</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">defaultDisplay</span>(<span class="params">nodeName</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> element, display;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 前文定义 elementDisplay = &#123;&#125;</span></span><br><span class="line">    <span class="keyword">if</span> (!elementDisplay[nodeName]) &#123;</span><br><span class="line">      <span class="comment">// 如果 elementDisplay 对象中，没有存储 nodeName 的信息</span></span><br><span class="line">      <span class="comment">// 则新建一个 nodeName 元素，添加到 body 中</span></span><br><span class="line">      element = <span class="built_in">document</span>.createElement(nodeName);</span><br><span class="line">      <span class="built_in">document</span>.body.appendChild(element);</span><br><span class="line">      <span class="comment">// 获取它的默认的 display 样式信息。</span></span><br><span class="line">      display = getComputedStyle(element, <span class="string">""</span>).getPropertyValue(<span class="string">"display"</span>);</span><br><span class="line">      <span class="comment">// 接着马上移除元素！！！</span></span><br><span class="line">      element.parentNode.removeChild(element);</span><br><span class="line">      <span class="comment">// 'none' 换成 'block'，另外还可能是 'inline' 'inline-block' 'table' 等等...</span></span><br><span class="line">      display == <span class="string">"none"</span> &amp;&amp; (display = <span class="string">"block"</span>);</span><br><span class="line">      <span class="comment">// 存储下来</span></span><br><span class="line">      elementDisplay[nodeName] = display;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 下文定义</span></span><br><span class="line">      <span class="comment">// var nativeGetComputedStyle = getComputedStyle;</span></span><br><span class="line">      <span class="comment">// window.getComputedStyle = function(element)&#123;</span></span><br><span class="line">      <span class="comment">//   try &#123;</span></span><br><span class="line">      <span class="comment">//     return nativeGetComputedStyle(element)</span></span><br><span class="line">      <span class="comment">//   &#125; catch(e) &#123;</span></span><br><span class="line">      <span class="comment">//     return null</span></span><br><span class="line">      <span class="comment">//   &#125;</span></span><br><span class="line">      <span class="comment">// &#125;</span></span><br><span class="line">      <span class="comment">// 解释：</span></span><br><span class="line">      <span class="comment">// 如果浏览器支持 getComputedStyle 则使用，如果不支持，就返回 null</span></span><br><span class="line">      <span class="comment">// getComputedStyle(elem, '伪类，如 :link') 返回一个 CSSStyleDeclaration 对象，里面存储了元素的样式信息，可以通过 getPropertyValue('name') 方法获取</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最终返回 display 结果</span></span><br><span class="line">    <span class="keyword">return</span> elementDisplay[nodeName];</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">children</span>(<span class="params">element</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 有些浏览器支持 elem.children 获取子元素，有些不支持</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">"children"</span> <span class="keyword">in</span> element</span><br><span class="line">      ? <span class="comment">// 上文定义 slice = [].slice</span></span><br><span class="line">        <span class="comment">// slice.call(likeArr) 可以将对象数组转换为真正的数组</span></span><br><span class="line">        slice.call(element.children)</span><br><span class="line">      : <span class="comment">// 浏览器不支持 elem.children 只能通过 elem.childNodes 获取子元素</span></span><br><span class="line">        <span class="comment">// 只去 node.nodeType == 1 的子元素，通过 $.map 拼接成数组</span></span><br><span class="line">        <span class="comment">// $.map 下文定义的， $.map = function (elements, callback) &#123;....&#125;</span></span><br><span class="line">        <span class="comment">// $.map 作用：针对 elements（对象数组或数组），对每个元素都经过 callback 函数的过滤，并将过滤通过的元素，push到一个新数组中，返回新数组</span></span><br><span class="line">        $.map(element.childNodes, <span class="function"><span class="keyword">function</span>(<span class="params">node</span>) </span>&#123;</span><br><span class="line">          <span class="keyword">if</span> (node.nodeType == <span class="number">1</span>) <span class="keyword">return</span> node;</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">// 上文定义 zepto = &#123;&#125;</span></span><br><span class="line">  <span class="comment">// 上文定义 zepto.matches = function(element, selector) &#123; /* 判断elem是否符合selector的要求 */ &#125;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// `$.zepto.fragment` takes a html string and an optional tag name</span></span><br><span class="line">  <span class="comment">// to generate DOM nodes nodes from the given html string.</span></span><br><span class="line">  <span class="comment">// The generated DOM nodes are returned as an array.</span></span><br><span class="line">  <span class="comment">// This function can be overriden in plugins for example to make</span></span><br><span class="line">  <span class="comment">// it compatible with browsers that don't support the DOM fully.</span></span><br><span class="line">  zepto.fragment = <span class="function"><span class="keyword">function</span>(<span class="params">html, name, properties</span>) </span>&#123;</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">      @html: 待处理的html字符串</span></span><br><span class="line"><span class="comment">      @name: 通过 name 可在 containers 中查找容器节点，如果不传入，取得的容器默认为 div</span></span><br><span class="line"><span class="comment">      @properties: 节点属性对象</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> dom, nodes, container;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 上文定义：singleTagRE = /^&lt;(\w+)\s*\/?&gt;(?:&lt;\/\1&gt;|)$/,   // 匹配 &lt;img /&gt; &lt;p&gt;&lt;/p&gt;  不匹配 &lt;img src=""/&gt; &lt;p&gt;123&lt;/p&gt;</span></span><br><span class="line">    <span class="comment">// 如果 html 是单标签，则直接用该标签创建元素</span></span><br><span class="line">    <span class="comment">// RegExp.$1 表示正则中的第一个括号匹配的内容，在此即 (\w+) 匹配的内容，</span></span><br><span class="line">    <span class="comment">// A special case optimization for a single tag</span></span><br><span class="line">    <span class="keyword">if</span> (singleTagRE.test(html)) dom = $(<span class="built_in">document</span>.createElement(<span class="built_in">RegExp</span>.$<span class="number">1</span>));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!dom) &#123;</span><br><span class="line">      <span class="comment">// 说明 html 不是单标签，dom未被赋值</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 上文定义 tagExpanderRE = /&lt;(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^&gt;]*)\/&gt;/ig,   // 单标签</span></span><br><span class="line">      <span class="comment">// 将 &lt;p/&gt;或&lt;p /&gt;，替换为 &lt;p&gt;&lt;/p&gt;，将&lt;p abc/&gt;替换为&lt;p&gt;abc&lt;/p&gt;</span></span><br><span class="line">      <span class="comment">// &lt;input/&gt; （在 tagExpanderRE 中定义）的不替换</span></span><br><span class="line">      <span class="keyword">if</span> (html.replace) html = html.replace(tagExpanderRE, <span class="string">"&lt;$1&gt;&lt;/$2&gt;"</span>);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// fragmentRE = /^\s*&lt;(\w+|!)[^&gt;]*&gt;/,   // 取出html代码中第一个html标签（或注释），如取出 &lt;p&gt;123&lt;/p&gt;&lt;h1&gt;345&lt;/h1&gt; 中的 &lt;p&gt;</span></span><br><span class="line">      <span class="comment">// 如果 name 未传入，则赋值为 html 的第一个标签</span></span><br><span class="line">      <span class="keyword">if</span> (name === <span class="literal">undefined</span>) name = fragmentRE.test(html) &amp;&amp; <span class="built_in">RegExp</span>.$<span class="number">1</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">// containers = &#123;</span></span><br><span class="line">      <span class="comment">//   'tr': document.createElement('tbody'),</span></span><br><span class="line">      <span class="comment">//   'tbody': table,</span></span><br><span class="line">      <span class="comment">//   'thead': table,</span></span><br><span class="line">      <span class="comment">//   'tfoot': table,</span></span><br><span class="line">      <span class="comment">//   'td': tableRow,</span></span><br><span class="line">      <span class="comment">//   'th': tableRow,</span></span><br><span class="line">      <span class="comment">// 除了上面指定的，其他所有元素的容器都是 div</span></span><br><span class="line">      <span class="comment">//   '*': document.createElement('div')</span></span><br><span class="line">      <span class="comment">// &#125;,</span></span><br><span class="line">      <span class="keyword">if</span> (!(name <span class="keyword">in</span> containers)) name = <span class="string">"*"</span>;</span><br><span class="line"></span><br><span class="line">      container = containers[name];</span><br><span class="line">      container.innerHTML = <span class="string">""</span> + html; <span class="comment">// 转变为字符串的快捷方式</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 遍历 container 的子元素（先转换为数组形式）</span></span><br><span class="line">      <span class="comment">// 返回的同时，将每个子元素移除。</span></span><br><span class="line">      <span class="comment">// $.each 返回的是一个数组，因为第一个参数就是数组 slice.call(container.childNodes)</span></span><br><span class="line">      dom = $.each(slice.call(container.childNodes), <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        container.removeChild(<span class="keyword">this</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="keyword">if</span> (isPlainObject(properties)) &#123;</span><br><span class="line">      <span class="comment">// 先将dom转换为 zepto 对象</span></span><br><span class="line">      nodes = $(dom);</span><br><span class="line"></span><br><span class="line">      $.each(properties, <span class="function"><span class="keyword">function</span>(<span class="params">key, value</span>) </span>&#123;</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">// methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],</span></span><br><span class="line">        <span class="keyword">if</span> (methodAttributes.indexOf(key) &gt; <span class="number">-1</span>) nodes[key](value);</span><br><span class="line">        <span class="comment">// 满足 methodAttributes 的，通过方法赋值</span></span><br><span class="line">        <span class="keyword">else</span> nodes.attr(key, value); <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">    <span class="comment">// 最终返回的dom可能有两种形式</span></span><br><span class="line">    <span class="comment">// 第一，如果 html 是单标签，则dom被复制为一个zepto对象 dom = $(document.createElement(RegExp.$1))</span></span><br><span class="line">    <span class="comment">// 第二，如果 html 不是单标签，则dom被复制为一个DOM节点的数组</span></span><br><span class="line">    <span class="keyword">return</span> dom;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 上文定义 zepto = &#123;&#125;</span></span><br><span class="line">  <span class="comment">// 上文定义 zepto.matches = function(element, selector) &#123; /* 判断elem是否符合selector的要求 */ &#125;</span></span><br><span class="line">  <span class="comment">// 上文定义 zepto.fragment = function(html, name, properties) &#123; /* 通过html字符串获取文档碎片 */ &#125;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// `$.zepto.Z` swaps out the prototype of the given `dom` array</span></span><br><span class="line">  <span class="comment">// of nodes with `$.fn` and thus supplying all the Zepto functions</span></span><br><span class="line">  <span class="comment">// to the array. Note that `__proto__` is not supported on Internet</span></span><br><span class="line">  <span class="comment">// Explorer. This method can be overriden in plugins.</span></span><br><span class="line">  zepto.Z = <span class="function"><span class="keyword">function</span>(<span class="params">dom, selector</span>) </span>&#123;</span><br><span class="line">    dom = dom || [];</span><br><span class="line">    <span class="comment">// 将 dom 隐式原型强制改为 $.fn</span></span><br><span class="line">    <span class="comment">// 下文 zepto.Z.prototype = $.fn   因此，dom.__proto__ = $.fn 即 dom.__proto__ = zepto.Z.prototype  可以不较真的认为 zepto.Z 就是一个构造函数（但感觉这么设计，有些蹩脚）</span></span><br><span class="line">    dom.__proto__ = $.fn;</span><br><span class="line">    dom.selector = selector || <span class="string">""</span>;</span><br><span class="line">    <span class="keyword">return</span> dom;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// `$.zepto.isZ` should return `true` if the given object is a Zepto</span></span><br><span class="line">  <span class="comment">// collection. This method can be overriden in plugins.</span></span><br><span class="line">  zepto.isZ = <span class="function"><span class="keyword">function</span>(<span class="params">object</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 上文 dom.__proto__ = $.fn</span></span><br><span class="line">    <span class="comment">// 下文 zepto.Z.prototype = $.fn</span></span><br><span class="line">    <span class="comment">// 可知：dom.__proto__ === $.fn === zepto.Z.prototype</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 因此，zepto对象都符合 object instanceof zepto.Z</span></span><br><span class="line">    <span class="keyword">return</span> object <span class="keyword">instanceof</span> zepto.Z;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and</span></span><br><span class="line">  <span class="comment">// takes a CSS selector and an optional context (and handles various</span></span><br><span class="line">  <span class="comment">// special cases).</span></span><br><span class="line">  <span class="comment">// This method can be overriden in plugins.</span></span><br><span class="line">  zepto.init = <span class="function"><span class="keyword">function</span>(<span class="params">selector, context</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> dom;</span><br><span class="line">    <span class="comment">// If nothing given, return an empty Zepto collection</span></span><br><span class="line">    <span class="keyword">if</span> (!selector) <span class="keyword">return</span> zepto.Z();</span><br><span class="line">    <span class="comment">// Optimize for string selectors</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> selector == <span class="string">"string"</span>) &#123;</span><br><span class="line">      <span class="comment">// 字符串的情况，一般有两种：</span></span><br><span class="line">      <span class="comment">// 第一，一段 html 代码，旨在通过zepto生成dom对象</span></span><br><span class="line">      <span class="comment">// 第二，一段查询字符串，旨在通过zepto查找dom对象</span></span><br><span class="line">      <span class="comment">// 将查询结果存储到 dom 变量中</span></span><br><span class="line"></span><br><span class="line">      selector = selector.trim();</span><br><span class="line">      <span class="comment">// If it's a html fragment, create nodes from it</span></span><br><span class="line">      <span class="comment">// Note: In both Chrome 21 and Firefox 15, DOM error 12</span></span><br><span class="line">      <span class="comment">// is thrown if the fragment doesn't begin with &lt;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 上文定义：</span></span><br><span class="line">      <span class="comment">// 取出html代码中第一个html标签（或注释），如取出 &lt;p&gt;123&lt;/p&gt;&lt;h1&gt;345&lt;/h1&gt; 中的 &lt;p&gt;</span></span><br><span class="line">      <span class="comment">// fragmentRE = /^\s*&lt;(\w+|!)[^&gt;]*&gt;/,</span></span><br><span class="line">      <span class="keyword">if</span> (selector[<span class="number">0</span>] == <span class="string">"&lt;"</span> &amp;&amp; fragmentRE.test(selector))</span><br><span class="line">        <span class="comment">// 第一，RegExp.$1取出来的就是第一个标签名称，即正则中 (\w+|!) 对应的内容</span></span><br><span class="line">        <span class="comment">// 第二，此时的 context 应该传入的是css属性对象（这里会产生歧义，老版的不会传入 context）</span></span><br><span class="line">        (dom = zepto.fragment(selector, <span class="built_in">RegExp</span>.$<span class="number">1</span>, context)), (selector = <span class="literal">null</span>);</span><br><span class="line">      <span class="comment">// If there's a context, create a collection on that context first, and select</span></span><br><span class="line">      <span class="comment">// nodes from there</span></span><br><span class="line">      <span class="comment">// 如果 selector 不是html字符串标签，并且 context 有值，则从context中查找</span></span><br><span class="line">      <span class="comment">// find 应该是在 $.fn 中定义的，有待解读？？？</span></span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (context !== <span class="literal">undefined</span>) <span class="keyword">return</span> $(context).find(selector);</span><br><span class="line">      <span class="comment">// If it's a CSS selector, use it to select nodes.</span></span><br><span class="line">      <span class="comment">// 除了以上情况，就从整个 document 执行 qsa 的查找</span></span><br><span class="line">      <span class="keyword">else</span> dom = zepto.qsa(<span class="built_in">document</span>, selector);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// If a function is given, call it when the DOM is ready</span></span><br><span class="line">    <span class="comment">// 如果是函数，则dom ready时执行，</span></span><br><span class="line">    <span class="comment">// ready方法应该在 $.fn 中定义，有待解毒</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (isFunction(selector)) <span class="keyword">return</span> $(<span class="built_in">document</span>).ready(selector);</span><br><span class="line">    <span class="comment">// If a Zepto collection is given, just return it</span></span><br><span class="line">    <span class="comment">// 传入的参数本身就已经是 zepto 对象，则直接返回</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (zepto.isZ(selector)) <span class="keyword">return</span> selector;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// compact函数：踢出数组中 == null 的元素</span></span><br><span class="line">      <span class="comment">// normalize array if an array of nodes is given</span></span><br><span class="line">      <span class="keyword">if</span> (isArray(selector)) dom = compact(selector);</span><br><span class="line">      <span class="comment">// 如果传入的是object，直接强制塞进一个数组</span></span><br><span class="line">      <span class="comment">// Wrap DOM nodes.</span></span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (isObject(selector)) (dom = [selector]), (selector = <span class="literal">null</span>);</span><br><span class="line">      <span class="comment">// 及时清空 selector 不妨碍下面的判断</span></span><br><span class="line">      <span class="comment">// 从此往下，感觉和上文 selector 是字符串的情况下，重复了</span></span><br><span class="line">      <span class="comment">// ？？？？？？？？</span></span><br><span class="line">      <span class="comment">// fragmentRE.test 即判断字符串是否是 html 标签开头（即是否是html fragement）</span></span><br><span class="line">      <span class="comment">// If it's a html fragment, create nodes from it</span></span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (fragmentRE.test(selector))</span><br><span class="line">        <span class="comment">//此时，context 也是属性集合，不是容器！！！</span></span><br><span class="line">        <span class="comment">//（这里会产生歧义，老版的不会传入 context）</span></span><br><span class="line">        (dom = zepto.fragment(selector.trim(), <span class="built_in">RegExp</span>.$<span class="number">1</span>, context)),</span><br><span class="line">          (selector = <span class="literal">null</span>);</span><br><span class="line">      <span class="comment">// 及时清空 selector 不妨碍下面的判断</span></span><br><span class="line">      <span class="comment">// If there's a context, create a collection on that context first, and select</span></span><br><span class="line">      <span class="comment">// nodes from there</span></span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (context !== <span class="literal">undefined</span>) <span class="keyword">return</span> $(context).find(selector);</span><br><span class="line">      <span class="comment">// And last but no least, if it's a CSS selector, use it to select nodes.</span></span><br><span class="line">      <span class="keyword">else</span> dom = zepto.qsa(<span class="built_in">document</span>, selector);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最终，还是通过 zepto.Z 创建了对象</span></span><br><span class="line">    <span class="comment">// 这里的 dom，其实就是一个数组</span></span><br><span class="line">    <span class="comment">// create a new Zepto collection from the nodes found</span></span><br><span class="line">    <span class="keyword">return</span> zepto.Z(dom, selector);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// `$` will be the base `Zepto` object. When calling this</span></span><br><span class="line">  <span class="comment">// function just call `$.zepto.init, which makes the implementation</span></span><br><span class="line">  <span class="comment">// details of selecting nodes and creating Zepto collections</span></span><br><span class="line">  <span class="comment">// patchable in plugins.</span></span><br><span class="line">  $ = <span class="function"><span class="keyword">function</span>(<span class="params">selector, context</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> zepto.init(selector, context);</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="comment">// $ 最终被这个匿名函数所返回，并复制给了全局的 Zepto 变量</span></span><br><span class="line">  <span class="comment">// 全局的 zepto 变量暴露给了 window，并且可能有一个别名—— $</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="function"><span class="keyword">function</span> <span class="title">extend</span>(<span class="params">target, source, deep</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// key 在上文已经定义，否则就污染全局变量了</span></span><br><span class="line">    <span class="comment">// 深度递归，首先必须 deep 参数为 true</span></span><br><span class="line">    <span class="comment">// 其次，source[key] 必须是数组或者对象，才有必要深度递归（否则没必要）</span></span><br><span class="line">    <span class="keyword">for</span> (key <span class="keyword">in</span> source)</span><br><span class="line">      <span class="keyword">if</span> (deep &amp;&amp; (isPlainObject(source[key]) || isArray(source[key]))) &#123;</span><br><span class="line">        <span class="comment">// source[key] 是对象，而 target[key] 不是对象</span></span><br><span class="line">        <span class="comment">// 则 target[key] = &#123;&#125; 初始化一下，否则递归会出错的</span></span><br><span class="line">        <span class="keyword">if</span> (isPlainObject(source[key]) &amp;&amp; !isPlainObject(target[key]))</span><br><span class="line">          target[key] = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// source[key] 是数组，而 target[key] 不是数组</span></span><br><span class="line">        <span class="comment">// 则 target[key] = [] 初始化一下，否则递归会出错的</span></span><br><span class="line">        <span class="keyword">if</span> (isArray(source[key]) &amp;&amp; !isArray(target[key])) target[key] = [];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 执行递归</span></span><br><span class="line">        extend(target[key], source[key], deep);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 不满足以上条件，说明 source[key] 是一般的值类型，直接赋值给 target 就是了</span></span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (source[key] !== <span class="literal">undefined</span>) target[key] = source[key];</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Copy all but undefined properties from one or more</span></span><br><span class="line">  <span class="comment">// objects to the `target` object.</span></span><br><span class="line">  $.extend = <span class="function"><span class="keyword">function</span>(<span class="params">target</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 一般传入的参数会是：</span></span><br><span class="line">    <span class="comment">// (targetObj, srcObj1, srcObj2, srcObj1...)</span></span><br><span class="line">    <span class="comment">// (true, targetObj, srcObj1, srcObj2, srcObj1...)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// arguments 是对象数组，slice.call 会返回真正的数组（此处返回从第二项开始）</span></span><br><span class="line">    <span class="keyword">var</span> deep,</span><br><span class="line">      args = slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 第一个参数是boolean，这里会把第二个参数当做 target，其他的作为 source</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> target == <span class="string">"boolean"</span>) &#123;</span><br><span class="line">      deep = target;</span><br><span class="line">      target = args.shift();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将所有的 source 添加到 target 中</span></span><br><span class="line">    args.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">arg</span>) </span>&#123;</span><br><span class="line">      extend(target, arg, deep);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> target;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 感觉这样设计是比较好，很好的将业务和底层进行了分离（虽然比较简单）：</span></span><br><span class="line">    <span class="comment">// 核心方法再 function extend(...)&#123;...&#125; 中定义，</span></span><br><span class="line">    <span class="comment">// 而 $.extend 方法中做一些外围的判断和处理，最终调用 extend 函数去执行</span></span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// `$.zepto.qsa` is Zepto's CSS selector implementation which</span></span><br><span class="line">  <span class="comment">// uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.</span></span><br><span class="line">  <span class="comment">// This method can be overriden in plugins.</span></span><br><span class="line">  zepto.qsa = <span class="function"><span class="keyword">function</span>(<span class="params">element, selector</span>) </span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">      @element: 容器</span></span><br><span class="line"><span class="comment">      @selector: 选择器</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> found,</span><br><span class="line">      maybeID = selector[<span class="number">0</span>] == <span class="string">"#"</span>,</span><br><span class="line">      maybeClass = !maybeID &amp;&amp; selector[<span class="number">0</span>] == <span class="string">"."</span>,</span><br><span class="line">      <span class="comment">// ID或class形式：返回 selector.slice(1) 即ID或者class的值</span></span><br><span class="line">      <span class="comment">// 否则：返回 selector，如通过 tagName 查询</span></span><br><span class="line">      nameOnly = maybeID || maybeClass ? selector.slice(<span class="number">1</span>) : selector, <span class="comment">// Ensure that a 1 char tag name still gets checked</span></span><br><span class="line">      <span class="comment">// 是否是一个简单的字符串（可能是一个复杂的选择器，如 'div#div1 .item[link] .red'）</span></span><br><span class="line">      isSimple = simpleSelectorRE.test(nameOnly);</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">// simpleSelectorRE = /^[\w-]*$/,</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以下代码的基本思路是：</span></span><br><span class="line">    <span class="comment">// 1. 优先通过 ID 获取元素；</span></span><br><span class="line">    <span class="comment">// 2. 然后试图通过 className 和 tagName 获取元素</span></span><br><span class="line">    <span class="comment">// 3. 最后通过 querySelectorAll 来获取</span></span><br><span class="line">    <span class="keyword">return</span> isDocument(element) &amp;&amp; isSimple &amp;&amp; maybeID</span><br><span class="line">      ? <span class="comment">// 这是最简单的形式：容器是document、选择器是一个id</span></span><br><span class="line">        <span class="comment">// 因为 getElementById 只能在 document 上用，所以这里单独拿出来</span></span><br><span class="line">        (found = element.getElementById(nameOnly))</span><br><span class="line">        ? [found]</span><br><span class="line">        : []</span><br><span class="line">      : element.nodeType !== <span class="number">1</span> &amp;&amp; element.nodeType !== <span class="number">9</span></span><br><span class="line">      ? <span class="comment">// 容器不是一般元素，也不是document，直接返回 []</span></span><br><span class="line">        []</span><br><span class="line">      : <span class="comment">// 将获取的所有元素集合，都转换为数组</span></span><br><span class="line">        slice.call(</span><br><span class="line">          isSimple &amp;&amp; !maybeID</span><br><span class="line">            ? <span class="comment">// isSimple情况下，nameOnly 只可能是 className 或者 tagName</span></span><br><span class="line">              <span class="comment">// getElementsByClassName 和 getElementsByTagName 可以在 elem 上用，而且比 querySelectorAll 速度快</span></span><br><span class="line">              <span class="comment">// 所以，只要elem容器有值，尽量单独拿出来处理</span></span><br><span class="line">              maybeClass</span><br><span class="line">              ? element.getElementsByClassName(nameOnly) <span class="comment">// If it's simple, it could be a class</span></span><br><span class="line">              : element.getElementsByTagName(selector) <span class="comment">// Or a tag</span></span><br><span class="line">            : <span class="comment">// 最后其他情况，只能通过 querySelectorAll 来处理</span></span><br><span class="line">              element.querySelectorAll(selector) <span class="comment">// Or it's not simple, and we need to query all</span></span><br><span class="line">        );</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 根据 selector 筛选 nodes</span></span><br><span class="line">  <span class="comment">// 并将 nodes 封装为 zepto 对象</span></span><br><span class="line">  <span class="comment">// $.fn.filter 下文定义</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">filtered</span>(<span class="params">nodes, selector</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> selector == <span class="literal">null</span> ? $(nodes) : $(nodes).filter(selector);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 判断 parent 是否包含 node</span></span><br><span class="line">  $.contains = <span class="built_in">document</span>.documentElement.contains</span><br><span class="line">    ? <span class="comment">// 浏览器支持 contains 方法</span></span><br><span class="line">      <span class="function"><span class="keyword">function</span>(<span class="params">parent, node</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> parent !== node &amp;&amp; parent.contains(node);</span><br><span class="line">      &#125;</span><br><span class="line">    : <span class="comment">// 不支持 contains 方法</span></span><br><span class="line">      <span class="function"><span class="keyword">function</span>(<span class="params">parent, node</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (node &amp;&amp; (node = node.parentNode))</span><br><span class="line">          <span class="keyword">if</span> (node === parent) <span class="keyword">return</span> <span class="literal">true</span>;</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="comment">// 如果 arg 是函数，则改变函数的执行环境和参数</span></span><br><span class="line">  <span class="comment">// 如果不是，直接返回 arg</span></span><br><span class="line">  <span class="comment">// $.fn.html 方法就用到了</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">funcArg</span>(<span class="params">context, arg, idx, payload</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> isFunction(arg) ? arg.call(context, idx, payload) : arg;</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">setAttribute</span>(<span class="params">node, name, value</span>) </span>&#123;</span><br><span class="line">    value == <span class="literal">null</span> ? node.removeAttribute(name) : node.setAttribute(name, value);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 设置或获取 node 的 className</span></span><br><span class="line">  <span class="comment">// 考虑 svg ？？？？</span></span><br><span class="line">  <span class="comment">// access className property while respecting SVGAnimatedString</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">className</span>(<span class="params">node, value</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> klass = node.className || <span class="string">""</span>,</span><br><span class="line">      svg = klass &amp;&amp; klass.baseVal !== <span class="literal">undefined</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取</span></span><br><span class="line">    <span class="keyword">if</span> (value === <span class="literal">undefined</span>) <span class="keyword">return</span> svg ? klass.baseVal : klass;</span><br><span class="line">    <span class="comment">// 设置</span></span><br><span class="line">    svg ? (klass.baseVal = value) : (node.className = value);</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">// "true"  =&gt; true</span></span><br><span class="line">  <span class="comment">// "false" =&gt; false</span></span><br><span class="line">  <span class="comment">// "null"  =&gt; null</span></span><br><span class="line">  <span class="comment">// "42"    =&gt; 42</span></span><br><span class="line">  <span class="comment">// "42.5"  =&gt; 42.5</span></span><br><span class="line">  <span class="comment">// "08"    =&gt; "08"</span></span><br><span class="line">  <span class="comment">// JSON    =&gt; parse if valid</span></span><br><span class="line">  <span class="comment">// String  =&gt; self</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">deserializeValue</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> value</span><br><span class="line">        ? <span class="comment">// value『有值』的情况：</span></span><br><span class="line">          value == <span class="string">"true"</span> || <span class="comment">// 如果 value == 'true'，那么这个表达式本身就返回 true ，导致整个函数返回true</span></span><br><span class="line">            <span class="comment">// value !== 'true' 的情况：</span></span><br><span class="line">            (value == <span class="string">"false"</span></span><br><span class="line">              ? <span class="literal">false</span> <span class="comment">// "null"  =&gt; null</span></span><br><span class="line">              : value == <span class="string">"null"</span></span><br><span class="line">              ? <span class="literal">null</span> <span class="comment">// "null"  =&gt; null</span></span><br><span class="line">              : +value + <span class="string">""</span> == value</span><br><span class="line">              ? +value <span class="comment">// 数字："42" =&gt; 42  "42.5" =&gt; 42.5  （ 但是 '08' 却不符合这个条件 ）</span></span><br><span class="line">              : <span class="regexp">/^[\[\&#123;]/</span>.test(value)</span><br><span class="line">              ? $.parseJSON(value) <span class="comment">// '[...]' 或者 '&#123;...&#125;'</span></span><br><span class="line">              : value) <span class="comment">// 其他</span></span><br><span class="line">        : <span class="comment">// value『无值』的情况： undefined / '' / flase / 0 / null</span></span><br><span class="line">          value;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">      <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 将上文定义的函数，暴露给 $ 对象（其实 $ 是一个 function）</span></span><br><span class="line">  $.type = type;</span><br><span class="line">  $.isFunction = isFunction;</span><br><span class="line">  $.isWindow = isWindow;</span><br><span class="line">  $.isArray = isArray;</span><br><span class="line">  $.isPlainObject = isPlainObject;</span><br><span class="line"></span><br><span class="line">  $.isEmptyObject = <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> name;</span><br><span class="line">    <span class="keyword">for</span> (name <span class="keyword">in</span> obj) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  $.inArray = <span class="function"><span class="keyword">function</span>(<span class="params">elem, array, i</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> emptyArray.indexOf.call(array, elem, i);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  $.camelCase = camelize;</span><br><span class="line">  $.trim = <span class="function"><span class="keyword">function</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> str == <span class="literal">null</span> ? <span class="string">""</span> : <span class="built_in">String</span>.prototype.trim.call(str);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// plugin compatibility</span></span><br><span class="line">  $.uuid = <span class="number">0</span>;</span><br><span class="line">  $.support = &#123;&#125;;</span><br><span class="line">  $.expr = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 重新组织 elements 对象（数组、对象或者对象数组），针对每一个元素，都用 callback 进行检验</span></span><br><span class="line">  <span class="comment">// 检验通过后，将元素push进一个新数组，并返回</span></span><br><span class="line">  $.map = <span class="function"><span class="keyword">function</span>(<span class="params">elements, callback</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> value,</span><br><span class="line">      values = [],</span><br><span class="line">      i,</span><br><span class="line">      key;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 数组，或者对象数组</span></span><br><span class="line">    <span class="keyword">if</span> (likeArray(elements))</span><br><span class="line">      <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; elements.length; i++) &#123;</span><br><span class="line">        <span class="comment">// 遍历，经过 callback 验证，push到结果中</span></span><br><span class="line">        value = callback(elements[i], i);</span><br><span class="line">        <span class="keyword">if</span> (value != <span class="literal">null</span>) values.push(value);</span><br><span class="line">      &#125;</span><br><span class="line">    <span class="comment">// 对象</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">for</span> (key <span class="keyword">in</span> elements) &#123;</span><br><span class="line">        <span class="comment">// 遍历，经过 callback 验证，push到结果中</span></span><br><span class="line">        value = callback(elements[key], key);</span><br><span class="line">        <span class="keyword">if</span> (value != <span class="literal">null</span>) values.push(value);</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">// flatten 函数上文定义的，作用：无论 values 是否是数组，都将返回一个正确的数组。例如，传入 'abc' ，返回 ['abc']</span></span><br><span class="line">    <span class="keyword">return</span> flatten(values);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 遍历 elements 所有元素（数组、对象数组、对象），执行 callback 方法，最终还是返回 elements</span></span><br><span class="line">  <span class="comment">// 注意1：callback.call(elements[i], i, elements[i]) 函数执行的环境和参数</span></span><br><span class="line">  <span class="comment">// 注意2：=== false) return elements 一旦有函数返回 false，即跳出循环，类似 break</span></span><br><span class="line">  <span class="comment">// 注意3：无论哪种情况，最终返回的还是 elements</span></span><br><span class="line">  $.each = <span class="function"><span class="keyword">function</span>(<span class="params">elements, callback</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> i, key;</span><br><span class="line">    <span class="keyword">if</span> (likeArray(elements)) &#123;</span><br><span class="line">      <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; elements.length; i++)</span><br><span class="line">        <span class="keyword">if</span> (callback.call(elements[i], i, elements[i]) === <span class="literal">false</span>)</span><br><span class="line">          <span class="keyword">return</span> elements;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">for</span> (key <span class="keyword">in</span> elements)</span><br><span class="line">        <span class="keyword">if</span> (callback.call(elements[key], key, elements[key]) === <span class="literal">false</span>)</span><br><span class="line">          <span class="keyword">return</span> elements;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> elements;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 上文定义：filter = emptyArray.filter</span></span><br><span class="line">  <span class="comment">// 筛选数组</span></span><br><span class="line">  $.grep = <span class="function"><span class="keyword">function</span>(<span class="params">elements, callback</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> filter.call(elements, callback);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">window</span>.JSON) $.parseJSON = <span class="built_in">JSON</span>.parse;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Populate the class2type map</span></span><br><span class="line">  $.each(</span><br><span class="line">    <span class="string">"Boolean Number String Function Array Date RegExp Object Error"</span>.split(<span class="string">" "</span>),</span><br><span class="line">    <span class="function"><span class="keyword">function</span>(<span class="params">i, name</span>) </span>&#123;</span><br><span class="line">      class2type[<span class="string">"[object "</span> + name + <span class="string">"]"</span>] = name.toLowerCase();</span><br><span class="line"></span><br><span class="line">      <span class="comment">/*</span></span><br><span class="line"><span class="comment">      上文将 class2type 赋值为 &#123;&#125;</span></span><br><span class="line"><span class="comment">      最终将 class2type 赋值为：</span></span><br><span class="line"><span class="comment">      &#123;</span></span><br><span class="line"><span class="comment">        '[object boolean]': 'boolean',</span></span><br><span class="line"><span class="comment">        '[object number]': 'number',</span></span><br><span class="line"><span class="comment">        '[object string]': 'string',</span></span><br><span class="line"><span class="comment">        ...</span></span><br><span class="line"><span class="comment">      &#125;</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">      例如 Object.prototype.toString.call([]) 返回的是 '[Object Array]'</span></span><br><span class="line"><span class="comment">      那么即可根据这个获取 [] 的类型是 'array'</span></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><br><span class="line">  <span class="comment">// Define methods that will be available on all</span></span><br><span class="line">  <span class="comment">// Zepto collections</span></span><br><span class="line">  $.fn = &#123;</span><br><span class="line">    <span class="comment">// 为何要这么多数组的方法？</span></span><br><span class="line">    <span class="comment">// 因为一个 zepto 对象，本身就是一个数组</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// Because a collection acts like an array</span></span><br><span class="line">    <span class="comment">// copy over these useful array functions.</span></span><br><span class="line">    forEach: emptyArray.forEach,</span><br><span class="line">    reduce: emptyArray.reduce, <span class="comment">// 方法何用？？？？</span></span><br><span class="line">    push: emptyArray.push,</span><br><span class="line">    sort: emptyArray.sort,</span><br><span class="line">    indexOf: emptyArray.indexOf,</span><br><span class="line">    concat: emptyArray.concat,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// `map` and `slice` in the jQuery API work differently</span></span><br><span class="line">    <span class="comment">// from their array counterparts</span></span><br><span class="line">    map: <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// $.map 上文定义的， $.map = function (elements, callback) &#123;....&#125;</span></span><br><span class="line">      <span class="comment">// $.map 作用：针对 elements（对象、对象数组或数组），对每个元素都经过 callback 函数的过滤，并将过滤通过的元素，push到一个新数组中，返回新数组</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><br><span class="line">        <span class="comment">// $.map 返回的是一个数组</span></span><br><span class="line">        $.map(</span><br><span class="line">          <span class="keyword">this</span>,</span><br><span class="line">          <span class="comment">// 针对每一个元素，都执行传入的函数，如果函数返回的 !=null 就将插入到新返回的数组</span></span><br><span class="line">          <span class="function"><span class="keyword">function</span>(<span class="params">el, i</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> fn.call(el, i, el);</span><br><span class="line">          &#125;</span><br><span class="line">        )</span><br><span class="line">      );</span><br><span class="line"></span><br><span class="line">      <span class="comment">/*</span></span><br><span class="line"><span class="comment">        $('div').map(function(key, value)&#123;</span></span><br><span class="line"><span class="comment">          return value.id;</span></span><br><span class="line"><span class="comment">          // 或者 return this.id;</span></span><br><span class="line"><span class="comment">        &#125;)</span></span><br><span class="line"><span class="comment">        这个结果就是 $(['div1', 'div2' ...])</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    &#125;,</span><br><span class="line">    slice: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 直接数组的slice方法，并将结果用 $ 封装返回</span></span><br><span class="line">      <span class="keyword">return</span> $(slice.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>));</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在 zepto.init 函数中，当传入的函数是函数时，就用到了 ready</span></span><br><span class="line">    <span class="comment">// else if (isFunction(selector)) return $(document).ready(selector)</span></span><br><span class="line">    ready: <span class="function"><span class="keyword">function</span>(<span class="params">callback</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// need to check if document.body exists for IE as that browser reports</span></span><br><span class="line">      <span class="comment">// document ready when it hasn't yet created the body element</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 下文定义：readyRE = /complete|loaded|interactive/,</span></span><br><span class="line">      <span class="keyword">if</span> (readyRE.test(<span class="built_in">document</span>.readyState) &amp;&amp; <span class="built_in">document</span>.body) callback($);</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">document</span>.addEventListener(</span><br><span class="line">          <span class="string">"DOMContentLoaded"</span>,</span><br><span class="line">          <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">            callback($);</span><br><span class="line">          &#125;,</span><br><span class="line">          <span class="literal">false</span></span><br><span class="line">        );</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="keyword">this</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    get: <span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> idx === <span class="literal">undefined</span></span><br><span class="line">        ? slice.call(<span class="keyword">this</span>) <span class="comment">// 未传参数，直接返回一整个数组</span></span><br><span class="line">        : <span class="comment">// 有参数，则试图返回单个元素（大于0，小于0 两种情况）</span></span><br><span class="line">          <span class="keyword">this</span>[idx &gt;= <span class="number">0</span> ? idx : idx + <span class="keyword">this</span>.length];</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将zepto集合变为纯数组</span></span><br><span class="line">    toArray: <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="keyword">this</span>.get();</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    size: <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="keyword">this</span>.length;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将元素从这个DOM树中移除</span></span><br><span class="line">    remove: <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="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>.parentNode != <span class="literal">null</span>) <span class="keyword">this</span>.parentNode.removeChild(<span class="keyword">this</span>);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    each: <span class="function"><span class="keyword">function</span>(<span class="params">callback</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// [].every ES5中Array的新特性。循环数组每个元素，返回是否符合callback函数的要求</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// every 函数返回的是 false 或者 true（不过这里返回什么无所谓，执行就可以了）</span></span><br><span class="line">      emptyArray.every.call(<span class="keyword">this</span>, <span class="function"><span class="keyword">function</span>(<span class="params">el, idx</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> callback.call(el, idx, el) !== <span class="literal">false</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="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    filter: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// not函数下文定义</span></span><br><span class="line">      <span class="comment">// 如果给not传入的参数是函数，则返回不符合这个函数规则的元素的数组（用 $ 封装）</span></span><br><span class="line">      <span class="keyword">if</span> (isFunction(selector)) <span class="keyword">return</span> <span class="keyword">this</span>.not(<span class="keyword">this</span>.not(selector));</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 上文定义：zepto.matches 判断elements是否符合 selector 的要求</span></span><br><span class="line">      <span class="comment">// zepto.matches = function(element, selector) &#123;...&#125;</span></span><br><span class="line">      <span class="keyword">return</span> $(</span><br><span class="line">        filter.call(<span class="keyword">this</span>, <span class="function"><span class="keyword">function</span>(<span class="params">element</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// 利用 [].filter 方法做筛选，利用 zepto.matches 做判断</span></span><br><span class="line">          <span class="keyword">return</span> zepto.matches(element, selector);</span><br><span class="line">        &#125;)</span><br><span class="line">      );</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// $('div') 可能只有三个 div 节点，那么 $('div').add('p') 再三个 div 节点的基础上，增加三个 p 节点</span></span><br><span class="line">    add: <span class="function"><span class="keyword">function</span>(<span class="params">selector, context</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// uniq函数——数组去重，例如：用来将 [1,1,2,2,3,3] 替换为 [1,2,3]</span></span><br><span class="line">      <span class="keyword">return</span> $(uniq(<span class="keyword">this</span>.concat($(selector, context))));</span><br><span class="line">    &#125;,</span><br><span class="line">    is: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 注意：这里只对 this[0] 第一个元素做判断了，其他的元素不管了</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.length &gt; <span class="number">0</span> &amp;&amp; zepto.matches(<span class="keyword">this</span>[<span class="number">0</span>], selector);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    not: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> nodes = []; <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="keyword">if</span> (isFunction(selector) &amp;&amp; selector.call !== <span class="literal">undefined</span>)</span><br><span class="line">        <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// 遍历对象的所有元素，对每个元素都执行传入的函数</span></span><br><span class="line">          <span class="comment">// 当函数返回 false 时（即不符合函数的规则），则将当前元素push到结果中，等待返回</span></span><br><span class="line">          <span class="keyword">if</span> (!selector.call(<span class="keyword">this</span>, idx)) nodes.push(<span class="keyword">this</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">      <span class="comment">// 如果参数不是函数</span></span><br><span class="line">      <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 为 excludes 赋值</span></span><br><span class="line">        <span class="keyword">var</span> excludes =</span><br><span class="line">          <span class="comment">// 如果 selector 是字符串（css选择器），则用filter过滤，将结果存储到 excludes 中</span></span><br><span class="line">          <span class="keyword">typeof</span> selector == <span class="string">"string"</span></span><br><span class="line">            ? <span class="keyword">this</span>.filter(selector)</span><br><span class="line">            : <span class="comment">// 如果 selector 不是字符串</span></span><br><span class="line">            <span class="comment">// 如果是数组或者对象数组（并且 selector.item 是函数？？？），则生成数组，赋值给 excludes</span></span><br><span class="line">            likeArray(selector) &amp;&amp; isFunction(selector.item)</span><br><span class="line">            ? slice.call(selector)</span><br><span class="line">            : <span class="comment">// 否则直接生成 zepto 对象，赋值给 excludes</span></span><br><span class="line">              $(selector);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 至此，excludes 中就存储了通过 selector 查找出来的元素</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// [].forEach 是ES5的新特性</span></span><br><span class="line">        <span class="keyword">this</span>.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">el</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// 取出 excludes 中不包含的元素，push到结果中</span></span><br><span class="line">          <span class="keyword">if</span> (excludes.indexOf(el) &lt; <span class="number">0</span>) nodes.push(el);</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="keyword">return</span> $(nodes);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    has: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 经过 filter 函数处理，返回的是一个处理后的值</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.filter(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> isObject(selector)</span><br><span class="line">          ? <span class="comment">// 如果 seletor 是 object（可能是elem节点），则用 $.contains 判断</span></span><br><span class="line">            $.contains(<span class="keyword">this</span>, selector)</span><br><span class="line">          : <span class="comment">// 否则（selector是css选择字符串）则返回find后的size（如果 size === 0 即相当于返回 false）</span></span><br><span class="line">            $(<span class="keyword">this</span>)</span><br><span class="line">              .find(selector)</span><br><span class="line">              .size();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// $.fn.find 在下文定义</span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    eq: <span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 取出指定index的元素</span></span><br><span class="line">      <span class="comment">// 可支持 -1、0、1、2 ……</span></span><br><span class="line">      <span class="keyword">return</span> idx === <span class="number">-1</span> ? <span class="keyword">this</span>.slice(idx) : <span class="keyword">this</span>.slice(idx, +idx + <span class="number">1</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    first: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> el = <span class="keyword">this</span>[<span class="number">0</span>];</span><br><span class="line">      <span class="comment">// 不是 object 则直接返回</span></span><br><span class="line">      <span class="comment">// 是 object 类型，则用 $ 封装 （因为时刻都要支持链式操作！！！）</span></span><br><span class="line">      <span class="keyword">return</span> el &amp;&amp; !isObject(el) ? el : $(el);</span><br><span class="line">    &#125;,</span><br><span class="line">    last: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> el = <span class="keyword">this</span>[<span class="keyword">this</span>.length - <span class="number">1</span>];</span><br><span class="line">      <span class="keyword">return</span> el &amp;&amp; !isObject(el) ? el : $(el);</span><br><span class="line">    &#125;,</span><br><span class="line">    find: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// result 存储返回结果</span></span><br><span class="line">      <span class="keyword">var</span> result,</span><br><span class="line">        $<span class="keyword">this</span> = <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 如果没有参数，就返回一个空的 zepto 对象</span></span><br><span class="line">      <span class="keyword">if</span> (!selector) result = $();</span><br><span class="line">      <span class="comment">// 如果selector是对象</span></span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> selector == <span class="string">"object"</span>)</span><br><span class="line">        result = $(selector).filter(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">          <span class="keyword">var</span> node = <span class="keyword">this</span>;</span><br><span class="line">          <span class="keyword">return</span> emptyArray.some.call($<span class="keyword">this</span>, <span class="function"><span class="keyword">function</span>(<span class="params">parent</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> $.contains(parent, node);</span><br><span class="line">          &#125;);</span><br><span class="line">        &#125;);</span><br><span class="line">      <span class="comment">// 如果 selector 不是对象（即是css选择器）：</span></span><br><span class="line">      <span class="comment">// 如果只有一个元素，则使用 qsa 判断，结果经过 $ 封装后赋值给 result</span></span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">this</span>.length == <span class="number">1</span>) result = $(zepto.qsa(<span class="keyword">this</span>[<span class="number">0</span>], selector));</span><br><span class="line">      <span class="comment">// 如果有多个元素，则使用 map 遍历所有元素，使用 qsa 针对每个元素判断，符合条件即返回（map将返回包含符合条件的元素的新数组，并 $ 封装，支持链式操作！！）</span></span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        result = <span class="keyword">this</span>.map(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">          <span class="keyword">return</span> zepto.qsa(<span class="keyword">this</span>, selector);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 返回最终结果</span></span><br><span class="line">      <span class="keyword">return</span> result;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从元素本身开始，逐级向上级元素匹配，并返回最先匹配selector的元素</span></span><br><span class="line">    closest: <span class="function"><span class="keyword">function</span>(<span class="params">selector, context</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> node = <span class="keyword">this</span>[<span class="number">0</span>],</span><br><span class="line">        collection = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 如果 selector 是对象，则用 $ 封装后，赋值给 collection</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">typeof</span> selector == <span class="string">"object"</span>) collection = $(selector);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">while</span> (</span><br><span class="line">        <span class="comment">// while循环的判断条件：</span></span><br><span class="line">        <span class="comment">// 第一，node有值（node一开始被赋值为对象的第一个元素）</span></span><br><span class="line">        <span class="comment">// 第二，collection有值（传入的selector是对象）则collection包含node；collection无值（传入的selector是字符串，css选择），则node满足selector条件</span></span><br><span class="line">        <span class="comment">// 满足第一个条件，不满足第二条件，则循环继续（node试图赋值为node.parentNode）；否则，循环跳出（说明已经找到了符合条件的父节点）</span></span><br><span class="line">        node &amp;&amp;</span><br><span class="line">        !(collection</span><br><span class="line">          ? collection.indexOf(node) &gt;= <span class="number">0</span></span><br><span class="line">          : zepto.matches(node, selector))</span><br><span class="line">      )</span><br><span class="line">        <span class="comment">// node赋值成 node.parentNode</span></span><br><span class="line">        <span class="comment">// 前提条件是：node != context &amp;&amp; node 不是 document，如果是这两个条件之一，那就不继续赋值</span></span><br><span class="line">        node = node !== context &amp;&amp; !isDocument(node) &amp;&amp; node.parentNode;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 返回最终结果</span></span><br><span class="line">      <span class="keyword">return</span> $(node);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取对象集合每个元素所有的祖先元素。 $('h1').parents() =&gt; [&lt;div#container&gt;, &lt;body&gt;, &lt;html&gt;]</span></span><br><span class="line">    parents: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> ancestors = [],</span><br><span class="line">        nodes = <span class="keyword">this</span>;</span><br><span class="line">      <span class="keyword">while</span> (nodes.length &gt; <span class="number">0</span>)</span><br><span class="line">        <span class="comment">// 可能需要执行多次 while 循环</span></span><br><span class="line">        <span class="comment">// 每次执行 $.map 函数都会对 nodes 重新赋值，然后再判断是否需要继续循环</span></span><br><span class="line">        <span class="comment">// 因为要获取每个元素的所有祖先元素，所以要多次循环</span></span><br><span class="line">        nodes = $.map(nodes, <span class="function"><span class="keyword">function</span>(<span class="params">node</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// 使用 $.map（返回符合条件的元素的新数组，并用 $ 封装）遍历所有元素</span></span><br><span class="line">          <span class="keyword">if</span> (</span><br><span class="line">            (node = node.parentNode) &amp;&amp;</span><br><span class="line">            !isDocument(node) &amp;&amp;</span><br><span class="line">            ancestors.indexOf(node) &lt; <span class="number">0</span></span><br><span class="line">          ) &#123;</span><br><span class="line">            <span class="comment">// 将符合条件的元素push到结果中</span></span><br><span class="line">            <span class="comment">// 条件：不能是 document，结果中元素不能重复。否则不执行push</span></span><br><span class="line">            ancestors.push(node);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 返回的 node ，将拼接出新数组，重新复制给 nodes，然后试图继续执行 while 循环</span></span><br><span class="line">            <span class="keyword">return</span> node;</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">// 如果css选择器参数给出，过滤出符合条件的元素</span></span><br><span class="line">      <span class="keyword">return</span> filtered(ancestors, selector);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取对象集合中每个元素的直接父元素。如果css选择器参数给出。过滤出符合条件的元素。</span></span><br><span class="line">    parent: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// pluck 函数在下文定义</span></span><br><span class="line">      <span class="comment">// parent 函数，只获取第一级父节点即可</span></span><br><span class="line">      <span class="keyword">return</span> filtered(uniq(<span class="keyword">this</span>.pluck(<span class="string">"parentNode"</span>)), selector);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获得每个匹配元素集合元素的直接子元素，可通过 selector 过滤</span></span><br><span class="line">    children: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> filtered(</span><br><span class="line">        <span class="keyword">this</span>.map(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">          <span class="keyword">return</span> children(<span class="keyword">this</span>);</span><br><span class="line">        &#125;),</span><br><span class="line">        selector</span><br><span class="line">      );</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">    contents: <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="keyword">this</span>.map(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> slice.call(<span class="keyword">this</span>.childNodes);</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">// 获取对象集合中所有元素的兄弟节点，可通过 selector 过滤</span></span><br><span class="line">    siblings: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> filtered(</span><br><span class="line">        <span class="keyword">this</span>.map(<span class="function"><span class="keyword">function</span>(<span class="params">i, el</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// 获取兄弟节点</span></span><br><span class="line">          <span class="keyword">return</span> filter.call(children(el.parentNode), <span class="function"><span class="keyword">function</span>(<span class="params">child</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> child !== el;</span><br><span class="line">          &#125;);</span><br><span class="line">        &#125;),</span><br><span class="line">        selector</span><br><span class="line">      );</span><br><span class="line">    &#125;,</span><br><span class="line">    empty: <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="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.innerHTML = <span class="string">""</span>;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// `pluck` is borrowed from Prototype.js</span></span><br><span class="line">    pluck: <span class="function"><span class="keyword">function</span>(<span class="params">property</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 获取自定义属性，返回值，拼接数组</span></span><br><span class="line">      <span class="keyword">return</span> $.map(<span class="keyword">this</span>, <span class="function"><span class="keyword">function</span>(<span class="params">el</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> el[property];</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    show: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 返回当前对象，保证可链式操作</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 第一步，针对内联样式，将 none 改为空字符串，如 &lt;p id="p2" style="display:none;"&gt;p2&lt;/p&gt;</span></span><br><span class="line">        <span class="keyword">this</span>.style.display == <span class="string">"none"</span> &amp;&amp; (<span class="keyword">this</span>.style.display = <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 第二步，针对css样式，如果是 none 则修改为默认的显示样式</span></span><br><span class="line">        <span class="keyword">if</span> (getComputedStyle(<span class="keyword">this</span>, <span class="string">""</span>).getPropertyValue(<span class="string">"display"</span>) == <span class="string">"none"</span>)</span><br><span class="line">          <span class="keyword">this</span>.style.display = defaultDisplay(<span class="keyword">this</span>.nodeName);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// show 方法是为了显示对象，而对象隐藏的方式有两种：内联样式 或 css样式</span></span><br><span class="line">        <span class="comment">// this.style.display 只能获取内联样式的值（获取属性值）</span></span><br><span class="line">        <span class="comment">// getComputedStyle(this, '').getPropertyValue("display") 可以获取内联、css样式的值（获取 renderTree 的值）</span></span><br><span class="line">        <span class="comment">// 因此，这两步都要做判断，</span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    replaceWith: <span class="function"><span class="keyword">function</span>(<span class="params">newContent</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 先在前面插入，然后将当前对象移除</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.before(newContent).remove();</span><br><span class="line">    &#125;,</span><br><span class="line">    wrap: <span class="function"><span class="keyword">function</span>(<span class="params">structure</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 是否是函数</span></span><br><span class="line">      <span class="keyword">var</span> func = isFunction(structure);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>[<span class="number">0</span>] &amp;&amp; !func)</span><br><span class="line">        <span class="keyword">var</span> dom = $(structure).get(<span class="number">0</span>),</span><br><span class="line">          <span class="comment">// 何时用 clone ？</span></span><br><span class="line">          <span class="comment">// 第一，dom.parentNode 说明 dom 在文档结构中，不 clone 就会被移动</span></span><br><span class="line">          <span class="comment">// 第二，this.length &gt; 1 说明当前对象有多个元素，每个元素都要添加，所有要clone</span></span><br><span class="line">          clone = dom.parentNode || <span class="keyword">this</span>.length &gt; <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">index</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 借用 wrapAll 方法来做包装</span></span><br><span class="line">        $(<span class="keyword">this</span>).wrapAll(</span><br><span class="line">          func ? structure.call(<span class="keyword">this</span>, index) : clone ? dom.cloneNode(<span class="literal">true</span>) : dom</span><br><span class="line">        );</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 在所有匹配元素外面包一个单独的结构</span></span><br><span class="line">    wrapAll: <span class="function"><span class="keyword">function</span>(<span class="params">structure</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>[<span class="number">0</span>]) &#123;</span><br><span class="line">        <span class="comment">// 先将 structure 插入到文档结构</span></span><br><span class="line">        $(<span class="keyword">this</span>[<span class="number">0</span>]).before((structure = $(structure)));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> children;</span><br><span class="line">        <span class="comment">// drill down to the inmost element</span></span><br><span class="line">        <span class="comment">// 通过循环，将 structure 重新赋值为当前 structure 的最深处的一个子元素</span></span><br><span class="line">        <span class="keyword">while</span> ((children = structure.children()).length)</span><br><span class="line">          structure = children.first();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将所有子元素都包裹进 structure</span></span><br><span class="line">        $(structure).append(<span class="keyword">this</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="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    wrapInner: <span class="function"><span class="keyword">function</span>(<span class="params">structure</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 是否是函数</span></span><br><span class="line">      <span class="keyword">var</span> func = isFunction(structure);</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="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">index</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> self = $(<span class="keyword">this</span>),</span><br><span class="line">          contents = self.contents(),</span><br><span class="line">          <span class="comment">// 是函数，即获取函数执行的返回结果；否则直接用 structure 参数</span></span><br><span class="line">          dom = func ? structure.call(<span class="keyword">this</span>, index) : structure;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果当前元素有内容，则通过内容 wrapAll。无内容，则直接用自身的 append 增加</span></span><br><span class="line">        contents.length ? contents.wrapAll(dom) : self.append(dom);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    unwrap: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 通过 this.parent() 获取每个元素的父节点（集合）</span></span><br><span class="line">      <span class="comment">// 遍历这个父节点的集合</span></span><br><span class="line">      <span class="keyword">this</span>.parent().each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 将当前父节点替换为它的子节点</span></span><br><span class="line">        $(<span class="keyword">this</span>).replaceWith($(<span class="keyword">this</span>).children());</span><br><span class="line">      &#125;);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 返回对象自身，保证链式操作</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    clone: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 通过 this.map 循环对象，</span></span><br><span class="line">      <span class="comment">// 针对每个元素都返回它的clone         这个 this 和前面的 this 不一样</span></span><br><span class="line">      <span class="comment">// 返回新数组（用 $ 封装）</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.map(<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="keyword">this</span>.cloneNode(<span class="literal">true</span>);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    hide: <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="keyword">this</span>.css(<span class="string">"display"</span>, <span class="string">"none"</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 切换显示和隐藏</span></span><br><span class="line">    toggle: <span class="function"><span class="keyword">function</span>(<span class="params">setting</span>) </span>&#123;</span><br><span class="line">      <span class="comment">/*</span></span><br><span class="line"><span class="comment">        @setting</span></span><br><span class="line"><span class="comment">        true : 强制切换为 show</span></span><br><span class="line"><span class="comment">        false : 强制切换为 hide</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> el = $(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 条件判断：</span></span><br><span class="line">        <span class="comment">// 如果 setting === undefined 则看 el.css("display") == "none"</span></span><br><span class="line">        <span class="comment">// 如果 setting !== undefined 则看 !!setting</span></span><br><span class="line">        (setting === <span class="literal">undefined</span></span><br><span class="line">        ? el.css(<span class="string">"display"</span>) == <span class="string">"none"</span></span><br><span class="line">        : setting)</span><br><span class="line">          ? el.show() <span class="comment">// 如果 true 则显示</span></span><br><span class="line">          : el.hide(); <span class="comment">// 如果 false 则隐藏</span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 借助 previousElementSibling 属性</span></span><br><span class="line">    prev: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> $(<span class="keyword">this</span>.pluck(<span class="string">"previousElementSibling"</span>)).filter(selector || <span class="string">"*"</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 借助 nextElementSibling 属性</span></span><br><span class="line">    next: <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> $(<span class="keyword">this</span>.pluck(<span class="string">"nextElementSibling"</span>)).filter(selector || <span class="string">"*"</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    html: <span class="function"><span class="keyword">function</span>(<span class="params">html</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 情况1：有参数，赋值，并返回自身</span></span><br><span class="line">      <span class="number">0</span> <span class="keyword">in</span> <span class="built_in">arguments</span></span><br><span class="line">        ? <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">var</span> originHtml = <span class="keyword">this</span>.innerHTML;</span><br><span class="line">            <span class="comment">// 传入的 html 参数允许是一个字符串，也允许是一个函数</span></span><br><span class="line">            <span class="comment">// 通过 funcArg 函数：</span></span><br><span class="line">            <span class="comment">//  1.如果 html 是字符串，则返回html</span></span><br><span class="line">            <span class="comment">//  2.如果 html 是函数，则执行执行函数（传入 idx、originHtml），返回函数执行结果</span></span><br><span class="line">            $(<span class="keyword">this</span>)</span><br><span class="line">              .empty()</span><br><span class="line">              .append(funcArg(<span class="keyword">this</span>, html, idx, originHtml));</span><br><span class="line">          &#125;)</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">          function funcArg(context, arg, idx, payload) &#123;</span></span><br><span class="line"><span class="comment">            return isFunction(arg) ? arg.call(context, idx, payload) : arg</span></span><br><span class="line"><span class="comment">          &#125;</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 情况2：无参数，取值</span></span><br><span class="line">        <span class="number">0</span> <span class="keyword">in</span> <span class="keyword">this</span></span><br><span class="line">        ? <span class="keyword">this</span>[<span class="number">0</span>].innerHTML <span class="comment">// 直接取第一个元素的 innerHTML</span></span><br><span class="line">        : <span class="literal">null</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    text: <span class="function"><span class="keyword">function</span>(<span class="params">text</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 情况1：有参数，赋值，并返回自身</span></span><br><span class="line">      <span class="number">0</span> <span class="keyword">in</span> <span class="built_in">arguments</span></span><br><span class="line">        ? <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">            <span class="comment">// funcArg的应用，和html方法中一样</span></span><br><span class="line">            <span class="keyword">var</span> newText = funcArg(<span class="keyword">this</span>, text, idx, <span class="keyword">this</span>.textContent);</span><br><span class="line">            <span class="keyword">this</span>.textContent = newText == <span class="literal">null</span> ? <span class="string">""</span> : <span class="string">""</span> + newText;</span><br><span class="line">          &#125;)</span><br><span class="line">        : <span class="comment">// 情况2：无参数，取值</span></span><br><span class="line">        <span class="number">0</span> <span class="keyword">in</span> <span class="keyword">this</span></span><br><span class="line">        ? <span class="keyword">this</span>[<span class="number">0</span>].textContent</span><br><span class="line">        : <span class="literal">null</span>; <span class="comment">// 直接借用 textContent 属性</span></span><br><span class="line">    &#125;,</span><br><span class="line">    attr: <span class="function"><span class="keyword">function</span>(<span class="params">name, value</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> result;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 情况1：无第二个参数，读取值（读取值只能读取第一个元素的值）</span></span><br><span class="line">      <span class="keyword">typeof</span> name == <span class="string">"string"</span> &amp;&amp; !(<span class="number">1</span> <span class="keyword">in</span> <span class="built_in">arguments</span>)</span><br><span class="line">        ? !<span class="keyword">this</span>.length || <span class="keyword">this</span>[<span class="number">0</span>].nodeType !== <span class="number">1</span></span><br><span class="line">          ? <span class="literal">undefined</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">            this[0]是一个DOM节点，有『属性』也有『特性』</span></span><br><span class="line"><span class="comment">              result = this[0].getAttribute(name) 试图获取 DOM节点属性</span></span><br><span class="line"><span class="comment">              name in this[0] 判断是不是js对象的属性</span></span><br><span class="line"><span class="comment">            然后，该返回哪一个就返回哪一个</span></span><br><span class="line"><span class="comment">          */</span></span><br><span class="line">          !(result = <span class="keyword">this</span>[<span class="number">0</span>].getAttribute(name)) &amp;&amp; name <span class="keyword">in</span> <span class="keyword">this</span>[<span class="number">0</span>]</span><br><span class="line">          ? <span class="keyword">this</span>[<span class="number">0</span>][name]</span><br><span class="line">          : result</span><br><span class="line">        : <span class="comment">// 情况2：有第二个参数，设置值（针对每个元素设置值）</span></span><br><span class="line">          <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">this</span>.nodeType !== <span class="number">1</span>) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 传入的参数可能是一个对象集合</span></span><br><span class="line">            <span class="comment">// 此时，是不是应该放在『情况1』当中？？？此时，value根本没有用啊？？？</span></span><br><span class="line">            <span class="keyword">if</span> (isObject(name))</span><br><span class="line">              <span class="keyword">for</span> (key <span class="keyword">in</span> name) setAttribute(<span class="keyword">this</span>, key, name[key]);</span><br><span class="line">            <span class="comment">// 传入的不是对象，即设置一个单一的属性。</span></span><br><span class="line">            <span class="comment">// 但是，这里的 value 参数可以是一个函数</span></span><br><span class="line">            <span class="comment">// funcArg 即处理了 value 是函数和非函数的两种情况</span></span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">              setAttribute(</span><br><span class="line">                <span class="keyword">this</span>,</span><br><span class="line">                name,</span><br><span class="line">                funcArg(<span class="keyword">this</span>, value, idx, <span class="keyword">this</span>.getAttribute(name))</span><br><span class="line">              );</span><br><span class="line">          &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    removeAttr: <span class="function"><span class="keyword">function</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.nodeType === <span class="number">1</span> &amp;&amp;</span><br><span class="line">          name.split(<span class="string">" "</span>).forEach(</span><br><span class="line">            <span class="function"><span class="keyword">function</span>(<span class="params">attribute</span>) </span>&#123;</span><br><span class="line">              setAttribute(<span class="keyword">this</span>, attribute); <span class="comment">// 将属性设置为空，setAttribute会移除属性</span></span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="keyword">this</span> <span class="comment">// 改参数将成为 forEach 中函数的this</span></span><br><span class="line">          );</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取、设置属性（js对象的属性）</span></span><br><span class="line">    prop: <span class="function"><span class="keyword">function</span>(<span class="params">name, value</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// propMap 中存储的：key是html中的属性名称，value是js对象中的属性名称</span></span><br><span class="line">      <span class="comment">// 例如，html中的 "class" 在DOM对象中，就需要使用 "className" 这个名字读取，同理于：for  maxlength  cellspacing 等等</span></span><br><span class="line">      name = propMap[name] || name;</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">        propMap = &#123;</span></span><br><span class="line"><span class="comment">          'tabindex': 'tabIndex',</span></span><br><span class="line"><span class="comment">          'readonly': 'readOnly',</span></span><br><span class="line"><span class="comment">          'for': 'htmlFor',</span></span><br><span class="line"><span class="comment">          'class': 'className',</span></span><br><span class="line"><span class="comment">          'maxlength': 'maxLength',</span></span><br><span class="line"><span class="comment">          'cellspacing': 'cellSpacing',</span></span><br><span class="line"><span class="comment">          'cellpadding': 'cellPadding',</span></span><br><span class="line"><span class="comment">          'rowspan': 'rowSpan',</span></span><br><span class="line"><span class="comment">          'colspan': 'colSpan',</span></span><br><span class="line"><span class="comment">          'usemap': 'useMap',</span></span><br><span class="line"><span class="comment">          'frameborder': 'frameBorder',</span></span><br><span class="line"><span class="comment">          'contenteditable': 'contentEditable'</span></span><br><span class="line"><span class="comment">        &#125;</span></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"></span><br><span class="line">      <span class="comment">// 有第二个参数，设置属性</span></span><br><span class="line">      <span class="number">1</span> <span class="keyword">in</span> <span class="built_in">arguments</span></span><br><span class="line">        ? <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">            <span class="comment">// 设置属性值，funcArg处理函数或者非函数</span></span><br><span class="line">            <span class="keyword">this</span>[name] = funcArg(<span class="keyword">this</span>, value, idx, <span class="keyword">this</span>[name]);</span><br><span class="line">          &#125;)</span><br><span class="line">        : <span class="comment">// 无第二个参数，读取属性（读取第一个元素的）</span></span><br><span class="line">          <span class="keyword">this</span>[<span class="number">0</span>] &amp;&amp; <span class="keyword">this</span>[<span class="number">0</span>][name];</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 前面加上 'data-' 通过 attr 设置或者读取</span></span><br><span class="line">    data: <span class="function"><span class="keyword">function</span>(<span class="params">name, value</span>) </span>&#123;</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">        capitalRE = /([A-Z])/g,  //大写字母</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">//      前面加上 'data-'               将 'A' 替换为 '-a'</span></span><br><span class="line">      <span class="keyword">var</span> attrName = <span class="string">"data-"</span> + name.replace(capitalRE, <span class="string">"-$1"</span>).toLowerCase();</span><br><span class="line"></span><br><span class="line">      <span class="keyword">var</span> data =</span><br><span class="line">        <span class="number">1</span> <span class="keyword">in</span> <span class="built_in">arguments</span> ? <span class="keyword">this</span>.attr(attrName, value) : <span class="keyword">this</span>.attr(attrName);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> data !== <span class="literal">null</span> ? deserializeValue(data) : <span class="literal">undefined</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/*</span></span><br><span class="line"><span class="comment">        上文定义的，deserializeValue 函数的作用是：</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">        // "true"  =&gt; true</span></span><br><span class="line"><span class="comment">        // "false" =&gt; false</span></span><br><span class="line"><span class="comment">        // "null"  =&gt; null</span></span><br><span class="line"><span class="comment">        // "42"    =&gt; 42</span></span><br><span class="line"><span class="comment">        // "42.5"  =&gt; 42.5</span></span><br><span class="line"><span class="comment">        // "08"    =&gt; "08"</span></span><br><span class="line"><span class="comment">        // JSON    =&gt; parse if valid</span></span><br><span class="line"><span class="comment">        // String  =&gt; self</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    &#125;,</span><br><span class="line">    val: <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 有参数，设置值</span></span><br><span class="line">      <span class="number">0</span> <span class="keyword">in</span> <span class="built_in">arguments</span></span><br><span class="line">        ? <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">            <span class="comment">// 遍历每个元素，直接对 value 属性赋值</span></span><br><span class="line">            <span class="keyword">this</span>.value = funcArg(<span class="keyword">this</span>, value, idx, <span class="keyword">this</span>.value);</span><br><span class="line">          &#125;)</span><br><span class="line">        : <span class="comment">// 无参数，读取值</span></span><br><span class="line">          <span class="keyword">this</span>[<span class="number">0</span>] &amp;&amp;</span><br><span class="line">          <span class="comment">// 如果元素是 &lt;select multiple&gt; 多选列表</span></span><br><span class="line">          (<span class="keyword">this</span>[<span class="number">0</span>].multiple</span><br><span class="line">            ? <span class="comment">// 返回所有选中的option的值的数组</span></span><br><span class="line">              $(<span class="keyword">this</span>[<span class="number">0</span>])</span><br><span class="line">                .find(<span class="string">"option"</span>)</span><br><span class="line">                .filter(<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="keyword">this</span>.selected;</span><br><span class="line">                &#125;)</span><br><span class="line">                .pluck(<span class="string">"value"</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">                      pluck: function(property)&#123;</span></span><br><span class="line"><span class="comment">                        return $.map(this, function(el)&#123; return el[property] &#125;)</span></span><br><span class="line"><span class="comment">                      &#125;,</span></span><br><span class="line"><span class="comment">                   */</span></span><br><span class="line"></span><br><span class="line">              <span class="comment">// 如果不是，直接获取 value</span></span><br><span class="line">              <span class="keyword">this</span>[<span class="number">0</span>].value);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取、设置元素的 offset</span></span><br><span class="line">    offset: <span class="function"><span class="keyword">function</span>(<span class="params">coordinates</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 如果有 coordinates 参数，设置坐标值，并返回当前对象</span></span><br><span class="line">      <span class="keyword">if</span> (coordinates)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">index</span>) </span>&#123;</span><br><span class="line">          <span class="keyword">var</span> $<span class="keyword">this</span> = $(<span class="keyword">this</span>),</span><br><span class="line">            <span class="comment">// 支持函数（传入 $this.offset() 做参数）和非函数</span></span><br><span class="line">            coords = funcArg(<span class="keyword">this</span>, coordinates, index, $<span class="keyword">this</span>.offset()),</span><br><span class="line">            <span class="comment">// 找到最近的 “relative”, “absolute” or “fixed” 的祖先元素，并获取它的 offset()</span></span><br><span class="line">            parentOffset = $<span class="keyword">this</span>.offsetParent().offset(),</span><br><span class="line">            <span class="comment">// left 和 top 需要去掉定位的祖先元素的 left、top 值</span></span><br><span class="line">            props = &#123;</span><br><span class="line">              top: coords.top - parentOffset.top,</span><br><span class="line">              left: coords.left - parentOffset.left</span><br><span class="line">            &#125;;</span><br><span class="line"></span><br><span class="line">          <span class="comment">// static时，设置 top、left是无效的</span></span><br><span class="line">          <span class="keyword">if</span> ($<span class="keyword">this</span>.css(<span class="string">"position"</span>) == <span class="string">"static"</span>) props[<span class="string">"position"</span>] = <span class="string">"relative"</span>;</span><br><span class="line"></span><br><span class="line">          <span class="comment">// 通过 css 赋值</span></span><br><span class="line">          $<span class="keyword">this</span>.css(props);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 当前对象是空，则返回 null</span></span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.length) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 如果没有 coordinates 参数，则返回第一个元素的坐标值</span></span><br><span class="line">      <span class="keyword">var</span> obj = <span class="keyword">this</span>[<span class="number">0</span>].getBoundingClientRect();</span><br><span class="line">      <span class="comment">/*</span></span><br><span class="line"><span class="comment">        elem.getBoundingClientRect() 返回一个对象，</span></span><br><span class="line"><span class="comment">        包含元素的 top bottom left right width height 的值</span></span><br><span class="line"><span class="comment">        但是这个 top、bottom、left、right 是相对于浏览器窗口的距离，而不是页面的边界</span></span><br><span class="line"><span class="comment">        （注意，elem.getBoundingClientRect()在IE低版本浏览器有2px的兼容问题）</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        window.pageXOffset 和 window.pageYOffset 可获取网页滚动的距离，</span></span><br><span class="line"><span class="comment">        IE低版本需要用 document.body.scrollLeft 和 document.body.scrollTop 兼容</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        left: obj.left + <span class="built_in">window</span>.pageXOffset,</span><br><span class="line">        top: obj.top + <span class="built_in">window</span>.pageYOffset,</span><br><span class="line">        width: <span class="built_in">Math</span>.round(obj.width),</span><br><span class="line">        height: <span class="built_in">Math</span>.round(obj.height)</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">// 设置、获取 css</span></span><br><span class="line">    css: <span class="function"><span class="keyword">function</span>(<span class="params">property, value</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 只有一个参数，获取第一个元素的样式</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">arguments</span>.length &lt; <span class="number">2</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> computedStyle,</span><br><span class="line">          element = <span class="keyword">this</span>[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">if</span> (!element) <span class="keyword">return</span>; <span class="comment">// 如果第一个元素无值，直接返回。否则继续</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取元素的计算后的样式</span></span><br><span class="line">        computedStyle = getComputedStyle(element, <span class="string">""</span>);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> property == <span class="string">"string"</span>)</span><br><span class="line">          <span class="comment">// 情况1，参数为字符串形式</span></span><br><span class="line">          <span class="comment">// 先从elem内联样式获取（element.style），此时需要 camelize(property) 转换，如将 background-color 变为 backgroundColor</span></span><br><span class="line">          <span class="comment">// 如果未找到，则从css样式获取 computedStyle.getPropertyValue(property)</span></span><br><span class="line">          <span class="comment">// （重要）注释：elem.style 只能获取元素设置的内联样式、不能获取css样式；而 getComputedStyle 可获取内联、css样式。</span></span><br><span class="line">          <span class="keyword">return</span> (</span><br><span class="line">            element.style[camelize(property)] ||</span><br><span class="line">            computedStyle.getPropertyValue(property)</span><br><span class="line">          );</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (isArray(property)) &#123;</span><br><span class="line">          <span class="comment">// 情况2，参数为数组形式（注意，此时 isObject 情况尚未判断）</span></span><br><span class="line">          <span class="keyword">var</span> props = &#123;&#125;;</span><br><span class="line">          $.each(property, <span class="function"><span class="keyword">function</span>(<span class="params">_, prop</span>) </span>&#123;</span><br><span class="line">            props[prop] =</span><br><span class="line">              element.style[camelize(prop)] ||</span><br><span class="line">              computedStyle.getPropertyValue(prop);</span><br><span class="line">          &#125;);</span><br><span class="line">          <span class="keyword">return</span> props; <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">      <span class="comment">// 其他情况：有两个参数、property是对象</span></span><br><span class="line">      <span class="keyword">var</span> css = <span class="string">""</span>;</span><br><span class="line">      <span class="keyword">if</span> (type(property) == <span class="string">"string"</span>) &#123;</span><br><span class="line">        <span class="comment">// 情况1，property 是字符串，设置单个样式</span></span><br><span class="line">        <span class="keyword">if</span> (!value &amp;&amp; value !== <span class="number">0</span>)</span><br><span class="line">          <span class="comment">// 如果value参数是 '' null undefined 则移除这个css样式</span></span><br><span class="line">          <span class="comment">// 注：此计算只适用于内联样式的删除，对 css 样式无效，因为它只通过 this.style.removeProperty 计算，而 this.style 获取不到css样式</span></span><br><span class="line">          <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.style.removeProperty(dasherize(property));</span><br><span class="line">          &#125;);</span><br><span class="line">        <span class="comment">// value有正常值，将 css 生成一个字符串（如 'font-size:20px'）等待赋值给内联样式</span></span><br><span class="line">        <span class="comment">// maybeAddPx(property, value) 需要增加 px 的增加上</span></span><br><span class="line">        <span class="keyword">else</span> css = dasherize(property) + <span class="string">":"</span> + maybeAddPx(property, value);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 情况2，property 是对象（此时就不管第二个参数是什么了，不用第二个参数），一次性设置多个样式</span></span><br><span class="line">        <span class="keyword">for</span> (key <span class="keyword">in</span> property)</span><br><span class="line">          <span class="keyword">if</span> (!property[key] &amp;&amp; property[key] !== <span class="number">0</span>)</span><br><span class="line">            <span class="comment">// 如果对象属性值是 '' null undefined 则移除这个css样式，同理，只针对内联样式</span></span><br><span class="line">            <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">              <span class="keyword">this</span>.style.removeProperty(dasherize(key));</span><br><span class="line">            &#125;);</span><br><span class="line">          <span class="comment">// 否则，给 css 赋值一个字符串，多样式属性用 ; 隔开</span></span><br><span class="line">          <span class="keyword">else</span></span><br><span class="line">            css += dasherize(key) + <span class="string">":"</span> + maybeAddPx(key, property[key]) + <span class="string">";"</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 针对每个元素，设置内联样式（this.style.cssText可获取、设置内联样式）</span></span><br><span class="line">      <span class="comment">// 最后返回自身</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.style.cssText += <span class="string">";"</span> + css;</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">        上文定义：</span></span><br><span class="line"><span class="comment">        // 将 lineHeight 转换为 line-height 格式</span></span><br><span class="line"><span class="comment">        function dasherize(str) &#123;</span></span><br><span class="line"><span class="comment">          return str.replace(/::/g, '/')</span></span><br><span class="line"><span class="comment">                    .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')</span></span><br><span class="line"><span class="comment">                    .replace(/([a-z\d])([A-Z])/g, '$1_$2')</span></span><br><span class="line"><span class="comment">                    .replace(/_/g, '-')</span></span><br><span class="line"><span class="comment">                    .toLowerCase()</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 获取一个元素的索引值（从0开始计数）。当elemen参数没有给出时，返回当前元素在兄弟节点中的位置</span></span><br><span class="line">    index: <span class="function"><span class="keyword">function</span>(<span class="params">element</span>) </span>&#123;</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">        $.fn.indexOf: emptyArray.indexOf</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 其实 this 本身就是一个数组，数组本身就有 indexOf ，为何还要上文的这个赋值呢？</span></span><br><span class="line">      <span class="comment">// 因为上文中，this.__proto__ 修改了，不是 Array.prototype 了，也就没有 indexOf 方法了</span></span><br><span class="line">      <span class="comment">// 因此要手动赋值，需要将数组常用的方法在重新赋值给 $.fn.indexOf</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> element</span><br><span class="line">        ? <span class="keyword">this</span>.indexOf($(element)[<span class="number">0</span>])</span><br><span class="line">        : <span class="keyword">this</span>.parent()</span><br><span class="line">            .children()</span><br><span class="line">            .indexOf(<span class="keyword">this</span>[<span class="number">0</span>]);</span><br><span class="line">    &#125;,</span><br><span class="line">    hasClass: <span class="function"><span class="keyword">function</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (!name) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">      <span class="keyword">return</span> emptyArray.some.call(</span><br><span class="line">        <span class="keyword">this</span>,</span><br><span class="line">        <span class="function"><span class="keyword">function</span>(<span class="params">el</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// this 就是 classRE(name) 的返回值（返回一个正则）</span></span><br><span class="line">          <span class="comment">// function className(node, value)&#123;...&#125; 获取或者设置elem的className</span></span><br><span class="line">          <span class="keyword">return</span> <span class="keyword">this</span>.test(className(el));</span><br><span class="line">        &#125;,</span><br><span class="line">        classRE(name)</span><br><span class="line">      );</span><br><span class="line"></span><br><span class="line">      <span class="comment">// array.some(callback,[ thisObject]); 只要数组中一项符合callback要求，即返回true</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/*</span></span><br><span class="line"><span class="comment">        // 上文定义 classCache = &#123;&#125;</span></span><br><span class="line"><span class="comment">        function classRE(name) &#123;</span></span><br><span class="line"><span class="comment">          return name in classCache ?</span></span><br><span class="line"><span class="comment">                 classCache[name] : </span></span><br><span class="line"><span class="comment">                 (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">          // classCache 存储的数据是这样的：</span></span><br><span class="line"><span class="comment">          // &#123;</span></span><br><span class="line"><span class="comment">          //   abc: /(^|\s)abc(\s|$)/,  // 能匹配 'abc' 或 ' abc ' 或 ' abc' 或 'abc '</span></span><br><span class="line"><span class="comment">          //   xyz: /(^|\s)abc(\s|$)/,</span></span><br><span class="line"><span class="comment">          //   ...</span></span><br><span class="line"><span class="comment">          // &#125;</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    &#125;,</span><br><span class="line">    addClass: <span class="function"><span class="keyword">function</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (!name) <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 针对所有元素都添加className，最终返回本身</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 说明当前元素不是 DOM node</span></span><br><span class="line">        <span class="keyword">if</span> (!(<span class="string">"className"</span> <span class="keyword">in</span> <span class="keyword">this</span>)) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// classList 是一开始就定义的空变量</span></span><br><span class="line">        classList = [];</span><br><span class="line">        <span class="comment">// 获取元素的 clasname      // 支持传入函数</span></span><br><span class="line">        <span class="keyword">var</span> cls = className(<span class="keyword">this</span>),</span><br><span class="line">          newName = funcArg(<span class="keyword">this</span>, name, idx, cls);</span><br><span class="line">        <span class="comment">// 把要赋值的值，按照空白分组，遍历</span></span><br><span class="line">        newName.split(<span class="regexp">/\s+/g</span>).forEach(<span class="function"><span class="keyword">function</span>(<span class="params">klass</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// 把当前元素不存在的class，push到classlist中</span></span><br><span class="line">          <span class="keyword">if</span> (!$(<span class="keyword">this</span>).hasClass(klass)) classList.push(klass);</span><br><span class="line">        &#125;, <span class="keyword">this</span>);</span><br><span class="line">        <span class="comment">// 如果classlist有数据，则为当前元素赋值最新的class值（现有的classname和新的classname拼接）</span></span><br><span class="line">        classList.length &amp;&amp;</span><br><span class="line">          className(<span class="keyword">this</span>, cls + (cls ? <span class="string">" "</span> : <span class="string">""</span>) + classList.join(<span class="string">" "</span>));</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    removeClass: <span class="function"><span class="keyword">function</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 针对所有元素都移除className，最终返回本身</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 说明当前元素不是 DOM node</span></span><br><span class="line">        <span class="keyword">if</span> (!(<span class="string">"className"</span> <span class="keyword">in</span> <span class="keyword">this</span>)) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果参数空，则移除元素的所有class</span></span><br><span class="line">        <span class="keyword">if</span> (name === <span class="literal">undefined</span>) <span class="keyword">return</span> className(<span class="keyword">this</span>, <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取现有的classname</span></span><br><span class="line">        classList = className(<span class="keyword">this</span>);</span><br><span class="line">        <span class="comment">// （可以传入函数）遍历新的classname字符串</span></span><br><span class="line">        funcArg(<span class="keyword">this</span>, name, idx, classList)</span><br><span class="line">          .split(<span class="regexp">/\s+/g</span>)</span><br><span class="line">          .forEach(<span class="function"><span class="keyword">function</span>(<span class="params">klass</span>) </span>&#123;</span><br><span class="line">            <span class="comment">// classRE(klass) 返回一个正则，匹配 'classname' 或 ' classname ' 或 ' classname' 或 'classname '</span></span><br><span class="line">            <span class="comment">// 针对传入的classname字符串，对每个符合条件的classname，都替换为 ' '（即删除了）</span></span><br><span class="line">            classList = classList.replace(classRE(klass), <span class="string">" "</span>);</span><br><span class="line">          &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 对整理好的classname，重新赋值给当前元素</span></span><br><span class="line">        className(<span class="keyword">this</span>, classList.trim());</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    toggleClass: <span class="function"><span class="keyword">function</span>(<span class="params">name, when</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// when 参数相当于一个条件：</span></span><br><span class="line">      <span class="comment">// 如果 when === true 则单纯执行 addClass</span></span><br><span class="line">      <span class="comment">// 如果 when === false 则单纯执行 removeClass</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!name) <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">        <span class="comment">//                   name 可接收函数，可以是空白分割开来的多个classname</span></span><br><span class="line">        <span class="keyword">var</span> $<span class="keyword">this</span> = $(<span class="keyword">this</span>),</span><br><span class="line">          names = funcArg(<span class="keyword">this</span>, name, idx, className(<span class="keyword">this</span>));</span><br><span class="line">        <span class="comment">// 用空白分割开多个class</span></span><br><span class="line">        names.split(<span class="regexp">/\s+/g</span>).forEach(<span class="function"><span class="keyword">function</span>(<span class="params">klass</span>) </span>&#123;</span><br><span class="line">          <span class="comment">// 如果有 when 参数，则只通过when参数判断，true则只执行addClass，false则只执行removeClass</span></span><br><span class="line">          <span class="comment">// 如果没有 when 参数，则判断元素有没有该class，有则移除，没有则添加</span></span><br><span class="line">          (when === <span class="literal">undefined</span></span><br><span class="line">          ? !$<span class="keyword">this</span>.hasClass(klass)</span><br><span class="line">          : when)</span><br><span class="line">            ? $<span class="keyword">this</span>.addClass(klass)</span><br><span class="line">            : $<span class="keyword">this</span>.removeClass(klass);</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    scrollTop: <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.length) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 普通elem有 scrollTop 属性，可以获取或者设置top值</span></span><br><span class="line">      <span class="comment">// window对象没有 scrollTop 属性，通过 pageYOffset 获取，通过 scrollTo() 赋值</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">var</span> hasScrollTop = <span class="string">"scrollTop"</span> <span class="keyword">in</span> <span class="keyword">this</span>[<span class="number">0</span>];</span><br><span class="line">      <span class="comment">// value 无值，获取 top</span></span><br><span class="line">      <span class="keyword">if</span> (value === <span class="literal">undefined</span>)</span><br><span class="line">        <span class="keyword">return</span> hasScrollTop ? <span class="keyword">this</span>[<span class="number">0</span>].scrollTop : <span class="keyword">this</span>[<span class="number">0</span>].pageYOffset;</span><br><span class="line">      <span class="comment">// value 有值，设置 top</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(</span><br><span class="line">        hasScrollTop</span><br><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">this</span>.scrollTop = value;</span><br><span class="line">            &#125;</span><br><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">this</span>.scrollTo(<span class="keyword">this</span>.scrollX, value);</span><br><span class="line">            &#125;</span><br><span class="line">      ); <span class="comment">// window.scrollX 获取横向滚动值</span></span><br><span class="line">    &#125;,</span><br><span class="line">    scrollLeft: <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.length) <span class="keyword">return</span>;</span><br><span class="line">      <span class="keyword">var</span> hasScrollLeft = <span class="string">"scrollLeft"</span> <span class="keyword">in</span> <span class="keyword">this</span>[<span class="number">0</span>];</span><br><span class="line">      <span class="keyword">if</span> (value === <span class="literal">undefined</span>)</span><br><span class="line">        <span class="keyword">return</span> hasScrollLeft ? <span class="keyword">this</span>[<span class="number">0</span>].scrollLeft : <span class="keyword">this</span>[<span class="number">0</span>].pageXOffset;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(</span><br><span class="line">        hasScrollLeft</span><br><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">this</span>.scrollLeft = value;</span><br><span class="line">            &#125;</span><br><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">this</span>.scrollTo(value, <span class="keyword">this</span>.scrollY);</span><br><span class="line">            &#125;</span><br><span class="line">      ); <span class="comment">// window.scrollX 获取纵向滚动值</span></span><br><span class="line">    &#125;,</span><br><span class="line">    position: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.length) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">var</span> elem = <span class="keyword">this</span>[<span class="number">0</span>],</span><br><span class="line">        <span class="comment">// Get *real* offsetParent</span></span><br><span class="line">        offsetParent = <span class="keyword">this</span>.offsetParent(), <span class="comment">// 找到第一个定位过的祖先元素 “relative”, “absolute” or “fixed”</span></span><br><span class="line">        <span class="comment">// Get correct offsets</span></span><br><span class="line">        offset = <span class="keyword">this</span>.offset(), <span class="comment">// 获取自身的offset</span></span><br><span class="line">        parentOffset = rootNodeRE.test(offsetParent[<span class="number">0</span>].nodeName)</span><br><span class="line">          ? &#123; <span class="attr">top</span>: <span class="number">0</span>, <span class="attr">left</span>: <span class="number">0</span> &#125;</span><br><span class="line">          : offsetParent.offset(); <span class="comment">// 获取定位祖先元素的offset（ body、html直接设置 top:0;left:0 ）</span></span><br><span class="line">      <span class="comment">// 上文定义： rootNodeRE = /^(?:body|html)$/i,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 去掉当前元素的 margin 宽度</span></span><br><span class="line">      <span class="comment">// Subtract element margins</span></span><br><span class="line">      <span class="comment">// note: when an element has margin: auto the offsetLeft and marginLeft</span></span><br><span class="line">      <span class="comment">// are the same in Safari causing offset.left to incorrectly be 0</span></span><br><span class="line">      offset.top -= <span class="built_in">parseFloat</span>($(elem).css(<span class="string">"margin-top"</span>)) || <span class="number">0</span>;</span><br><span class="line">      offset.left -= <span class="built_in">parseFloat</span>($(elem).css(<span class="string">"margin-left"</span>)) || <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 增加父元素的 border 宽度</span></span><br><span class="line">      <span class="comment">// Add offsetParent borders</span></span><br><span class="line">      parentOffset.top +=</span><br><span class="line">        <span class="built_in">parseFloat</span>($(offsetParent[<span class="number">0</span>]).css(<span class="string">"border-top-width"</span>)) || <span class="number">0</span>;</span><br><span class="line">      parentOffset.left +=</span><br><span class="line">        <span class="built_in">parseFloat</span>($(offsetParent[<span class="number">0</span>]).css(<span class="string">"border-left-width"</span>)) || <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// Subtract the two offsets</span></span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        top: offset.top - parentOffset.top,</span><br><span class="line">        left: offset.left - parentOffset.left</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    offsetParent: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 通过 this.map 遍历当前对象所有元素，进行计算，然后拼接新的数组，并返回。保证链式操作</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.map(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> parent = <span class="keyword">this</span>.offsetParent || <span class="built_in">document</span>.body; <span class="comment">// elem.offsetParent 可返回最近的改元素最近的已经定位的父元素</span></span><br><span class="line">        <span class="keyword">while</span> (</span><br><span class="line">          parent &amp;&amp;</span><br><span class="line">          !rootNodeRE.test(parent.nodeName) &amp;&amp;</span><br><span class="line">          $(parent).css(<span class="string">"position"</span>) == <span class="string">"static"</span></span><br><span class="line">        )</span><br><span class="line">          <span class="comment">// 如果获取的parent不是null、不是body或html、而且position==static</span></span><br><span class="line">          <span class="comment">// 则继续向上查找 offsetParent、大不了找到 body 为止</span></span><br><span class="line">          parent = parent.offsetParent;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 最后返回改元素</span></span><br><span class="line">        <span class="keyword">return</span> parent;</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">// for now</span></span><br><span class="line">  $.fn.detach = $.fn.remove;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Generate the `width` and `height` functions</span></span><br><span class="line">  [<span class="string">"width"</span>, <span class="string">"height"</span>].forEach(<span class="function"><span class="keyword">function</span>(<span class="params">dimension</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 将 width height 变为  Width Height</span></span><br><span class="line">    <span class="keyword">var</span> dimensionProperty = dimension.replace(<span class="regexp">/./</span>, <span class="function"><span class="keyword">function</span>(<span class="params">m</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> m[<span class="number">0</span>].toUpperCase();</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    $.fn[dimension] = <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> offset,</span><br><span class="line">        el = <span class="keyword">this</span>[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 情况1，无参数，获取第一个元素的值</span></span><br><span class="line">      <span class="keyword">if</span> (value === <span class="literal">undefined</span>)</span><br><span class="line">        <span class="keyword">return</span> isWindow(el)</span><br><span class="line">          ? el[<span class="string">"inner"</span> + dimensionProperty] <span class="comment">// window.innerHeight</span></span><br><span class="line">          : isDocument(el)</span><br><span class="line">          ? el.documentElement[<span class="string">"scroll"</span> + dimensionProperty] <span class="comment">// document.documentElement.scrollHeight</span></span><br><span class="line">          : (offset = <span class="keyword">this</span>.offset()) &amp;&amp; offset[dimension];</span><br><span class="line">      <span class="comment">// this.offset().width</span></span><br><span class="line">      <span class="comment">// 情况2，有参数，设置所有元素的值</span></span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">idx</span>) </span>&#123;</span><br><span class="line">          el = $(<span class="keyword">this</span>);</span><br><span class="line">          <span class="comment">// 通过 css() 方法设置，支持传入函数</span></span><br><span class="line">          el.css(dimension, funcArg(<span class="keyword">this</span>, value, idx, el[dimension]()));</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">// 针对当前元素、遍历子元素，都执行 fun 函数</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">traverseNode</span>(<span class="params">node, fun</span>) </span>&#123;</span><br><span class="line">    fun(node);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, len = node.childNodes.length; i &lt; len; i++)</span><br><span class="line">      traverseNode(node.childNodes[i], fun);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 上文定义 adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// Generate the `after`, `prepend`, `before`, `append`,</span></span><br><span class="line">  <span class="comment">// `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.</span></span><br><span class="line">  adjacencyOperators.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">operator, operatorIndex</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> inside = operatorIndex % <span class="number">2</span>; <span class="comment">//=&gt; prepend, append</span></span><br><span class="line"></span><br><span class="line">    $.fn[operator] = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="comment">// arguments can be nodes, arrays of nodes, Zepto objects and HTML strings</span></span><br><span class="line">      <span class="keyword">var</span> argType,</span><br><span class="line">        nodes = $.map(<span class="built_in">arguments</span>, <span class="function"><span class="keyword">function</span>(<span class="params">arg</span>) </span>&#123;</span><br><span class="line">          argType = type(arg);</span><br><span class="line">          <span class="keyword">return</span> argType == <span class="string">"object"</span> || argType == <span class="string">"array"</span> || arg == <span class="literal">null</span></span><br><span class="line">            ? arg</span><br><span class="line">            : zepto.fragment(arg);</span><br><span class="line">        &#125;),</span><br><span class="line">        parent,</span><br><span class="line">        copyByClone = <span class="keyword">this</span>.length &gt; <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">if</span> (nodes.length &lt; <span class="number">1</span>) <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.each(<span class="function"><span class="keyword">function</span>(<span class="params">_, target</span>) </span>&#123;</span><br><span class="line">        parent = inside ? target : target.parentNode;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// convert all methods to a "before" operation</span></span><br><span class="line">        target =</span><br><span class="line">          operatorIndex == <span class="number">0</span></span><br><span class="line">            ? target.nextSibling</span><br><span class="line">            : operatorIndex == <span class="number">1</span></span><br><span class="line">            ? target.firstChild</span><br><span class="line">            : operatorIndex == <span class="number">2</span></span><br><span class="line">            ? target</span><br><span class="line">            : <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> parentInDocument = $.contains(<span class="built_in">document</span>.documentElement, parent);</span><br><span class="line"></span><br><span class="line">        nodes.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">node</span>) </span>&#123;</span><br><span class="line">          <span class="keyword">if</span> (copyByClone) node = node.cloneNode(<span class="literal">true</span>);</span><br><span class="line">          <span class="keyword">else</span> <span class="keyword">if</span> (!parent) <span class="keyword">return</span> $(node).remove();</span><br><span class="line"></span><br><span class="line">          parent.insertBefore(node, target);</span><br><span class="line">          <span class="keyword">if</span> (parentInDocument)</span><br><span class="line">            traverseNode(node, <span class="function"><span class="keyword">function</span>(<span class="params">el</span>) </span>&#123;</span><br><span class="line">              <span class="keyword">if</span> (</span><br><span class="line">                el.nodeName != <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                el.nodeName.toUpperCase() === <span class="string">"SCRIPT"</span> &amp;&amp;</span><br><span class="line">                (!el.type || el.type === <span class="string">"text/javascript"</span>) &amp;&amp;</span><br><span class="line">                !el.src</span><br><span class="line">              )</span><br><span class="line">                <span class="built_in">window</span>[<span class="string">"eval"</span>].call(<span class="built_in">window</span>, el.innerHTML);</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="comment">// after    =&gt; insertAfter</span></span><br><span class="line">    <span class="comment">// prepend  =&gt; prependTo</span></span><br><span class="line">    <span class="comment">// before   =&gt; insertBefore</span></span><br><span class="line">    <span class="comment">// append   =&gt; appendTo</span></span><br><span class="line">    $.fn[</span><br><span class="line">      inside ? operator + <span class="string">"To"</span> : <span class="string">"insert"</span> + (operatorIndex ? <span class="string">"Before"</span> : <span class="string">"After"</span>)</span><br><span class="line">    ] = <span class="function"><span class="keyword">function</span>(<span class="params">html</span>) </span>&#123;</span><br><span class="line">      $(html)[operator](<span class="keyword">this</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  zepto.Z.prototype = $.fn;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Export internal API functions in the `$.zepto` namespace</span></span><br><span class="line">  zepto.uniq = uniq;</span><br><span class="line">  zepto.deserializeValue = deserializeValue;</span><br><span class="line">  $.zepto = zepto;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> $;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.Zepto = Zepto;</span><br><span class="line"><span class="built_in">window</span>.$ === <span class="literal">undefined</span> &amp;&amp; (<span class="built_in">window</span>.$ = Zepto);</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href=&quot;https://www.kancloud.cn/wangfupeng/zepto-design-srouce/173692&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;zepto-core-1.1.6 源码注释&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="前端框架" scheme="https://www.yangtao.site/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/"/>
    
    
      <category term="zepto" scheme="https://www.yangtao.site/tags/zepto/"/>
    
  </entry>
  
  <entry>
    <title>Zepto 对象思想与源码分析</title>
    <link href="https://www.yangtao.site/2020/03/12/zepto/"/>
    <id>https://www.yangtao.site/2020/03/12/zepto/</id>
    <published>2020-03-12T09:56:32.000Z</published>
    <updated>2020-03-16T04:03:15.930Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><a href="http://www.kancloud.cn/wangfupeng/zepto-design-srouce/173680" target="_blank" rel="noopener">zepto 对象思想与源码分析</a></p></blockquote><a id="more"></a><h2 id="原型对象"><a href="#原型对象" class="headerlink" title="原型对象"></a>原型对象</h2><h3 id="Array-prototype"><a href="#Array-prototype" class="headerlink" title="Array.prototype"></a><code>Array.prototype</code></h3><p><strong>每一个函数，都有一个 prototype 属性</strong>，不管是你自定义的，还是函数内置的。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> fn = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line"><span class="built_in">console</span>.log(fn.prototype); <span class="comment">// &#123;constructor: ƒ&#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(fn.prototype.constructor === fn); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p><img src="https://user-images.githubusercontent.com/19526072/53319611-f76d3c00-390d-11e9-8542-313fa56bffab.png" alt="image"></p><p>这里的 <code>fn.prototype</code> 打印出一个对象，对象里的 <code>constructor</code> 属性又指回了该函数本身 fn。</p><p>即<strong>每一个原型对象都有一个 consctructor 属性指向关联的构造函数</strong>，比如：</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="built_in">Array</span>.prototype.constructor === <span class="built_in">Array</span>; <span class="comment">//true</span></span><br></pre></td></tr></table></figure><p><img src="https://user-images.githubusercontent.com/19526072/53319627-ff2ce080-390d-11e9-8dab-3659d53b1296.png" alt="image"></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="built_in">console</span>.log(<span class="built_in">Array</span>.prototype); <span class="comment">// [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]</span></span><br></pre></td></tr></table></figure><p>这里，除了 constructor 属性，还有其他内置的属性，即我们经常使用的操作数组的方法。</p><h3 id="proto-隐式原型"><a href="#proto-隐式原型" class="headerlink" title="__proto__(隐式原型)"></a><code>__proto__</code>(隐式原型)</h3><p>所有通过函数 <code>new</code> （构造函数）出来的实例对象，都有一个 <code>__proto__</code>属性，指向该对象的 <code>prototype</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">var</span> arr = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">arr.__proto__ === <span class="built_in">Array</span>.prototype; <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p><img src="https://user-images.githubusercontent.com/19526072/53319688-2f747f00-390e-11e9-9555-af336e12b587.png" alt="image"></p><p>原型链：由相互关联的原型(<code>__proto__</code>)组成的链状结构就是原型链。</p><p>举个关于继承 extends 的例子：</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Animal</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.eat = <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">"Animal eat"</span>);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Dog</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.bark = <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">"Dog bark"</span>);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 绑定原型，实现继承</span></span><br><span class="line">Dog.prototype = <span class="keyword">new</span> Animal();</span><br><span class="line"><span class="keyword">var</span> dog = <span class="keyword">new</span> Dog();</span><br><span class="line"></span><br><span class="line">dog.eat(); <span class="comment">// Animal eat</span></span><br><span class="line">dog.bark(); <span class="comment">// Dog bark</span></span><br></pre></td></tr></table></figure><p>上面看明白了，那么 ES6 的继承我们也就可以明白原理了，即 <code>class Dog extends Animal</code> 相当于 <code>Dog.prototype = new Animal()</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Animal</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(name) &#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">  &#125;</span><br><span class="line">  eat() &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Animal eat!"</span> + <span class="keyword">this</span>.name);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dog</span> <span class="keyword">extends</span> <span class="title">Animal</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(name) &#123;</span><br><span class="line">    <span class="keyword">super</span>(name);</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">  &#125;</span><br><span class="line">  bark() &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Dog bark!"</span> + <span class="keyword">this</span>.name);</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> dog = <span class="keyword">new</span> Dog(<span class="string">"哈士奇"</span>);</span><br><span class="line">dog.eat();</span><br><span class="line">dog.bark();</span><br></pre></td></tr></table></figure><p>接下来，我们就清楚为什么能这样：</p><p><strong>当我们要使用一个对象（数组）的某个功能时，如果该对象本身具有这个功能，直接调用，没有的话，那就去自身的<code>__proto__</code>属性中去找</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">  myfn: <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">"myfn"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line">obj.myfn(); <span class="comment">// 'myfn'</span></span><br><span class="line">obj.hasOwnProperty(<span class="string">"myfn"</span>); <span class="comment">//true</span></span><br><span class="line">obj.toString(); <span class="comment">// "[object Object]"</span></span><br><span class="line">obj.hasOwnProperty(<span class="string">"toString"</span>); <span class="comment">// false</span></span><br><span class="line">obj.__proto__.hasOwnProperty(<span class="string">"toString"</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p><code>hasOwnProperty()</code>就可以得出这个属性是否是属于该对象本身的属性:</p><ul><li>myfn 是我们自定义的，<code>obj.hasOwnProperty(&#39;myfn&#39;)</code>为 true</li><li>toString() 我们不是自定义的，却可以使用，查一下是否属于自定义属性，<code>obj.hasOwnProperty(&#39;toString&#39;)</code>，答案为 false</li><li>既然不属于自定义属性，那就去自身的<code>__proto__</code>去找，然后去原型对象上查一下，<code>obj.__proto__.hasOwnProperty(&#39;toString&#39;)</code>，哦，原来在这儿</li></ul><p>在源码中，我们经常看到<code>Array.prototype.concat</code>，其实就是我们使用的<code>[].concat</code>，<code>[]</code>，因为<code>[].__proto__ === Array.prototype</code></p><h3 id="proto-是可修改的"><a href="#proto-是可修改的" class="headerlink" title="__proto__是可修改的"></a><code>__proto__</code>是可修改的</h3><p>比如，我们新增一个<code>addClass()</code>方法：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">arr.__proto__.addClass = <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="number">123</span>);</span><br><span class="line">&#125;;</span><br><span class="line">arr.push(<span class="number">4</span>);</span><br><span class="line">arr.addClass(); <span class="comment">// 123</span></span><br></pre></td></tr></table></figure><p><img src="https://user-images.githubusercontent.com/19526072/53319708-44e9a900-390e-11e9-9899-6c303f5467aa.png" alt="image"></p><p>但是，这里要注意，如下重写之后，就没有了诸如 push、concat 等方法：</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">arr.__proto__ = &#123;</span><br><span class="line">  addClass: <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="number">123</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line">arr.push(<span class="number">3</span>); <span class="comment">//Uncaught TypeError: arr.push is not a function</span></span><br></pre></td></tr></table></figure><h3 id="Object-prototype的原型"><a href="#Object-prototype的原型" class="headerlink" title="Object.prototype的原型"></a><code>Object.prototype</code>的原型</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><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 构造函数 Person</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实例对象 myfn，它的 隐式原型 指向了其构造函数的 原型对象</span></span><br><span class="line"><span class="keyword">var</span> myfn = <span class="keyword">new</span> Person();</span><br><span class="line">myfn.__proto__ === Person.prototype; <span class="comment">//true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 那构造函数 Person 的 隐式原型又指向了谁呢</span></span><br><span class="line">Person.prototype.__proto__ === <span class="built_in">Object</span>.prototype; <span class="comment">//true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Function呐</span></span><br><span class="line"><span class="built_in">Function</span>.prototype.__proto__ === <span class="built_in">Object</span>.prototype; <span class="comment">//true</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> fn = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line">fn.prototype.__proto__ === <span class="built_in">Object</span>.prototype; <span class="comment">//true</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.__proto__ === <span class="literal">null</span>; <span class="comment">//true</span></span><br><span class="line"><span class="keyword">typeof</span> <span class="literal">null</span>; <span class="comment">//"object"</span></span><br></pre></td></tr></table></figure><p>总结：</p><ul><li><p>所有的函数都有一个 <code>prototype</code>属性，该属性指向了一个对象，该对象就是调用该构造函数而创建出来的实例（如 myfn）的原型（如<code>myfn.__proto__</code>）,即：<code>myfn.__proto__ === Person.prototype</code></p></li><li><p>所有的对象（除 null）都具有一个<code>__proto__</code>属性，该属性指向该对象的原型，比如：<code>myfn.__proto__ === Person.prototype</code></p></li><li><p>原型也是一个对象，根据上条，那原型的原型，就是<code>Object.prototype</code></p></li><li><p>最后的 null 对象，可以当做是 什么都没有</p></li></ul><p>盗一张图，我们就更加清楚了（蓝色这条表示的是原型链）<br><img src="https://user-images.githubusercontent.com/19526072/53319411-64340680-390d-11e9-80b9-4b86736291b2.png" alt="image"></p><p>参考资料：</p><ul><li><a href="https://www.kancloud.cn/wangfupeng/zepto-design-srouce/173684" target="_blank" rel="noopener">js 原型链基础</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/2" target="_blank" rel="noopener">JavaScript 深入之从原型到原型链 </a></li><li><a href="https://www.cnblogs.com/wangfupeng1988/p/3978131.html" target="_blank" rel="noopener">深入理解 javascript 原型和闭包（3）——prototype 原型</a></li></ul><h2 id="Zepto-对象设计"><a href="#Zepto-对象设计" class="headerlink" title="Zepto 对象设计"></a>Zepto 对象设计</h2><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Zepto = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> $,</span><br><span class="line">    zepto = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...省略N行代码...</span></span><br><span class="line"></span><br><span class="line">  zepto.Z = <span class="function"><span class="keyword">function</span>(<span class="params">dom, selector</span>) </span>&#123;</span><br><span class="line">    dom = dom || [];</span><br><span class="line">    dom.__proto__ = $.fn; <span class="comment">// 关键位置</span></span><br><span class="line">    dom.selector = selector || <span class="string">""</span>;</span><br><span class="line">    <span class="keyword">return</span> dom;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  zepto.init = <span class="function"><span class="keyword">function</span>(<span class="params">selector, context</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> dom;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 针对参数情况，分别对dom赋值</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最终调用 zepto.Z 返回的数据</span></span><br><span class="line">    <span class="keyword">return</span> zepto.Z(dom, selector);</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="params">selector, context</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> zepto.init(selector, context);</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">  $.fn = &#123;</span><br><span class="line">    forEach: emptyArray.forEach,</span><br><span class="line">    <span class="comment">// ...省略N行代码...</span></span><br><span class="line">    concat: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">    <span class="comment">// ...省略N行代码...</span></span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...省略N行代码...</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> $;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.Zepto = Zepto;</span><br><span class="line"><span class="built_in">window</span>.$ === <span class="literal">undefined</span> &amp;&amp; (<span class="built_in">window</span>.$ = Zepto);</span><br></pre></td></tr></table></figure><h3 id="自定义模拟"><a href="#自定义模拟" class="headerlink" title="自定义模拟"></a>自定义模拟</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">window</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> $,</span><br><span class="line">    zepto = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Z</span>(<span class="params">dom, selector</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> i,</span><br><span class="line">      len = dom.length ? dom.length : <span class="number">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; dom.length; i++) &#123;</span><br><span class="line">      <span class="keyword">this</span>[i] = dom[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>.length = len;</span><br><span class="line">    <span class="keyword">this</span>.selector = selector || <span class="string">""</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  zepto.Z = <span class="function"><span class="keyword">function</span>(<span class="params">dom, selector</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Z(dom, selector);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  zepto.init = <span class="function"><span class="keyword">function</span>(<span class="params">selector</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> slice = <span class="built_in">Array</span>.prototype.slice;</span><br><span class="line">    <span class="keyword">var</span> dom = slice.call(<span class="built_in">document</span>.querySelectorAll(selector));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> zepto.Z(dom, selector);</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="params">selector</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> zepto.init(selector);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  $.fn = &#123;</span><br><span class="line">    css: <span class="function"><span class="keyword">function</span>(<span class="params">key, value</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">"css"</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    html: <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">"html"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  Z.prototype = $.fn;</span><br><span class="line"></span><br><span class="line">  <span class="built_in">window</span>.$ = $;</span><br><span class="line">&#125;)(<span class="built_in">window</span>);</span><br></pre></td></tr></table></figure><p>流程：<code>$() -&gt; zepto.init() -&gt; zepto.Z() -&gt; new Z() -&gt; Z.prototype = $.fn;</code></p><h3 id="最新版（v1-2-0）模拟"><a href="#最新版（v1-2-0）模拟" class="headerlink" title="最新版（v1.2.0）模拟"></a>最新版（v1.2.0）模拟</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自执行匿名函数(IIFE，立即调用函数表达式)</span></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">global, factory</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// amd 规范</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> define === <span class="string">"function"</span> &amp;&amp; define.amd) &#123;</span><br><span class="line">    define(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> factory(global);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    factory(global);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)(<span class="keyword">this</span>, <span class="function"><span class="keyword">function</span>(<span class="params">window</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> Zepto = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> $;</span><br><span class="line">    <span class="keyword">var</span> zepto = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 大Z构造函数</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">Z</span>(<span class="params">dom, selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> i,</span><br><span class="line">        len = dom ? dom.length : <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; len; i++) <span class="keyword">this</span>[i] = dom[i];</span><br><span class="line">      <span class="keyword">this</span>.length = len;</span><br><span class="line">      <span class="keyword">this</span>.selector = selector || <span class="string">""</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// zepto.Z() 返回大Z构造函数的实例，方便实例对象的 __proto 就会指向 Z.prototype</span></span><br><span class="line">    zepto.Z = <span class="function"><span class="keyword">function</span>(<span class="params">dom, selector</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> Z(dom, selector);</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 解析 DOM，并调用 zepto.Z()</span></span><br><span class="line">    zepto.init = <span class="function"><span class="keyword">function</span>(<span class="params">selector, context</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 自定义模拟方法，源码并非这样简单</span></span><br><span class="line">      <span class="keyword">var</span> slice = <span class="built_in">Array</span>.prototype.slice;</span><br><span class="line">      <span class="keyword">var</span> dom = slice.call(<span class="built_in">document</span>.querySelectorAll(selector));</span><br><span class="line"></span><br><span class="line">      <span class="comment">// create a new Zepto collection from the nodes found</span></span><br><span class="line">      <span class="keyword">return</span> zepto.Z(dom, selector);</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="params">selector, context</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> zepto.init(selector, context);</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">    $.fn = &#123;</span><br><span class="line">      <span class="keyword">constructor</span>: zepto.Z, // 手动绑定 <span class="keyword">constructor</span> 属性</span><br><span class="line">      length: 0,</span><br><span class="line">      push: "push method",</span><br><span class="line">      css: function() &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"css"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;  <span class="comment">// 返回 this，可以链式调用</span></span><br><span class="line">      &#125;,</span><br><span class="line">      html: <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">"html"</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    $.fn.on = <span class="function"><span class="keyword">function</span>(<span class="params">event, data, callback</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">"on method"</span>);</span><br><span class="line">    &#125;;</span><br><span class="line">    $.fn.off = <span class="function"><span class="keyword">function</span>(<span class="params">event, data, callback</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">"off method"</span>);</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    $.fn.bind = <span class="function"><span class="keyword">function</span>(<span class="params">event, data, callback</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.on(event, data, callback);</span><br><span class="line">    &#125;;</span><br><span class="line">    $.fn.unbind = <span class="function"><span class="keyword">function</span>(<span class="params">event, callback</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.off(event, callback);</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最终的方法属性等都指向了 $.fn，这里就是原型的关键使用</span></span><br><span class="line">    zepto.Z.prototype = Z.prototype = $.fn;</span><br><span class="line"></span><br><span class="line">    $.zepto = zepto;</span><br><span class="line">    <span class="keyword">return</span> $;</span><br><span class="line">  &#125;)();</span><br><span class="line"></span><br><span class="line">  <span class="built_in">window</span>.Zepto = Zepto;</span><br><span class="line">  <span class="built_in">window</span>.$ === <span class="literal">undefined</span> &amp;&amp; (<span class="built_in">window</span>.$ = Zepto);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>amd 规范：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params">global, factory</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> define === <span class="string">"function"</span> &amp;&amp; define.amd) &#123;</span><br><span class="line">    define(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> factory(global);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    factory(global);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)(<span class="keyword">this</span>, <span class="function"><span class="keyword">function</span>(<span class="params">window</span>) </span>&#123;&#125;)</span><br></pre></td></tr></table></figure><h3 id="学习资料"><a href="#学习资料" class="headerlink" title="学习资料"></a>学习资料</h3><ul><li><a href="https://www.imooc.com/learn/745" target="_blank" rel="noopener">zepto 设计和源码分析</a> - 视频</li><li><a href="http://www.kancloud.cn/wangfupeng/zepto-design-srouce/173680" target="_blank" rel="noopener">zepto 对象思想与源码分析</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://www.kancloud.cn/wangfupeng/zepto-design-srouce/173680&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;zepto 对象思想与源码分析&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="前端框架" scheme="https://www.yangtao.site/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/"/>
    
    
      <category term="zepto" scheme="https://www.yangtao.site/tags/zepto/"/>
    
  </entry>
  
  <entry>
    <title>AJAX 请求</title>
    <link href="https://www.yangtao.site/2020/01/07/ajax/"/>
    <id>https://www.yangtao.site/2020/01/07/ajax/</id>
    <published>2020-01-07T07:06:16.000Z</published>
    <updated>2020-01-07T09:51:42.474Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>关于 http 请求的几种方式</p></blockquote><a id="more"></a><h2 id="AJAX"><a href="#AJAX" class="headerlink" title="AJAX"></a>AJAX</h2><p>具体来说，AJAX 包括以下几个步骤：</p><ul><li>创建 XMLHttpRequest 实例</li><li>发出 HTTP 请求</li><li>接收服务器传回的数据</li><li>更新网页数据</li></ul><h2 id="起步"><a href="#起步" class="headerlink" title="起步"></a>起步</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> MYTHOD = <span class="string">"GET"</span>;</span><br><span class="line"><span class="keyword">const</span> URL =</span><br><span class="line">  <span class="string">"https://api.github.com/search/repositories?q=javascript&amp;sort=stars"</span>;</span><br><span class="line"><span class="keyword">let</span> xhr = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//处理低版本IE不兼容问题</span></span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">window</span>.XMLHttpRequest) &#123;</span><br><span class="line">  xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  xhr = <span class="keyword">new</span> ActiveXObject(<span class="string">"Microsoft.XMLHTTP"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 指定回调函数，监听通信状态（readyState属性）的变化</span></span><br><span class="line">xhr.onreadystatechange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (xhr.readyState === <span class="number">4</span> &amp;&amp; xhr.status === <span class="number">200</span>) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(xhr.responseText);</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">xhr.onerror = <span class="function"><span class="params">err</span> =&gt;</span> <span class="built_in">console</span>.error(<span class="string">"Error: "</span>, xhr.statusText);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 发出 HTTP 请求</span></span><br><span class="line">xhr.open(MYTHOD, URL, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">xhr.send(<span class="literal">null</span>);</span><br></pre></td></tr></table></figure><h2 id="设置头信息"><a href="#设置头信息" class="headerlink" title="设置头信息"></a>设置头信息</h2><p>该方法必须在 open()之后、send()之前调用。如果该方法多次调用，设定同一个字段，则每一次调用的值会被合并成一个单一的值发送。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">xhr.setRequestHeader(<span class="string">"Content-Type"</span>, <span class="string">"application/json"</span>);</span><br><span class="line">xhr.setRequestHeader(<span class="string">"Content-Length"</span>, <span class="built_in">JSON</span>.stringify(data).length);</span><br><span class="line">xhr.send(<span class="built_in">JSON</span>.stringify(data));</span><br></pre></td></tr></table></figure><h2 id="Ajax-Promise"><a href="#Ajax-Promise" class="headerlink" title="Ajax+Promise"></a>Ajax+Promise</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> ajax = <span class="function">(<span class="params">&#123; method = <span class="string">"GET"</span>, path, body, headers &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">//进行Promise封装</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> request = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line"></span><br><span class="line">    request.open(method, path, <span class="literal">true</span>); <span class="comment">//配置</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (method == <span class="string">"GET"</span>) &#123;</span><br><span class="line">      request.send(<span class="literal">null</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> headers) &#123;</span><br><span class="line">        <span class="comment">//遍历header,设置响应头</span></span><br><span class="line">        <span class="keyword">let</span> value = headers[key];</span><br><span class="line">        request.setRequestHeader(key, value);</span><br><span class="line">      &#125;</span><br><span class="line">      request.send(body);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    request.onreadystatechange = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (request.readyState === <span class="number">4</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (request.status &gt;= <span class="number">200</span> &amp;&amp; request.status &lt; <span class="number">400</span>) &#123;</span><br><span class="line">          resolve.call(<span class="literal">undefined</span>, request.responseText);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (request.status &gt;= <span class="number">400</span>) &#123;</span><br><span class="line">          reject.call(<span class="literal">undefined</span>, request);</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">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用ajax</span></span><br><span class="line">ajax(&#123;</span><br><span class="line">  method: <span class="string">"get"</span>,</span><br><span class="line">  path: <span class="string">"https://api.github.com/search/repositories?q=javascript&amp;sort=stars"</span>,</span><br><span class="line">  headers: &#123;</span><br><span class="line">    <span class="string">"content-type"</span>: <span class="string">"application/json"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;).then(</span><br><span class="line">  responseText =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(responseText);</span><br><span class="line">  &#125;,</span><br><span class="line">  request =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(request);</span><br><span class="line">  &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h2 id="jQuery-使用"><a href="#jQuery-使用" class="headerlink" title="jQuery 使用"></a>jQuery 使用</h2><p>get 方法，返回一个 deferred 对象：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> URL =</span><br><span class="line">  <span class="string">"https://api.github.com/search/repositories?q=javascript&amp;sort=stars"</span>;</span><br><span class="line"><span class="keyword">let</span> request = $.get(URL);</span><br><span class="line">request</span><br><span class="line">  .done(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(data);</span><br><span class="line">  &#125;)</span><br><span class="line">  .fail(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(err);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者</span></span><br><span class="line"><span class="keyword">let</span> request = $.ajax(&#123;</span><br><span class="line">  url: URL,</span><br><span class="line">  type: <span class="string">"GET"</span></span><br><span class="line">&#125;);</span><br><span class="line">request.done(<span class="function"><span class="params">data</span> =&gt;</span> <span class="built_in">console</span>.log(data)).fail(<span class="function"><span class="params">err</span> =&gt;</span> <span class="built_in">console</span>.log(err));</span><br></pre></td></tr></table></figure><p>使用 promise：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getData</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    $.ajax(&#123;</span><br><span class="line">      url: <span class="string">"https://api.github.com/search/repositories?q=javascript&amp;sort=stars"</span>,</span><br><span class="line">      type: <span class="string">"GET"</span>,</span><br><span class="line">      success: <span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">        resolve(data);</span><br><span class="line">      &#125;,</span><br><span class="line">      error: <span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">        resolve(err);</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">getData()</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(data);</span><br><span class="line">  &#125;)</span><br><span class="line">  .catch(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(err);</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><h2 id="Fetch-函数"><a href="#Fetch-函数" class="headerlink" title="Fetch 函数"></a>Fetch 函数</h2><p>Fetch 提供了 Request 和 Response 对象（以及与网络请求有关的其他内容）的一般定义。</p><p>Fetch API 提供了 fetch() 方法，它被定义在 BOM 的 window 对象中，你可以用它来发起对远程资源的请求。</p><p>fetch() 方法返回的是一个 Promise 对象，让你能够对请求的返回结果进行检索。</p><p>fetch 的配置：</p><ul><li><code>Promise fetch(String url [, Object options])</code>;</li><li><code>Promise fetch(Request req [, Object options])</code>;</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> URL = <span class="string">'https://api.github.com/search/repositories?q=javascript&amp;sort=stars'</span>;</span><br><span class="line"><span class="keyword">let</span> req = <span class="keyword">new</span> Request(URL, &#123; <span class="attr">method</span>: <span class="string">"GET"</span>, <span class="attr">cache</span>: <span class="string">"reload"</span> &#125;);</span><br><span class="line">fetch(req)</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">response</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> response.json();</span><br><span class="line">  &#125;)</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">json</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(json);</span><br><span class="line">  &#125;).catch(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(err);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>fetch 和 ajax 的主要区别：</p><ul><li>fetch()返回的 promise 将不会拒绝 http 的错误状态，即使响应是一个 HTTP 404 或者 500</li><li>在默认情况下 fetch 不会接受或者发送 cookies</li></ul><h2 id="Axios"><a href="#Axios" class="headerlink" title="Axios"></a>Axios</h2><p>基本使用：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> URL =</span><br><span class="line">  <span class="string">"https://api.github.com/search/repositories?q=javascript&amp;sort=stars"</span>;</span><br><span class="line">axios</span><br><span class="line">  .get(URL)</span><br><span class="line">  .then(<span class="function"><span class="params">response</span> =&gt;</span> <span class="built_in">console</span>.log(response))</span><br><span class="line">  .catch(<span class="function"><span class="params">err</span> =&gt;</span> <span class="built_in">console</span>.log(err));</span><br></pre></td></tr></table></figure><p>或者使用 <code>async/await</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">getData</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> axios.get(URL);</span><br><span class="line">  &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">    <span class="built_in">console</span>.error(error);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">getData().then(<span class="function"><span class="params">data</span> =&gt;</span> <span class="built_in">console</span>.log(data));</span><br></pre></td></tr></table></figure><h2 id="资料"><a href="#资料" class="headerlink" title="资料"></a>资料</h2><ul><li><a href="http://javascript.ruanyifeng.com/bom/ajax.html" target="_blank" rel="noopener">AJAX – JavaScript 标准参考教程(alpha)</a></li><li><a href="https://segmentfault.com/a/1190000018935873?utm_source=tag-newest" target="_blank" rel="noopener">原生 javaScript 实现 Ajax 和 jQuery 实现 Ajax</a></li><li><a href="https://segmentfault.com/a/1190000015938472" target="_blank" rel="noopener">回调、使用 Promise 封装 ajax()、Promise 入门</a></li><li><a href="https://www.jianshu.com/p/7762515f8d1a" target="_blank" rel="noopener">Fetch</a></li><li><a href="http://www.axios-js.com/" target="_blank" rel="noopener">axios-易用、简洁且高效的 http 库</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;关于 http 请求的几种方式&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="面试" scheme="https://www.yangtao.site/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
      <category term="Ajax" scheme="https://www.yangtao.site/tags/Ajax/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript 类型判断</title>
    <link href="https://www.yangtao.site/2020/01/07/code-typeof/"/>
    <id>https://www.yangtao.site/2020/01/07/code-typeof/</id>
    <published>2020-01-07T06:42:25.000Z</published>
    <updated>2020-01-07T07:00:58.458Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>学习<a href="https://github.com/mqyqingfeng/Blog/issues/28" target="_blank" rel="noopener">JavaScript 专题之类型判断(上)</a>的文章总结，其实就是照抄，哈哈哈哈…顺便几一句文章里的话：所有这些点，都必须脚踏实地在具体应用场景下去分析、去选择，要让场景说话。</p></blockquote><a id="more"></a><h2 id="typeof"><a href="#typeof" class="headerlink" title="typeof"></a>typeof</h2><p>最新的 ECMAScript 标准定义了 8 种数据类型：</p><p>7 种原始类型:</p><ul><li>Boolean</li><li>Null</li><li>Undefined</li><li>Number</li><li>String</li><li>Symbol</li><li>BigInt</li></ul><p>和 Object</p><p>使用 typeof 检测类型如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"Number"</span>; <span class="comment">// number</span></span><br><span class="line"><span class="string">"String"</span>; <span class="comment">// string</span></span><br><span class="line"><span class="string">"Boolean"</span>; <span class="comment">// boolean</span></span><br><span class="line"><span class="string">"Undefined"</span>; <span class="comment">// undefined</span></span><br><span class="line"><span class="string">"Null"</span>; <span class="comment">// object</span></span><br><span class="line"><span class="string">"Symbol"</span>; <span class="comment">// symbol</span></span><br><span class="line"><span class="string">"BigInt"</span>; <span class="comment">// bigint</span></span><br><span class="line"><span class="string">"Object"</span>; <span class="comment">// object</span></span><br></pre></td></tr></table></figure><p>所以 typeof 能检测出七种基本类型的值，但是，除此之外 Object 下还有很多细分的类型呐，如 Array、Function、Date、RegExp、Error 等。</p><p>如果用 typeof 去检测这些类型，返回的都是 object，除了 Function：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="keyword">var</span> error = <span class="keyword">new</span> <span class="built_in">Error</span>();</span><br><span class="line"><span class="keyword">var</span> fn = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span> date); <span class="comment">// object</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span> error); <span class="comment">// object</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span> fn); <span class="comment">// function</span></span><br></pre></td></tr></table></figure><h2 id="Object-prototype-toString"><a href="#Object-prototype-toString" class="headerlink" title="Object.prototype.toString"></a>Object.prototype.toString</h2><p>所有，该如何区分 object 呢？我们用<code>Object.prototype.toString</code>。</p><p>规范：当 toString 方法被调用的时候，下面的步骤会被执行：</p><ul><li>如果 this 值是 undefined，就返回 <code>[object Undefined]</code></li><li>如果 this 的值是 null，就返回 <code>[object Null]</code></li><li>让 O 成为 <code>ToObject(this)</code> 的结果</li><li>让 class 成为 O 的内部属性 <code>[[Class]]</code> 的值</li><li>最后返回由 <code>&quot;[object &quot;</code> 和 <code>class</code> 和 <code>&quot;]&quot;</code> 三个部分组成的字符串</li></ul><p>通过规范，我们至少知道了调用 <code>Object.prototype.toString</code> 会返回一个由 <code>&quot;[object &quot; 和 class 和 &quot;]&quot;</code> 组成的字符串，而 class 是要判断的对象的内部属性。</p><p>我们可以了解到这个 class 值就是识别对象类型的关键！</p><p>正是因为这种特性，我们可以用 <code>Object.prototype.toString</code> 方法识别出更多类型！</p><p>先看下常见的 15 种（ES6 新增：Symbol Set Map，还有 BigInt）：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> number = <span class="number">1</span>; <span class="comment">// [object Number]</span></span><br><span class="line"><span class="keyword">var</span> string = <span class="string">"123"</span>; <span class="comment">// [object String]</span></span><br><span class="line"><span class="keyword">var</span> boolean = <span class="literal">true</span>; <span class="comment">// [object Boolean]</span></span><br><span class="line"><span class="keyword">var</span> und = <span class="literal">undefined</span>; <span class="comment">// [object Undefined]</span></span><br><span class="line"><span class="keyword">var</span> nul = <span class="literal">null</span>; <span class="comment">// [object Null]</span></span><br><span class="line"><span class="keyword">var</span> obj = &#123; <span class="attr">a</span>: <span class="number">1</span> &#125;; <span class="comment">// [object Object]</span></span><br><span class="line"><span class="keyword">var</span> array = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]; <span class="comment">// [object Array]</span></span><br><span class="line"><span class="keyword">var</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>(); <span class="comment">// [object Date]</span></span><br><span class="line"><span class="keyword">var</span> error = <span class="keyword">new</span> <span class="built_in">Error</span>(); <span class="comment">// [object Error]</span></span><br><span class="line"><span class="keyword">var</span> reg = <span class="regexp">/a/g</span>; <span class="comment">// [object RegExp]</span></span><br><span class="line"><span class="keyword">var</span> func = <span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>) </span>&#123;&#125;; <span class="comment">// [object Function]</span></span><br><span class="line"><span class="keyword">var</span> symb = <span class="built_in">Symbol</span>(<span class="string">"test"</span>); <span class="comment">// [object Symbol]</span></span><br><span class="line"><span class="keyword">var</span> set = <span class="keyword">new</span> <span class="built_in">Set</span>(); <span class="comment">// [object Set]</span></span><br><span class="line"><span class="keyword">var</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>(); <span class="comment">// [object Map]</span></span><br><span class="line"><span class="keyword">var</span> bigI = BigInt(<span class="number">1</span>); <span class="comment">// [object BigInt]</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkType</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>, l = <span class="built_in">arguments</span>.length; i &lt; l; i++) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">Object</span>.prototype.toString.call(<span class="built_in">arguments</span>[i]));</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">checkType(</span><br><span class="line">  number,</span><br><span class="line">  string,</span><br><span class="line">  boolean,</span><br><span class="line">  und,</span><br><span class="line">  nul,</span><br><span class="line">  obj,</span><br><span class="line">  array,</span><br><span class="line">  date,</span><br><span class="line">  error,</span><br><span class="line">  reg,</span><br><span class="line">  func,</span><br><span class="line">  symb,</span><br><span class="line">  set,</span><br><span class="line">  map,</span><br><span class="line">  bigI</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>除了以上 15 种，还有以下 3 种：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.prototype.toString.call(<span class="built_in">Math</span>)); <span class="comment">// [object Math]</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.prototype.toString.call(<span class="built_in">JSON</span>)); <span class="comment">// [object JSON]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fn = <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="built_in">Object</span>.prototype.toString.call(<span class="built_in">arguments</span>)); <span class="comment">// [object Arguments]</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">fn();</span><br></pre></td></tr></table></figure><h2 id="type-API"><a href="#type-API" class="headerlink" title="type API"></a>type API</h2><p>写一个 type 函数能检测各种类型的值，如果是基本类型，就使用 typeof，引用类型就使用 toString。</p><p>此外鉴于 typeof 的结果是小写，我也希望所有的结果都是小写。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> class2type = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="string">"Boolean Number String Function Array Date RegExp Object Error Null Undefined"</span></span><br><span class="line">  .split(<span class="string">" "</span>)</span><br><span class="line">  .map(<span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</span><br><span class="line">    class2type[<span class="string">"[object "</span> + item + <span class="string">"]"</span>] = item.toLowerCase(); <span class="comment">// e.g. '[object Boolean]': 'boolean'</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">type</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (obj == <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> obj + <span class="string">""</span>; <span class="comment">// IE6</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> obj === <span class="string">"object"</span> || <span class="keyword">typeof</span> obj === <span class="string">"function"</span></span><br><span class="line">    ? class2type[<span class="built_in">Object</span>.prototype.toString.call(obj)] || <span class="string">"object"</span></span><br><span class="line">    : <span class="keyword">typeof</span> obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里<code>class2type[Object.prototype.toString.call(obj)] || &quot;object&quot;</code>的 object，为了 ES6 新增的 Symbol、Map、Set 等类型返回 object。</p><p>当然也可以添加进去，返回的就是对应的类型：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> class2type = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="string">"Boolean Number String Function Array Date RegExp Object Error Null Undefined Symbol Set Map BigInt"</span></span><br><span class="line">  .split(<span class="string">" "</span>)</span><br><span class="line">  .map(<span class="function"><span class="keyword">function</span>(<span class="params">item</span>) </span>&#123;</span><br><span class="line">    class2type[<span class="string">"[object "</span> + item + <span class="string">"]"</span>] = item.toLowerCase();</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">type</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (obj == <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> obj + <span class="string">""</span>; <span class="comment">// IE6</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">typeof</span> obj === <span class="string">"object"</span> || <span class="keyword">typeof</span> obj === <span class="string">"function"</span></span><br><span class="line">    ? class2type[<span class="built_in">Object</span>.prototype.toString.call(obj)]</span><br><span class="line">    : <span class="keyword">typeof</span> obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="isFunction"><a href="#isFunction" class="headerlink" title="isFunction"></a>isFunction</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isFunction</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> type(obj) === <span class="string">"function"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="isArray"><a href="#isArray" class="headerlink" title="isArray"></a>isArray</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> isArray =</span><br><span class="line">  <span class="built_in">Array</span>.isArray ||</span><br><span class="line">  <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> type(obj) === <span class="string">"array"</span>;</span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure><h2 id="plainObject"><a href="#plainObject" class="headerlink" title="plainObject"></a>plainObject</h2><p><code>plainObject</code> 来自于 jQuery，可以翻译成纯粹的对象，所谓”纯粹的对象”，就是该对象是通过 “{}” 或 “new Object” 创建的，该对象含有零个或者多个键值对。</p><p>之所以要判断是不是 <code>plainObject</code>，是为了跟其他的 JavaScript 对象如 null，数组，宿主对象（documents）等作区分，因为这些用 typeof 都会返回 object。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 上节中写 type 函数时，用来存放 toString 映射结果的对象</span></span><br><span class="line"><span class="keyword">var</span> class2type = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 相当于 Object.prototype.toString</span></span><br><span class="line"><span class="keyword">var</span> toString = class2type.toString;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 相当于 Object.prototype.hasOwnProperty</span></span><br><span class="line"><span class="keyword">var</span> hasOwn = class2type.hasOwnProperty;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isPlainObject</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> proto, Ctor;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 排除掉明显不是obj的以及一些宿主对象如Window</span></span><br><span class="line">  <span class="keyword">if</span> (!obj || toString.call(obj) !== <span class="string">"[object Object]"</span>) &#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="comment">/**</span></span><br><span class="line"><span class="comment">   * getPrototypeOf es5 方法，获取 obj 的原型</span></span><br><span class="line"><span class="comment">   * 以 new Object 创建的对象为例的话</span></span><br><span class="line"><span class="comment">   * obj.__proto__ === Object.prototype</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  proto = <span class="built_in">Object</span>.getPrototypeOf(obj);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 没有原型的对象是纯粹的，Object.create(null) 就在这里返回 true</span></span><br><span class="line">  <span class="keyword">if</span> (!proto) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 以下判断通过 new Object 方式创建的对象</span></span><br><span class="line"><span class="comment">   * 判断 proto 是否有 constructor 属性，如果有就让 Ctor 的值为 proto.constructor</span></span><br><span class="line"><span class="comment">   * 如果是 Object 函数创建的对象，Ctor 在这里就等于 Object 构造函数</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  Ctor = hasOwn.call(proto, <span class="string">"constructor"</span>) &amp;&amp; proto.constructor;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 在这里判断 Ctor 构造函数是不是 Object 构造函数，用于区分自定义构造函数和 Object 构造函数</span></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="keyword">typeof</span> Ctor === <span class="string">"function"</span> &amp;&amp;</span><br><span class="line">    hasOwn.toString.call(Ctor) === hasOwn.toString.call(<span class="built_in">Object</span>)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="EmptyObject"><a href="#EmptyObject" class="headerlink" title="EmptyObject"></a>EmptyObject</h2><p>jQuery 提供了 <code>isEmptyObject</code> 方法来判断是否是空对象，代码简单：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isEmptyObject</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> name;</span><br><span class="line">  <span class="comment">// 判断是否有属性，for 循环一旦执行，就说明有属性，有属性就会返回 false</span></span><br><span class="line">  <span class="keyword">for</span> (name <span class="keyword">in</span> obj) &#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 class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(isEmptyObject(&#123;&#125;)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(isEmptyObject([])); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(isEmptyObject(<span class="literal">null</span>)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(isEmptyObject(<span class="literal">undefined</span>)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(isEmptyObject(<span class="number">1</span>)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(isEmptyObject(<span class="string">""</span>)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(isEmptyObject(<span class="literal">true</span>)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><h2 id="Window-对象"><a href="#Window-对象" class="headerlink" title="Window 对象"></a>Window 对象</h2><p>Window 对象作为客户端 JavaScript 的全局对象，它有一个 window 属性指向自身。我们可以利用这个特性判断是否是 Window 对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isWindow</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> obj !== <span class="literal">null</span> &amp;&amp; obj === obj.window;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="isArrayLike"><a href="#isArrayLike" class="headerlink" title="isArrayLike"></a>isArrayLike</h2><p>如果 isArrayLike 返回 true，至少要满足三个条件之一：</p><ul><li>是数组</li><li>长度为 0</li><li>lengths 属性是大于 0 的数字类型，并且 <code>obj[length - 1]</code>必须存在</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isArrayLike</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// obj 必须有 length属性</span></span><br><span class="line">  <span class="keyword">var</span> length = !!obj &amp;&amp; <span class="string">"length"</span> <span class="keyword">in</span> obj &amp;&amp; obj.length;</span><br><span class="line">  <span class="keyword">var</span> typeRes = type(obj);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 排除掉函数和 Window 对象</span></span><br><span class="line">  <span class="keyword">if</span> (typeRes === <span class="string">"function"</span> || isWindow(obj)) &#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">    typeRes === <span class="string">"array"</span> ||</span><br><span class="line">    length === <span class="number">0</span> ||</span><br><span class="line">    (<span class="keyword">typeof</span> length === <span class="string">"number"</span> &amp;&amp; length &gt; <span class="number">0</span> &amp;&amp; length - <span class="number">1</span> <span class="keyword">in</span> obj)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="isElement"><a href="#isElement" class="headerlink" title="isElement"></a>isElement</h2><p>判断是不是 DOM 元素</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isElement</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> !!(obj &amp;&amp; obj.nodeType === <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> div = <span class="built_in">document</span>.createElement(<span class="string">"div"</span>);</span><br><span class="line"><span class="built_in">console</span>.log(isElement(div)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(isElement(<span class="string">""</span>)); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><h2 id="原文地址"><a href="#原文地址" class="headerlink" title="原文地址"></a>原文地址</h2><ul><li><a href="https://github.com/mqyqingfeng/Blog/issues/28" target="_blank" rel="noopener">JavaScript 专题之类型判断(上)</a></li><li><a href="https://github.com/mqyqingfeng/Blog/issues/30" target="_blank" rel="noopener">JavaScript 专题之类型判断(下)</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;学习&lt;a href=&quot;https://github.com/mqyqingfeng/Blog/issues/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JavaScript 专题之类型判断(上)&lt;/a&gt;的文章总结，其实就是照抄，哈哈哈哈…顺便几一句文章里的话：所有这些点，都必须脚踏实地在具体应用场景下去分析、去选择，要让场景说话。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/categories/JavaScript/"/>
    
    
      <category term="面试" scheme="https://www.yangtao.site/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="JavaScript" scheme="https://www.yangtao.site/tags/JavaScript/"/>
    
  </entry>
  
</feed>
