<?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://hybin.github.io/"/>
  <updated>2020-07-05T12:59:12.336Z</updated>
  <id>https://hybin.github.io/</id>
  
  <author>
    <name>Hybin Hwang</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Unit Test with Jtest and Enzyme inside the ReactJS</title>
    <link href="https://hybin.github.io/2020/07/05/Unit-Test-with-Jtest-and-Enzyme-inside-the-ReactJS/"/>
    <id>https://hybin.github.io/2020/07/05/Unit-Test-with-Jtest-and-Enzyme-inside-the-ReactJS/</id>
    <published>2020-07-05T12:25:53.000Z</published>
    <updated>2020-07-05T12:59:12.336Z</updated>
    
    <content type="html"><![CDATA[<p>从接触软件工程开始，编写单元测试就一直被不断强调，其重要性可想而知。那么，如何在我们的 React 项目中编写单元测试呢？</p><a id="more"></a><p>要知道，借助<code>npx create-react-app [project-name] --template typescript</code>命令，我们可以创建一个基于 TypeScript 的 React 项目，并且项目内部集成<code>Jtest</code>工具，通过运行<code>yarn test</code>命令，或者<code>npm run test</code>命令，能够进行单元测试。当然，前提是我们编写了相应的单元测试代码，即以<code>.test.tsx</code>或者<code>.spec.tsx</code> 后缀的文件。<code>Jtest</code>工具会自动检测改动并运行单元测试。那么，关键问题是，如何编写单元测试代码？</p><p>首先，我们需要借助<code>Enzyme</code>工具，作为React生态系统里一个通用工具，它方便了我们针对组件的行为编写测试。在项目根文件夹，运行命令：</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 -D enzyme @types/enzyme enzyme-adapter-react-16 react-addons-test-utils</span><br></pre></td></tr></table></figure><p>我们同时安装了<code>enzyme</code>和<code>@types/enzyme</code>，前者包含实际运行的<strong>JavaScript</strong>代码包，而后者则包含了声明文件，以便<strong>TypeScript</strong>能够了解该如何使用<code>Enzyme</code>。此外，我们安装了<code>react-addons-test-utils</code>，以便于在我们的 React 项目中使用<code>Enzyme</code>编写单元测试代码。</p><p>接下来，我们通过一组示例，来看看<code>Enzyme</code>的正确打开方式。假定我们创建了一个基于 TypeScript 的 React 项目，那么其<code>src</code>下的目录大致如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── App.css</span><br><span class="line">├── App.test.tsx</span><br><span class="line">├── App.tsx</span><br><span class="line">├── components</span><br><span class="line">│   └── ...</span><br><span class="line">├── index.css</span><br><span class="line">├── index.tsx</span><br><span class="line">├── logo.svg</span><br><span class="line">├── react-app-env.d.ts</span><br><span class="line">├── serviceWorker.ts</span><br><span class="line">└── setupTests.ts</span><br></pre></td></tr></table></figure><p>为了使用配合<code>React@16</code>使用<code>Enzyme</code>，编辑<code>setupTests.ts</code>，否则会在运行单元测试时出现报错：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// jest-dom adds custom jest matchers for asserting on DOM nodes.</span></span><br><span class="line"><span class="comment">// allows you to do things like:</span></span><br><span class="line"><span class="comment">// expect(element).toHaveTextContent(/react/i)</span></span><br><span class="line"><span class="comment">// learn more: https://github.com/testing-library/jest-dom</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'@testing-library/jest-dom/extend-expect'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// use enzyme</span></span><br><span class="line"><span class="keyword">import</span> &#123; configure &#125; <span class="keyword">from</span> <span class="string">'enzyme'</span></span><br><span class="line"><span class="keyword">import</span> Adapter <span class="keyword">from</span> <span class="string">'enzyme-adapter-react-16'</span></span><br><span class="line"></span><br><span class="line">configure(&#123; adapter: <span class="keyword">new</span> Adapter() &#125;)</span><br></pre></td></tr></table></figure><p>现在，我们可以编写一个<code>Hello</code>组件，并将其命名为<code>Hello.tsx</code>：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">interface</span> Props &#123;</span><br><span class="line">    name: <span class="built_in">string</span>,</span><br><span class="line">    enthusiasmLevel?: <span class="built_in">number</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Method I: Define React Component by function</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Hello</span>(<span class="params">&#123; name, enthusiasmLevel = 1&#125;: Props</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (enthusiasmLevel &lt;= <span class="number">0</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">'You could be a little more enthusiastic. :D'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        &lt;div className=<span class="string">"hello"</span>&gt;</span><br><span class="line">            &lt;div className=<span class="string">"greeting"</span>&gt;</span><br><span class="line">                Hello &#123;name + getExclamationMarks(enthusiasmLevel)&#125;</span><br><span class="line">            &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">        &lt;/</span>div&gt;</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Method II: Define React Component by class</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">class Hello extends React.Component&lt;Props, object&gt; &#123;</span></span><br><span class="line"><span class="comment">    render() &#123;</span></span><br><span class="line"><span class="comment">        const &#123; name, enthusiasmLevel = 1 &#125; = this.props;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        if (enthusiasmLevel &lt;= 0) &#123;</span></span><br><span class="line"><span class="comment">            throw new Error('You could be a little more enthusiastic. :D');</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">        return (</span></span><br><span class="line"><span class="comment">            &lt;div className="hello"&gt;</span></span><br><span class="line"><span class="comment">                &lt;div className="greeting"&gt;</span></span><br><span class="line"><span class="comment">                    Hello &#123;name + getExclamationMarks(enthusiasmLevel)&#125;</span></span><br><span class="line"><span class="comment">                &lt;/div&gt;</span></span><br><span class="line"><span class="comment">            &lt;/div&gt;</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"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Hello;</span><br><span class="line"></span><br><span class="line"><span class="comment">// helpers</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getExclamationMarks</span>(<span class="params">numChars: <span class="built_in">number</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Array</span>(numChars + <span class="number">1</span>).join(<span class="string">'!'</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>那么，针对<code>Hello</code>组件的单元测试，可以在同一文件夹下创建文件<code>Hello.test.tsx</code>：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> enzyme <span class="keyword">from</span> <span class="string">'enzyme'</span>;</span><br><span class="line"><span class="keyword">import</span> Hello <span class="keyword">from</span> <span class="string">'./Hello'</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">it(<span class="string">'renders the correct text when no enthusiasm level is given'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> hello = enzyme.shallow(&lt;Hello name=<span class="string">'Daniel'</span> /&gt;);</span><br><span class="line">    expect(hello.find(<span class="string">".greeting"</span>).text()).toEqual(<span class="string">'Hello Daniel!'</span>)</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">it(<span class="string">'renders the correct text with an explicit enthusiasm of 1'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> hello = enzyme.shallow(&lt;Hello name=<span class="string">'Daniel'</span> enthusiasmLevel=&#123;<span class="number">1</span>&#125;/&gt;);</span><br><span class="line">    expect(hello.find(<span class="string">".greeting"</span>).text()).toEqual(<span class="string">'Hello Daniel!'</span>)</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">it(<span class="string">'renders the correct text with an explicit enthusiasm level of 5'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> hello = enzyme.shallow(&lt;Hello name=<span class="string">'Daniel'</span> enthusiasmLevel=&#123;<span class="number">5</span>&#125; /&gt;);</span><br><span class="line">    expect(hello.find(<span class="string">".greeting"</span>).text()).toEqual(<span class="string">'Hello Daniel!!!!!'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">it(<span class="string">'throws when the enthusiasm level is 0'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">     expect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        enzyme.shallow(&lt;Hello name=<span class="string">'Daniel'</span> enthusiasmLevel=&#123;<span class="number">0</span>&#125; /&gt;);</span><br><span class="line">    &#125;).toThrow();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">it(<span class="string">'throws when the enthusiasm level is negative'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    expect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        enzyme.shallow(&lt;Hello name=<span class="string">'Daniel'</span> enthusiasmLevel=&#123;<span class="number">-1</span>&#125; /&gt;);</span><br><span class="line">    &#125;).toThrow();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>更多测试接口可以参看<a href="https://enzymejs.github.io/enzyme/docs/api/" target="_blank" rel="noopener">API Reference</a>.</p><p><strong>参考文献</strong></p><p>使用Jest编写测试 - <a href="https://typescript.bootcss.com/tutorials/react.html" target="_blank" rel="noopener">React - TypeScript 中文手册</a></p><p>以上。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;从接触软件工程开始，编写单元测试就一直被不断强调，其重要性可想而知。那么，如何在我们的 React 项目中编写单元测试呢？&lt;/p&gt;
    
    </summary>
    
    
      <category term="前端" scheme="https://hybin.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="Web Development" scheme="https://hybin.github.io/tags/Web-Development/"/>
    
      <category term="TypeScript" scheme="https://hybin.github.io/tags/TypeScript/"/>
    
  </entry>
  
  <entry>
    <title>字节跳动校招前端面试知识点</title>
    <link href="https://hybin.github.io/2020/03/03/%E5%AD%97%E8%8A%82%E8%B7%B3%E5%8A%A8%E6%A0%A1%E6%8B%9B%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    <id>https://hybin.github.io/2020/03/03/%E5%AD%97%E8%8A%82%E8%B7%B3%E5%8A%A8%E6%A0%A1%E6%8B%9B%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E7%9F%A5%E8%AF%86%E7%82%B9/</id>
    <published>2020-03-03T13:45:00.000Z</published>
    <updated>2020-03-03T14:04:46.874Z</updated>
    
    <content type="html"><![CDATA[<p>秋招流年不利，于是发奋刷算法题，春招时来运转，拿到了字节跳动的前端Offer。准备面试阶段，将去年所有前端面经的题目所涉及到的知识点作了一遍梳理。实践证明，面试问题确实没有超出相关知识点范围，权作参考吧！</p><a id="more"></a><h2 id="计算机基础"><a href="#计算机基础" class="headerlink" title="计算机基础"></a>计算机基础</h2><h3 id="进程与线程"><a href="#进程与线程" class="headerlink" title="进程与线程"></a>进程与线程</h3><blockquote><p>进程是正在运行的程序的实例，是一个具有一定独立功能的程序关于某个数据集合的一次运行活动，是操作系统动态执行的基本单元。</p></blockquote><p><strong>组成部分</strong>: 文本区域（text region）、数据区域（data region）和堆栈（stack region）。文本区域存储处理器执行的代码；数据区域存储变量和进程执行期间使用的动态分配的内存；堆栈区域存储着活动过程调用的指令和本地变量。</p><p><a href="https://hit-alibaba.github.io/interview/basic/arch/Concurrency.html" target="_blank" rel="noopener">具体参看</a></p><blockquote><p>线程是进程中的实体,一个进程可以拥有多个线程，一个线程必须拥有一个父进程,线程不拥有系统资源。只有运行必须的一些数据结构,他与父进行的其他线程共享该进程的其他全部资源,线程可以创建和撤销线程</p></blockquote><h3 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a>线程池</h3><p>开始之前，需要明确几个概念，方便后面理解线程池的运行原理。</p><ul><li><p><strong>核心线程（corePool）</strong>：线程池最终执行任务的角色肯定还是线程，同时我们也会限制线程的数量，所以我们可以这样理解核心线程，<strong>有新任务提交时，首先检查核心线程数，如果核心线程都在工作，而且数量也已经达到最大核心线程数，则不会继续新建核心线程，而会将任务放入等待队列</strong>。</p></li><li><p><strong>等待队列 (workQueue)</strong>：等待队列用于存储<strong>当核心线程都在忙时，继续新增的任务，核心线程在执行完当前任务后，也会去等待队列拉取任务继续执行</strong>，这个队列一般是一个线程安全的阻塞队列，它的容量也可以由开发者根据业务来定制。</p></li><li><p><strong>非核心线程</strong>：<strong>当等待队列满了，如果当前线程数没有超过最大线程数，则会新建线程执行任务</strong>，那么核心线程和非核心线程到底有什么区别呢？<strong>说出来你可能不信，本质上它们没有什么区别，创建出来的线程也根本没有标识去区分它们是核心还是非核心的，线程池只会去判断已有的线程数（包括核心和非核心）去跟核心线程数和最大线程数比较，来决定下一步的策略</strong>。</p></li><li><p><strong>线程活动保持时间 (keepAliveTime)</strong>：线程空闲下来之后，保持存活的持续时间，超过这个时间还没有任务执行，该工作线程结束。</p></li><li><p><strong>饱和策略 (RejectedExecutionHandler)</strong>：当等待队列已满，线程数也达到最大线程数时，线程池会根据饱和策略来执行后续操作，默认的策略是抛弃要加入的任务。</p></li></ul><p><img src="https://user-gold-cdn.xitu.io/2019/3/13/169758ccf138378c?imageView2/0/w/1280/h/960/ignore-error/1" alt="Threads"></p><blockquote><p>那么，线程池的基本思想还是一种对象池的思想，开辟一块内存空间，里面存放了众多(未死亡)的线程，池中线程执行调度由池管理器来处理。当有线程任务时，从池中取一个，执行完成后线程对象归池，这样可以避免反复创建线程对象所带来的性能开销，节省了系统的资源。</p></blockquote><h3 id="TCP-UDP"><a href="#TCP-UDP" class="headerlink" title="TCP / UDP"></a>TCP / UDP</h3><div class="table-container"><table><thead><tr><th>OSI七层模型</th><th>TCP/IP概念层模型</th><th>功能</th><th>TCP/IP协议族</th></tr></thead><tbody><tr><td>应用层</td><td>应用层</td><td>文件传输、电子邮件、文件服务等等</td><td>TFTP、HTTP、SMTP</td></tr><tr><td>表示层</td><td>应用层</td><td>数据格式化、数据加密</td><td></td></tr><tr><td>会话层</td><td>应用层</td><td>建立或接触与别的点的联系</td><td></td></tr><tr><td>传输层</td><td>传输层</td><td>提供端对端的借口</td><td>TCP、UDP</td></tr><tr><td>网络层</td><td>网络层</td><td>为数据包选择路由</td><td>IP</td></tr><tr><td>数据链路层</td><td>链路层</td><td>传输有地址的帧以及错误检测功能</td><td>ARP</td></tr><tr><td>物理层</td><td>链路层</td><td>以二进制形式在物理媒体上传输数据</td><td>IEEE802</td></tr></tbody></table></div><h4 id="UDP"><a href="#UDP" class="headerlink" title="UDP"></a>UDP</h4><blockquote><p>UDP协议全称是用户数据报协议，在网络中它与TCP协议一样用于处理数据包，是一种无连接的协议。在OSI模型中，在第四层——传输层，处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点，也就是说，当报文发送之后，是无法得知其是否安全完整到达的。</p></blockquote><ul><li><p>面向无连接</p><p>具体来说就是：</p><ul><li>在发送端，应用层将数据传递给传输层的 UDP 协议，UDP 只会给数据增加一个 UDP 头标识，然后就传递给网络层了</li><li>在接收端，网络层将数据传递给传输层，UDP 只去除 IP 报文头就传递给应用层，不会任何拼接操作</li></ul></li><li><p>有单播，多播，广播的功能</p><p>UDP 不止支持一对一的传输方式，同样支持一对多，多对多，多对一的方式，也就是说 UDP 提供了单播，多播，广播的功能。</p></li><li><p>面向报文</p><p>发送方的UDP对应用程序交下来的报文，在添加首部后就向下交付IP层。UDP对应用层交下来的报文，既不合并，也不拆分，而是保留这些报文的边界。因此，应用程序必须选择合适大小的报文。</p></li><li><p>不可靠</p><p>有单播，多播，广播的功能</p></li></ul><h4 id="TCP"><a href="#TCP" class="headerlink" title="TCP"></a>TCP</h4><blockquote><p>TCP协议全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议，由 IETF 的RFC 793定义。TCP 是面向连接的、可靠的流协议。</p></blockquote><h4 id="可靠传输"><a href="#可靠传输" class="headerlink" title="可靠传输"></a>可靠传输</h4><ol><li><p>交互数据流传输</p><ul><li>停止等待协议</li><li>nagle 算法</li></ul></li><li><p>成块数据流传输</p><ul><li><p>滑动窗口协议</p><blockquote><p><strong>滑动窗户协议</strong>是指数据发送方有一个发送窗口，发送窗口范围内的数据允许发送，随着接收方传来的确认信息以及通知的接收窗口的大小来动态调整发送窗口的起始位置以及大小。</p></blockquote><p><img src="https://user-gold-cdn.xitu.io/2019/3/18/16990147282828ca?imageView2/0/w/1280/h/960/ignore-error/1" alt="window"></p><p>如图所示：序号1-4的数据是已经发送过并且被确认的数据，TCP可以将这些数据清出缓存；序号5-11是在窗口范围内的数据，允许发送；序号12-16的数据在缓存中不允许发送。</p><p>窗口后沿是由接收方发送的确认序号决定的，窗口前沿是由确认序号与发送窗口大小共同决定的。而发送窗口大小并不一定等于接收方提供的接收窗口的大小，发送方会根据网络拥塞情况来动态调整发送窗口大小，前提是发送方发送窗口大小一定不大于接收方接收窗口大小。</p></li><li><p>流量控制</p><p><img src="https://user-gold-cdn.xitu.io/2019/3/18/1699014752ebf42b?imageView2/0/w/1280/h/960/ignore-error/1" alt="control"></p></li><li><p>拥塞控制</p><blockquote><p><strong>拥塞控制</strong>是指防止过多的数据注入网络中，这样可以使网络中路由器或者链路不致过载。现在通信线路的传输质量一般都很好，因传输出现差错丢弃分组的概率很小。因此，判断网络拥塞的依据就是出现了<strong>超时</strong>。</p><p>TCP进行拥塞控制常用的算法有四种：<strong>慢启动</strong>、<strong>拥塞避免</strong>、<strong>快重传</strong>、<strong>快恢复</strong>。</p></blockquote><ul><li><p>慢启动</p></li><li><p>拥塞避免</p></li><li><p>快重传</p></li><li><p>快恢复</p></li></ul></li></ul></li></ol><h3 id="HTTP-状态码"><a href="#HTTP-状态码" class="headerlink" title="HTTP 状态码"></a>HTTP 状态码</h3><h4 id="区分状态码"><a href="#区分状态码" class="headerlink" title="区分状态码"></a>区分状态码</h4><p>1××开头 - 信息提示<br>2××开头 - 请求成功<br>3××开头 - 请求被重定向<br>4××开头 - 请求错误<br>5××开头 - 服务器错误</p><h4 id="常见状态码"><a href="#常见状态码" class="headerlink" title="常见状态码"></a>常见状态码</h4><p>100 - Continue</p><p>101 - Switching protocol</p><p>102 - Processing</p><p>103 - Early Hints</p><p>200 - 请求成功，Ajax 接受到信息了</p><p>300 - 重定向</p><p>301 - Moved Permanently</p><p>304 - Not Modifoed</p><p>307 - Temporary Redirect</p><p>308 -  Permanent Redirect</p><p>400 - 服务器不理解请求</p><p>401 - 验证有误</p><p>403 - 服务器拒绝请求</p><p>404 - 请求页面错误</p><p>405 - Method Not Allowed</p><p>408 - Request Timeout</p><p>500 - 服务器内部错误，无法完成请求</p><p>501 - 此请求方法不被服务器支持且无法被处理</p><p>502 - Bad Gateway</p><p>503 - Service Unavaliable</p><p>504 - Gateway Timeout</p><h3 id="HTTP-跨域"><a href="#HTTP-跨域" class="headerlink" title="HTTP 跨域"></a>HTTP 跨域</h3><p>浏览器的同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。</p><p>举个例子，我们的网站可以从不同的源加载脚本文件、样式文件、图片、字体等等，但假若在我们自身的脚本文件中，我们仅允许向同一源发起资源请求，向不同的源请求或发送资源时，会形成跨域限制。</p><p><strong>如何解决跨院带来的困扰</strong></p><p><strong>CORS</strong></p><p>答案有很多，一个比较好的方案是跨域资源共享（CORS），我们可以通过设置相关HTTP请求头的参数来完成跨域请求。对于简单请求，包括GET、HEAD和POST，可以通过在服务端设置<code>Access-Control-Allow-Origin</code>对源进行处理；对于预检请求，则有必要设置更多请求头，并在服务端对<code>Access-Control-Allow-Headers</code> 进行响应的设置；对于附带身份的请求，则需在服务端对<code>Access-Control-Allow-Credentials</code>进行响应的设置。</p><p><strong>JSONP</strong></p><p>由于<code>AJAX</code> 无法跨域，而<code>&lt;script&gt;</code>标签的<code>src</code>属性是可以跨域的，也就是说，令跨域服务器调用本地数据，回调数据即可。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>&gt;</span><span class="undefined"> </span></span><br><span class="line"><span class="actionscript">    <span class="comment">// 得到航班信息查询结果后的回调函数 </span></span></span><br><span class="line"><span class="actionscript">    <span class="keyword">var</span> flightHandler = <span class="function"><span class="keyword">function</span><span class="params">(data)</span></span>&#123;</span></span><br><span class="line"><span class="actionscript">        alert(<span class="string">'你查询的航班结果是：票价 '</span> + data.price + <span class="string">' 元，'</span> + <span class="string">'余票 '</span> + data.tickets + <span class="string">' 张。'</span>);</span></span><br><span class="line"><span class="undefined">    &#125;; </span></span><br><span class="line"><span class="actionscript">    <span class="comment">// 提供jsonp服务的url地址（不管是什么类型的地址，最终生成的返回值都是一段javascript代码） </span></span></span><br><span class="line"><span class="actionscript">    <span class="keyword">var</span> url = <span class="string">"跨域服务器/flightResult.php?code=CA1998&amp;callback=flightHandler"</span>;</span></span><br><span class="line"><span class="actionscript">    <span class="comment">// 创建script标签，设置其属性 </span></span></span><br><span class="line"><span class="javascript">    <span class="keyword">var</span> script = <span class="built_in">document</span>.createElement(<span class="string">'script'</span>); </span></span><br><span class="line"><span class="actionscript">    script.setAttribute(<span class="string">'src'</span>, url); </span></span><br><span class="line"><span class="actionscript">    <span class="comment">// 把script标签加入head，此时调用开始 </span></span></span><br><span class="line"><span class="javascript">    <span class="built_in">document</span>.getElementsByTagName(<span class="string">'head'</span>)[<span class="number">0</span>].appendChild(script); </span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><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">flightHandler(&#123;</span><br><span class="line">    <span class="string">"code"</span>:<span class="string">"CA1998"</span>,</span><br><span class="line">    <span class="string">"price"</span>: <span class="number">1780</span>,</span><br><span class="line">    <span class="string">"tickets"</span>: <span class="number">5</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="comment">// 数据</span></span><br><span class="line">$data = [</span><br><span class="line">    <span class="string">"name"</span>:<span class="string">"anonymous66"</span>,</span><br><span class="line">    <span class="string">"age"</span>:<span class="string">"18"</span>,</span><br><span class="line">    <span class="string">"like"</span>:<span class="string">"jianshu"</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接收callback函数名称</span></span><br><span class="line">$callback = $_GET[<span class="string">'callback'</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出</span></span><br><span class="line"><span class="keyword">echo</span> $callback . <span class="string">"("</span> . json_encode($data) . <span class="string">")"</span>;</span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><p><strong>jsonp</strong>的核心则是动态添加<code>&lt;script&gt;</code>标签来调用<strong>服务器</strong>提供的<strong>js脚本</strong>。</p><h3 id="HTTPS-HTTP-over-TLS"><a href="#HTTPS-HTTP-over-TLS" class="headerlink" title="HTTPS(HTTP over TLS)"></a>HTTPS(HTTP over TLS)</h3><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Client-&gt;Server: 消息（TLS版本）+ 加密算法 + 压缩算法</span><br><span class="line">Server-&gt;Client: 消息（TLS版本）+ 加密算法 + 压缩算法 + 公开证书（公钥）</span><br><span class="line">Note left of Client: 验证证书 -&gt; 生成伪随机数 -&gt; 公钥加密 -&gt; 对称密钥</span><br><span class="line">Client-&gt;Server: 加密伪随机数</span><br><span class="line">Note right of Server: 解密伪随机数 -&gt; 生成对称主密钥</span><br><span class="line">Client-&gt;Server: 消息[&#39;Finished&#39;](加密) + 通讯散列值（加密）</span><br><span class="line">Note right of Server: 生成通讯散列值</span><br><span class="line">Note right of Server: 解密客户端信息</span><br><span class="line">Note right of Server: 比对通讯散列值</span><br><span class="line">Server-&gt;Client: 消息[&#39;Finished&#39;](加密)</span><br></pre></td></tr></table></figure><hr><p>我们以 Github 网站使用的 TLS 为例，使用浏览器可以看到它使用的加密为 <code>TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</code>。其中密钥交互算法是 <code>ECDHE_RSA</code>，对称加密算法是 <code>AES_128_GCM</code>，消息认证（MAC）算法为 <code>SHA256</code>。</p><h3 id="浏览器页面加载机制"><a href="#浏览器页面加载机制" class="headerlink" title="浏览器页面加载机制"></a>浏览器页面加载机制</h3><p>STEP 1：浏览器客户端借助URL向目标服务器发起HTTP请求：</p><ul><li>借助<a href="#DNS">DNS服务器解析</a>得到URL所指向的目标服务器的IP地址</li><li>通过TCP/IP协议，客户端与服务端进行三次握手，建立连接。</li><li>客户端向服务端发送HTTP请求</li></ul><p>STEP 2：服务器收到HTTP请求，并向客户端发送响应</p><p>STEP 3：客户端收到响应，开始下载HTML代码（开源服务器以8k / chunk的速率）</p><ul><li>解析HTML代码，生成DOM树</li><li>遇到<code>&lt;style&gt;</code>或<code>&lt;script&gt;</code>标签，下载响应的CSS文件和JS文件，而后继续构建DOM树</li><li>解析CSS代码，生成CSS规则树，并与DOM树结合，构建渲染树</li><li>解析JS代码，借助DOM API修改DOM树，借助CSSOM修改渲染树</li><li>将渲染树节点由上至下、由左至右压入文档流，并对页面进行布局</li><li>最后绘制页面</li></ul><h4 id="DNS"><a href="#DNS" class="headerlink" title="DNS"></a>DNS</h4><blockquote><p>DNS (Domain Name System)， 也叫网域名称系统，是互联网的一项服务。它实质上是一个 <strong>域名</strong> 和 <strong>IP</strong> 相互映射的分布式数据库，有了它，我们就可以通过域名更方便的访问互联网。</p></blockquote><p><strong>特点</strong></p><ul><li>分布式</li><li>协议支持TCP 和 UDP, 常用端口是53</li><li>每一级域名的长度限制是63</li><li>域名总长度限制是253</li></ul><p><strong>解析流程</strong></p><ol><li>客户端向本地DNS服务器(递归解析服务器) 发出解析tool.chinaz.com域名的请求</li><li>本地dns服务器查看缓存，是否有缓存过tool.chinaz.com域名，如果有直接返回给客户端；如果没有执行下一步</li><li>本地dns服务器向根域名服务器发送请求，查询com顶级域的nameserver 地址</li><li>拿到com域名的IP后，再向com nameserver发送请求，获取chinaz域名的nameserver地址</li><li>继续请求chinaz的nameserver, 获取tool域名的地址，最终得到了tool.chinaz.com的IP，本地dns服务器把这个结果缓存起来，以供下次查询快速返回</li><li>本地dns服务器把把结果返回给客户端</li></ol><h4 id="资源加载"><a href="#资源加载" class="headerlink" title="资源加载"></a>资源加载</h4><ul><li>首先会将所有需要加载的资源进行分类。</li><li>然后根据浏览器相关的安全策略，来决定资源的加载权限。</li><li>接着对各个资源的加载优先级进行计算和排序。</li><li>最后一步，根据加载优先级顺序来加载资源。</li></ul><h4 id="回流-Reflow-amp-重绘-Repaint"><a href="#回流-Reflow-amp-重绘-Repaint" class="headerlink" title="回流(Reflow) &amp; 重绘(Repaint)"></a>回流(Reflow) &amp; 重绘(Repaint)</h4><blockquote><p>当<code>Render Tree</code>中部分或全部元素的尺寸、结构、或某些属性发生改变时，浏览器重新渲染部分或全部文档的过程称为回流。</p></blockquote><div class="table-container"><table><thead><tr><th>页面首次渲染</th><th></th></tr></thead><tbody><tr><td>浏览器窗口大小发生改变</td><td><code>clientWidth</code>、<code>clientHeight</code>、<code>clientTop</code>、<code>clientLeft</code></td></tr><tr><td>元素尺寸或位置发生改变</td><td><code>offsetWidth</code>、<code>offsetHeight</code>、<code>offsetTop</code>、<code>offsetLeft</code></td></tr><tr><td>元素尺寸或位置发生改变</td><td><code>scrollWidth</code>、<code>scrollHeight</code>、<code>scrollTop</code>、<code>scrollLeft</code></td></tr><tr><td>元素尺寸或位置发生改变</td><td><code>scrollIntoView()</code>、<code>scrollIntoViewIfNeeded()</code></td></tr><tr><td>元素尺寸或位置发生改变</td><td><code>scrollTo()</code></td></tr><tr><td>元素内容变化</td><td></td></tr><tr><td>元素字体大小变化</td><td></td></tr><tr><td>添加或者删除<strong>可见</strong>的<code>DOM</code>元素</td><td></td></tr><tr><td>激活<code>CSS</code>伪类</td><td></td></tr><tr><td>查询某些属性或调用某些方法</td><td><code>getComputedStyle()</code>、<code>getBoundingClientRect()</code></td></tr></tbody></table></div><hr><blockquote><p>当页面中元素样式的改变并不影响它在文档流中的位置时（例如：<code>color</code>、<code>background-color</code>、<code>visibility</code>等），浏览器会将新样式赋予给元素并重新绘制它，这个过程称为重绘。</p></blockquote><p>浏览器会维护一个队列，把所有引起回流和重绘的操作放入队列中，如果队列中的任务数量或者时间间隔达到一个阈值的，浏览器就会将队列清空，进行一次批处理，这样可以把多次回流和重绘变成一次。</p><p><strong>延伸</strong></p><p>针对页面性能优化，为什么<code>transform</code>优于<code>top</code> ?</p><p>前文提到，元素位置发生改变，会引发回流，而<code>transform</code>通过创建一个RenderLayers合成层，拥有独立的GraphicsLayers，并不会引起整个页面回流。</p><h4 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h4><p><strong>CSS</strong></p><ul><li>避免使用<code>table</code>布局。</li><li>尽可能在<code>DOM</code>树的最末端改变<code>class</code>。</li><li>避免设置多层内联样式。</li><li>将动画效果应用到<code>position</code>属性为<code>absolute</code>或<code>fixed</code>的元素上。</li><li>避免使用<code>CSS</code>表达式（例如：<code>calc()</code>）。</li></ul><p><strong>JavaScript</strong></p><ul><li><p>避免频繁操作样式，最好一次性重写<code>style</code>属性，或者将样式列表定义为<code>class</code>并一次性更改<code>class</code>属性。</p></li><li><p>避免频繁操作<code>DOM</code>，创建一个<code>documentFragment</code>，在它上面应用所有<code>DOM操作</code>，最后再把它添加到文档中。</p></li><li><p>也可以先为元素设置<code>display: none</code>，操作结束后再把它显示出来。因为在<code>display</code>属性为<code>none</code>的元素上进行的<code>DOM</code>操作不会引发回流和重绘。</p></li><li><p>避免频繁读取会引发回流/重绘的属性，如果确实需要多次使用，就用一个变量缓存起来。</p></li><li><p>对具有复杂动画的元素使用绝对定位，使它脱离文档流，否则会引起父元素及后续元素频繁回流。</p></li></ul><h3 id="浏览器的缓存机制"><a href="#浏览器的缓存机制" class="headerlink" title="浏览器的缓存机制"></a>浏览器的缓存机制</h3><blockquote><p>浏览器的缓存机制也就是我们说的HTTP缓存机制，其机制是根据HTTP报文的缓存标识进行的。</p></blockquote><h4 id="缓存过程分析"><a href="#缓存过程分析" class="headerlink" title="缓存过程分析"></a>缓存过程分析</h4><p>浏览器与服务器通信的方式为应答模式，即是：浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果，会根据响应报文中HTTP头的缓存标识，决定是否缓存结果，是则将请求结果和缓存标识存入浏览器缓存中。</p><ul><li>浏览器每次发起请求，都会先在浏览器缓存中查找该请求的结果以及缓存标识</li><li>浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中</li></ul><h4 id="强制缓存"><a href="#强制缓存" class="headerlink" title="强制缓存"></a>强制缓存</h4><blockquote><p>强制缓存就是向浏览器缓存查找该请求结果，并根据该结果的缓存规则来决定是否使用该缓存结果的过程</p></blockquote><p>强制缓存的情况主要有三种:</p><ul><li>不存在该缓存结果和缓存标识，强制缓存失效，则直接向服务器发起请求</li><li>存在该缓存结果和缓存标识，但该结果已失效，强制缓存失效，则使用协商缓存</li><li>存在该缓存结果和缓存标识，且该结果尚未失效，强制缓存生效，直接返回该结果</li></ul><p><strong>问题的关键在于：强制缓存的缓存规则是什么？</strong></p><p>当浏览器向服务器发起请求时，服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器，控制强制缓存的字段分别是<code>Expires</code>和<code>Cache-Control</code>，其中<code>Cache-Control</code>优先级比<code>Expires</code>高。</p><p><strong>Expires</strong></p><blockquote><p>Expires是HTTP/1.0控制网页缓存的字段，其值为服务器返回该请求结果缓存的到期时间，即再次发起该请求时，如果客户端的时间小于Expires的值时，直接使用缓存结果。</p></blockquote><p>到了HTTP/1.1，<code>Expire</code>已经被<code>Cache-Control</code>替代，原因在于<code>Expires</code>控制缓存的原理是使用客户端的时间与服务端返回的时间做对比，那么如果客户端与服务端的时间因为某些原因（例如时区不同；客户端和服务端有一方的时间不准确）发生误差，那么强制缓存则会直接失效，这样的话强制缓存的存在则毫无意义，那么<code>Cache-Control</code>又是如何控制的呢？</p><p><strong>Cache-Control</strong></p><p>在HTTP/1.1中，Cache-Control是最重要的规则，主要用于控制网页缓存，主要取值为：</p><ul><li><code>public</code>：所有内容都将被缓存（客户端和代理服务器都可缓存）</li><li><code>private</code>：所有内容只有客户端可以缓存，Cache-Control的默认取值</li><li><code>no-cache</code>：客户端缓存内容，但是是否使用缓存则需要经过协商缓存来验证决定</li><li><code>no-store</code>：所有内容都不会被缓存，即不使用强制缓存，也不使用协商缓存</li><li><code>max-age=xxx (xxx is numeric)</code>：缓存内容将在xxx秒后失效</li><li><code>no-transform</code>: 告知服务端希望获取的实体数据没有被转换</li><li><code>only-if-cached</code>：告知服务端获取缓存的资源，不向原服务器请求</li></ul><p>在浏览器中，浏览器会在js和图片等文件解析执行后直接存入内存缓存中，那么当刷新页面时只需直接从内存缓存中读取(from memory cache)；而css文件则会存入硬盘文件中，所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。</p><h4 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h4><blockquote><p>协商缓存就是强制缓存失效后，浏览器携带缓存标识向服务器发起请求，由服务器根据缓存标识决定是否使用缓存的过程</p></blockquote><p>协商缓存主要有两种情况：</p><ul><li>协商缓存生效，返回304</li><li>协商缓存失效，返回200和请求结果结果</li></ul><h4 id="拓展"><a href="#拓展" class="headerlink" title="拓展"></a>拓展</h4><p>与缓存相关的HTTP首部字段大致如下</p><ol><li><p>通用首部字段</p><p>| 名称          | 说明                                         |<br>| ——————- | —————————————————————— |<br>| Cache-Control | 控制缓存行为                                 |<br>| Pragma        | HTTP 1.0遗留字段，取值为<code>no-cache</code>时禁用缓存 |</p><p><code>Pragma</code>的优先级高于<code>Cache-Control</code>。</p></li><li><p>请求首部字段</p><p>| 名称                | 说明                           |<br>| —————————- | ——————————————— |<br>| If-Match            | 比较E-tag是否一致              |<br>| If-None-Match       | 比较E-tag是否不一致            |<br>| If-Modified-Since   | 比较资源最后更新时间是否一致   |<br>| If-Unmodified-Since | 比较资源最后更新时间是否不一致 |</p></li><li><p>响应首部字段</p><p>| 名称  | 说明           |<br>| ——- | ——————— |<br>| E-tag | 资源的匹配信息 |</p></li><li><p>实体首部字段</p><p>| 名称          | 说明                               |<br>| ——————- | ————————————————— |<br>| Expires       | HTTP 1.0遗留字段，实体主体过期时间 |<br>| Last-Modified | 资源最后一次修改时间               |</p><p>相对于<code>pragma</code>字段禁用缓存，<code>Expires</code>字段用于启用和定义缓存，即规定资源过期时间，该时间是相对于服务器时间而言的，因此，客户端与服务端时间不一致，会出现问题（为了解决这一问题，HTTP1.1提出了字段<code>Cache-Control</code>）。此外，<code>pragma</code>的优先级高于<code>Expires</code>。</p></li></ol><h4 id="本地缓存"><a href="#本地缓存" class="headerlink" title="本地缓存"></a>本地缓存</h4><p><strong>cookie</strong></p><p>cookie存在于浏览器端，是客户端保存用户信息的一种机制，用来记录用户的一些信息。是通过扩展HTTP协议来实现的，服务器通过在HTTP的响应头加上一行特殊的指示(<code>set-cookie</code>)以提示浏览器按照指示生成对应的cookie。主要用于以下三个方面。</p><ul><li>1、会话状态管理（保存用户登录状态等需要记录的信息）</li><li>2、个性化设置（用户自定义主题、设置等）</li><li>3、浏览器行为跟踪（跟踪分析用户行为）</li></ul><p>cookie的内容包括：名字、值、过期时间、路径和域。路径和域一起构成cookie的作用范围。如果不设置过期时间，则表示该cookie的生命周期为浏览器会话期间，关闭浏览器窗口，cookie就消失，一般保存在内存中（<code>会话cookie</code>），如果设置了过期时间，该cookie则会被保存到硬盘里(<code>硬盘cookie</code>)。</p><p><strong>sessionStorage</strong></p><p>客户端的存储，基于html5，本地存储。可以将数据在当前会话中保存下来。刷新页面数据依然存在，但是关闭页面后，sessionStorage中的数据就会被清空。</p><p><strong>localStorage</strong></p><p>是HTML5标准中新添加的技术。本地存储，存储在本地硬盘。除非手动清除，否则永久保存。</p><h3 id="HTTP-2"><a href="#HTTP-2" class="headerlink" title="HTTP/2"></a>HTTP/2</h3><p>简单来说，HTTP/2，是HTTP协议的第二个主要版本。HTTP/2是HTTP协议自1999年HTTP1.1发布后的首个更新，主要基于google的SPDY协议。 HTTP/2的特点/目的是：在不改动HTTP语义、方法、状态码、URI及首部字段的情况下，<strong>大幅度提高web性能</strong>。</p><h4 id="HTTP-1-存在的性能问题"><a href="#HTTP-1-存在的性能问题" class="headerlink" title="HTTP/1 存在的性能问题"></a>HTTP/1 存在的性能问题</h4><ul><li><p>TCP 连接数限制（6～8）</p><p>每一个TCP连接创建都需要经历DNS解析、三次握手、慢启动等过程，对于客户端而言，占用了额外的CPU和内存；对于服务器而言，过多连接容易造成网络拥堵</p></li><li><p>线头阻塞问题（Head of Line-blocking）</p><ul><li>每一个TCP连接一次只能处理一个请求-响应，浏览器按照先进先出原则，如果上一个请求迟迟没有响应，后续的请求-响应都会收到影响。</li><li>为了解决上述问题，出现了管线化（HTTP Pipelining）技术。HTTP Pipelining（管线化）是将多个 HTTP 请求整批提交的技术，在传送过程中不需等待服务端的回应。但如果第一个响应慢还是会阻塞后续响应，服务器为了按序返回相应需要缓存多个响应占用更多资源等等问题亦没有从根本上解决线头阻塞问题。</li></ul></li><li><p>Header 内容多，而且每次请求 Header 不会变化太多，没有相应的压缩传输优化方案</p></li><li><p>文件合并等优化工作带来单个请求的延迟</p></li><li><p>明文传输不安全</p></li></ul><p>总而言之，性能不高和安全不足，是HTTP/1的主要缺陷。</p><h4 id="HTTP-2-的改进"><a href="#HTTP-2-的改进" class="headerlink" title="HTTP/2 的改进"></a>HTTP/2 的改进</h4><ul><li><p>二进制传输</p><p>HTTP/2 采用二进制格式传输数据，而非HTTP/1.x 里纯文本形式的报文。<strong>HTTP/2 将请求和响应数据分割为更小的帧，并且它们采用二进制编码</strong>。它把TCP协议的部分特性挪到了应用层，把原来的”Header+Body”的消息”打散”为数个小片的二进制”帧”(Frame),用”HEADERS”帧存放头数据、”DATA”帧存放实体数据。HTTP/2 中，同域名下所有通信都在单个连接上完成，该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送，而消息又由一个或多个帧组成。<strong>多个帧之间可以乱序发送，根据帧首部的流标识可以重新组装</strong>。</p></li><li><p>Header 压缩</p><ul><li>客户端和服务端建立连接后，双方共同建立和维护一个首部表，用于存储和更新头部字段</li><li>第一次请求-响应发送完整的头部字段，存储到首部表，后续连接只需要发送差异字段，从而达到减少冗余数据，节省开销的目的</li><li>每个新的首部键-值对要么被追加到当前表的末尾，要么替换表中之前的值</li></ul></li><li><p>多路复用</p><ul><li>同一域名开启一个TCP连接（不再受限于TCP连接数量），每个HTTP请求以流的方式传输（解决线头阻塞）</li><li>二进制传输方式令客户端与服务端之间的数据以帧的形式乱序传输，借助帧上的标识符将同一流的数据解析成可用数据</li><li>客户端通过对象的依赖关系告诉服务器哪些资源应该优先传输，服务端根据依赖关系和权重进行带宽分配，优化传输</li></ul></li><li><p>服务端推送</p><blockquote><p>HTTP2还在一定程度上改变了传统的“请求-应答”工作模式，服务器不再是完全被动地响应请求，也可以新建“流”主动向客户端发送消息。比如，在浏览器刚请求HTML的时候就提前把可能会用到的JS、CSS文件发给客户端，减少等待的延迟，这被称为”服务器推送”。</p></blockquote><p>另外需要补充的是,服务端可以主动推送，客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过，浏览器可以通过发送RST_STREAM帧来拒收。主动推送也遵守同源策略，换句话说，服务器不能随便将第三方资源推送给客户端，而必须是经过双方确认才行。</p></li><li><p>提高安全性</p><p>HTTPS加密</p></li></ul><h3 id="Content-Type"><a href="#Content-Type" class="headerlink" title="Content-Type"></a>Content-Type</h3><blockquote><p><strong><code>Content-Type</code></strong> 实体头部用于指示资源的MIME类型 <a href="https://developer.mozilla.org/en-US/docs/Glossary/MIME_type" target="_blank" rel="noopener">media type</a> 。</p></blockquote><h4 id="案例-1-Form"><a href="#案例-1-Form" class="headerlink" title="案例#1 - Form"></a>案例#1 - Form</h4><p>在通过HTML form提交生成的<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/POST" target="_blank" rel="noopener"><code>POST</code></a>请求中，请求头的Content-Type由<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/form" target="_blank" rel="noopener">``</a>元素上的enctype属性指定，即<code>multipart/form-data</code></p><h4 id="案例-2-JSON"><a href="#案例-2-JSON" class="headerlink" title="案例#2 - JSON"></a>案例#2 - JSON</h4><p><code>application/json</code></p><h2 id="JavaScript"><a href="#JavaScript" class="headerlink" title="JavaScript"></a>JavaScript</h2><h3 id="JS的几条基本规范"><a href="#JS的几条基本规范" class="headerlink" title="JS的几条基本规范"></a>JS的几条基本规范</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="number">1</span>、不要在同一行声明多个变量</span><br><span class="line"><span class="number">2</span>、请使用===<span class="regexp">/！==来比较true/</span><span class="literal">false</span>或者数值</span><br><span class="line"><span class="number">3</span>、使用对象字面量替代<span class="keyword">new</span> <span class="built_in">Array</span>这种形式</span><br><span class="line"><span class="number">4</span>、不要使用全局变量</span><br><span class="line"><span class="number">5</span>、Switch语句必须带有<span class="keyword">default</span>分支</span><br><span class="line"><span class="number">6</span>、函数不应该有时候有返回值，有时候没有返回值</span><br><span class="line"><span class="number">7</span>、For循环必须使用大括号</span><br><span class="line"><span class="number">8</span>、IF语句必须使用大括号</span><br><span class="line"><span class="number">9</span>、<span class="keyword">for</span>-<span class="keyword">in</span>循环中的变量 应该使用<span class="keyword">var</span>关键字明确限定作用域，从而避免作用域污染</span><br></pre></td></tr></table></figure><h3 id="JS-引用方法"><a href="#JS-引用方法" class="headerlink" title="JS 引用方法"></a>JS 引用方法</h3><h4 id="行内引入"><a href="#行内引入" class="headerlink" title="行内引入"></a>行内引入</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">onclick</span>=<span class="string">"alert('行内引入')"</span> <span class="attr">value</span>=<span class="string">"按钮"</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"alert(123)"</span>&gt;</span>点击我<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="内部引入"><a href="#内部引入" class="headerlink" title="内部引入"></a>内部引入</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">  <span class="built_in">window</span>.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="actionscript">    alert(<span class="string">"js 内部引入！"</span>);</span></span><br><span class="line"><span class="undefined">  &#125;</span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="外部引入"><a href="#外部引入" class="headerlink" title="外部引入"></a>外部引入</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"./js/index.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></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></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>，不推荐写行内或者HTML中插入&lt;script&gt;,因为浏览器解析顺序缘故，如果解析到死循环之类的JS代码，会卡住页面</span><br><span class="line"><span class="number">2</span>，建议在onload事件之后，即等HTML、CSS渲染完毕再执行代码</span><br></pre></td></tr></table></figure><h3 id="JS-的基本数据类型"><a href="#JS-的基本数据类型" class="headerlink" title="JS 的基本数据类型"></a>JS 的基本数据类型</h3><p>Undefined、Null、Boolean、Number、String、新增:Symbol</p><h4 id="类型判断"><a href="#类型判断" class="headerlink" title="类型判断"></a>类型判断</h4><p><strong><code>typeof</code></strong></p><p>效果有限</p><p>最佳方式：<code>Object.prototype.toString.call(obj).slice(8, -1)</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="function"><span class="keyword">function</span> <span class="title">is</span>(<span class="params">type, obj</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> clas = <span class="built_in">Object</span>.prototype.toString.call(obj).slice(<span class="number">8</span>, <span class="number">-1</span>);</span><br><span class="line">    <span class="keyword">return</span> obj !== <span class="literal">undefined</span> &amp;&amp; obj !== <span class="literal">null</span> &amp;&amp; clas === type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>那么，<code>typeof</code>·有用的地方是什么？</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typeof</span> foo !== <span class="string">'undefined'</span></span><br></pre></td></tr></table></figure><p>上面代码会检测 <code>foo</code> 是否已经定义；如果没有定义而直接使用会导致 <code>ReferenceError</code> 的异常。 这是 <code>typeof</code> 唯一有用的地方。</p><h3 id="JS有哪些内置对象"><a href="#JS有哪些内置对象" class="headerlink" title="JS有哪些内置对象"></a>JS有哪些内置对象</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>是JavaScript中所有对象的父对象 </span><br><span class="line"></span><br><span class="line">数据封装对象：<span class="built_in">Object</span>、<span class="built_in">Array</span>、<span class="built_in">Boolean</span>、<span class="built_in">Number</span>和<span class="built_in">String</span> </span><br><span class="line">其他对象：<span class="built_in">Function</span>、Arguments、<span class="built_in">Math</span>、<span class="built_in">Date</span>、<span class="built_in">RegExp</span>、<span class="built_in">Error</span></span><br></pre></td></tr></table></figure><h3 id="Get请求传参长度的误区"><a href="#Get请求传参长度的误区" class="headerlink" title="Get请求传参长度的误区"></a>Get请求传参长度的误区</h3><p>对get请求参数的限制是来源与浏览器或web服务器，浏览器或web服务器限制了url的长度。为了明确这个概念，我们必须再次强调下面几点:</p><ul><li>HTTP 协议 未规定 GET 和POST的长度限制</li><li>GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度</li><li>不同的浏览器和WEB服务器，限制的最大长度不一样</li><li>要支持IE，则最大长度为2083byte，若只支持Chrome，则最大长度 8182byte</li></ul><h3 id="get和post请求在缓存方面的区别"><a href="#get和post请求在缓存方面的区别" class="headerlink" title="get和post请求在缓存方面的区别"></a>get和post请求在缓存方面的区别</h3><ul><li>get请求类似于查找的过程，用户获取数据，可以不用每次都与数据库连接，所以可以使用缓存。</li><li>post不同，post做的一般是修改和删除的工作，所以必须与数据库交互，所以不能使用缓存。因此get请求适合于请求缓存。</li></ul><h3 id="原型和原型链"><a href="#原型和原型链" class="headerlink" title="原型和原型链"></a>原型和原型链</h3><blockquote><p>每个对象都会在其内部初始化一个属性，就是prototype(原型)，当我们访问一个对象的属性时，如果这个对象内部不存在这个属性，那么他就会去prototype里找这个属性，这个prototype又会有自己的prototype，于是就这样一直找下去。</p></blockquote><p>其中，对象的原型和原型链是相同的，即</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">instance.constructor.prototype === instance.__proto__</span><br></pre></td></tr></table></figure><p>JavaScript对象是通过引用来传递的，我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时，与之相关的对象也会继承这一改变。</p><p>当我们需要一个属性的时，Javascript引擎会先看当前对象中是否有这个属性， 如果没有的话，就会查找他的Prototype对象是否有这个属性，如此递推下去，一直检索到 Object 内建对象。</p><h3 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h3><blockquote><p>所有的编程语言都可以存储，访问，修改变量。但是这些变量如何存储，程序如何找到并且能够使用它们？这些问题需要设计一套规则，这套规则就被称为我们所熟知的<code>作用域</code>。</p><p>简单来说，<code>作用域</code>指的是变量的生命周期的所在范围。</p></blockquote><h4 id="JavaScript-作用域"><a href="#JavaScript-作用域" class="headerlink" title="JavaScript 作用域"></a>JavaScript 作用域</h4><ul><li>全局作用域</li><li>词法作用域</li><li>函数作用域</li><li>块级作用域</li><li>动态作用域</li></ul><hr><p><strong>全局作用域</strong></p><p>全局变量的生命周期在整个程序之内，能被任意函数或方法访问，并默认可修改；整个程序就是全局变量的作用域。在浏览器中，全局作用域为<code>window</code></p><p><strong>词法作用域</strong></p><blockquote><p>词法作用域是指一个变量的可见性，及其文本表述的模拟值</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> testValue = <span class="string">'outer'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(testValue);<span class="comment">// "outer"</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"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> testValue = <span class="string">'inner'</span>;</span><br><span class="line">  </span><br><span class="line">  foo();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">bar();                         <span class="comment">// "outer"</span></span><br></pre></td></tr></table></figure><p><strong>函数作用域</strong></p><blockquote><p>每声明一个函数都会为其自身创建一个作用域，外部函数或方法无法访问或修改函数内部变量。</p></blockquote><p>如何访问函数内部变量？</p><ul><li><code>return</code></li><li>闭包</li><li>立即执行函数</li></ul><p><strong>块级作用域</strong></p><p>ES6之前，JavaScript 语言中并不存在块级作用域。在C++等语言中，花括号<code>{}</code>能够创建一个块级作用域，在JavaScript中，通常情况下，即使用<code>var</code>声明变量，并不能在循环语句或条件语句中创建块级作用域；而使用<code>let</code>和<code>const</code>即可创建一个块级作用域。</p><p><strong>动态作用域</strong></p><blockquote><p>动态作用域，作用域是基于调用栈的，而不是代码中的作用域嵌套；</p><p>作用域嵌套，有词法作用域一样的特性，查找变量时，总是寻找最近的作用域；</p></blockquote><p>JavaScript 中仅存的动态作用域，在于<code>this</code>  的引用</p><p><strong>举例</strong></p><p>如何处理<code>for</code>循环语句中的<code>setTimeout</code>函数？</p><p>思路：创建作用域</p><ul><li>使用<code>let</code>或<code>const</code>创建块级作用域</li><li>使用外部函数或者立即执行函数创建函数作用域</li></ul><h3 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h3><h4 id="原型链继承"><a href="#原型链继承" class="headerlink" title="原型链继承"></a>原型链继承</h4><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">Foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.value = <span class="number">42</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Foo.prototype = &#123;</span><br><span class="line">    method: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Bar</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">Bar.prototype = <span class="keyword">new</span> Foo();</span><br><span class="line">Bar.prototype.foo = <span class="string">'Hello World'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修正Bar.prototype.constructor为Bar本身</span></span><br><span class="line">Bar.prototype.constructor = Bar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> test = <span class="keyword">new</span> Bar() <span class="comment">// 创建Bar的一个新实例</span></span><br><span class="line"><span class="comment">// 原型链</span></span><br><span class="line">test [Bar的实例]</span><br><span class="line">    Bar.prototype [Foo的实例] </span><br><span class="line">        &#123; <span class="attr">foo</span>: <span class="string">'Hello World'</span> &#125;</span><br><span class="line">        Foo.prototype</span><br><span class="line">            &#123;<span class="attr">method</span>: ...&#125;;</span><br><span class="line">            <span class="built_in">Object</span>.prototype</span><br><span class="line">                &#123;<span class="attr">toString</span>: ... <span class="comment">/* etc. */</span>&#125;;</span><br></pre></td></tr></table></figure><h4 id="构造函数继承"><a href="#构造函数继承" class="headerlink" title="构造函数继承"></a>构造函数继承</h4> <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">Product</span>(<span class="params">name, price</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">    <span class="keyword">this</span>.price = price;</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">Food</span>(<span class="params">name, price</span>) </span>&#123;</span><br><span class="line">    Product.call(<span class="keyword">this</span>, name, price);</span><br><span class="line">    <span class="keyword">this</span>.category = <span class="string">'food'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cheese = <span class="keyword">new</span> Food(<span class="string">'feta'</span>, <span class="number">5</span>);</span><br><span class="line"><span class="built_in">console</span>.log(cheese)</span><br><span class="line"><span class="comment">// Food &#123; name: 'feta', price: 5, category: 'food' &#125;</span></span><br></pre></td></tr></table></figure><p> 缺陷：<code>构造函数式继承</code>并没有继承父类原型上的方法。</p><h4 id="组合式继承"><a href="#组合式继承" class="headerlink" title="组合式继承"></a>组合式继承</h4><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">Product</span>(<span class="params">name, price</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">    <span class="keyword">this</span>.price = price;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Product.prototype.sale = <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">"商品："</span> + <span class="keyword">this</span>.name + <span class="string">" 价格："</span> + <span class="keyword">this</span>.price);</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">Food</span>(<span class="params">name, price</span>) </span>&#123;</span><br><span class="line">    Product.call(<span class="keyword">this</span>, name, price);</span><br><span class="line">    <span class="keyword">this</span>.category = <span class="string">'food'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Food.prototype = <span class="keyword">new</span> Product();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cheese = <span class="keyword">new</span> Food(<span class="string">'feta'</span>, <span class="number">5</span>);</span><br><span class="line"><span class="built_in">console</span>.log(cheese)</span><br></pre></td></tr></table></figure><h4 id="extends"><a href="#extends" class="headerlink" title="extends"></a>extends</h4><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="class"><span class="keyword">class</span> <span class="title">ColorPoint</span> <span class="keyword">extends</span> <span class="title">Point</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(x, y, color) &#123;</span><br><span class="line">    <span class="keyword">super</span>(x, y); <span class="comment">// 调用父类的constructor(x, y)</span></span><br><span class="line">    <span class="keyword">this</span>.color = color;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  toString() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.color + <span class="string">' '</span> + <span class="keyword">super</span>.toString(); <span class="comment">// 调用父类的toString()</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><blockquote><p>闭包是 JavaScript 一个非常重要的特性，这意味着当前作用域<strong>总是</strong>能够访问外部作用域中的变量。 因为 <a href="https://bonsaiden.github.io/JavaScript-Garden/zh/#function.scopes" target="_blank" rel="noopener">函数</a> 是 JavaScript 中唯一拥有自身作用域的结构，因此闭包的创建依赖于函数。</p></blockquote><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">Counter</span>(<span class="params">start</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> count = start;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        increment: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">            count++;</span><br><span class="line">        &#125;,</span><br><span class="line"></span><br><span class="line">        <span class="keyword">get</span>: function() &#123;</span><br><span class="line">            <span class="keyword">return</span> count;</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">var</span> foo = Counter(<span class="number">4</span>);</span><br><span class="line">foo.increment();</span><br><span class="line">foo.get(); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure><p><strong>闭包的特征</strong></p><ul><li>函数内再嵌套函数</li><li>内部函数可以引用外层的参数和变量</li><li>参数和变量不会被垃圾回收制回收</li></ul><p><strong>闭包的好处</strong></p><p>能够实现封装和缓存等</p><p><strong>闭包的坏处</strong></p><p>就是消耗内存、不正当使用会造成内存溢出的问题</p><p><strong>作用域链</strong></p><p>一般情况下，变量取值到 创建 这个变量 的函数的作用域中取值。</p><p>但是如果在当前作用域中没有查到值，就会向上级作用域去查，直到查到全局作用域，这么一个查找过程形成的链条就叫做作用域链</p><h3 id="组件化和模块化"><a href="#组件化和模块化" class="headerlink" title="组件化和模块化"></a>组件化和模块化</h3><h4 id="组件化"><a href="#组件化" class="headerlink" title="组件化"></a>组件化</h4><p><strong>为什么要组件化开发</strong></p><p>有时候页面代码量太大，逻辑太多或者同一个功能组件在许多页面均有使用，维护起来相当复杂，这个时候，就需要组件化开发来进行功能拆分、组件封装，已达到组件通用性，增强代码可读性，维护成本也能大大降低</p><p><strong>组件化开发的优点</strong></p><p>很大程度上降低系统各个功能的耦合性，并且提高了功能内部的聚合性。这对前端工程化及降低代码的维护来说，是有很大的好处的，耦合性的降低，提高了系统的伸展性，降低了开发的复杂度，提升开发效率，降低开发成本</p><p><strong>组件化开发的原则</strong></p><ul><li>专一</li><li>可配置性</li><li>标准性</li><li>复用性</li><li>可维护性</li></ul><h4 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h4><p><strong>为什么要模块化</strong></p><p>早期的javascript版本没有块级作用域、没有类、没有包、也没有模块，这样会带来一些问题，如复用、依赖、冲突、代码组织混乱等，随着前端的膨胀，模块化显得非常迫切</p><p><strong>模块化的好处</strong></p><ul><li>避免变量污染，命名冲突</li><li>提高代码复用率</li><li>提高了可维护性</li><li>方便依赖关系管理</li></ul><p><strong>模块化的几种方法</strong></p><ul><li><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="keyword">var</span> myModule = &#123;</span><br><span class="line">    var1: <span class="number">1</span>,</span><br><span class="line">    </span><br><span class="line">    var2: <span class="number">2</span>,</span><br><span class="line">    </span><br><span class="line">    fn1: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    </span><br><span class="line">    &#125;,</span><br><span class="line">    </span><br><span class="line">    fn2: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>立即执行函数表达式(IIFE)</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">var</span> myModule = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> var1 = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">var</span> var2 = <span class="number">2</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">fn1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    </span><br><span class="line">    &#125; </span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">fn2</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line">    fn1: fn1,</span><br><span class="line">    fn2: fn2</span><br><span class="line">&#125;;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></li></ul><h4 id="CommonJS和ES6模块循环加载处理的区别"><a href="#CommonJS和ES6模块循环加载处理的区别" class="headerlink" title="CommonJS和ES6模块循环加载处理的区别"></a>CommonJS和ES6模块循环加载处理的区别</h4><p>CommonJS模块规范使用require语句导入模块，module.exports导出模块，输出的是值的拷贝，模块导入的也是输出值的拷贝，也就是说，一旦输出这个值，这个值在模块内部的变化是监听不到的。</p><p>ES6模块的规范是使用import语句导入模块，export语句导出模块，输出的是对值的引用。ES6模块的运行机制和CommonJS不一样，遇到模块加载命令import时不去执行这个模块，只会生成一个动态的只读引用，等真的需要用到这个值时，再到模块中取值，也就是说原始值变了，那输入值也会发生变化。</p><h3 id="图片的预加载和懒加载"><a href="#图片的预加载和懒加载" class="headerlink" title="图片的预加载和懒加载"></a>图片的预加载和懒加载</h3><ul><li>预加载：提前加载图片，当用户需要查看时可直接从本地缓存中渲染</li><li>懒加载：懒加载的主要目的是作为服务器前端的优化，减少请求数或延迟请求数</li></ul><p>两种技术的本质：两者的行为是相反的，一个是提前加载，一个是迟缓甚至不加载。预加载则会增加服务器前端压力，懒加载对服务器有一定的缓解压力作用。</p><h3 id="mouseover和mouseenter的区别"><a href="#mouseover和mouseenter的区别" class="headerlink" title="mouseover和mouseenter的区别"></a>mouseover和mouseenter的区别</h3><p>mouseover：当鼠标移入元素或其子元素都会触发事件，所以有一个重复触发，冒泡的过程。对应的移除事件是mouseout</p><p>mouseenter：当鼠标移入元素本身（不包含元素的子元素）会触发事件，也就是不会冒泡，对应的移除事件是mouseleave</p><h3 id="堆和栈"><a href="#堆和栈" class="headerlink" title="堆和栈"></a>堆和栈</h3><p>堆和栈都是内存中划分出来用来存储的区域。</p><blockquote><p>栈（stack）为自动分配的内存空间，它由系统自动释放；而堆（heap）则是动态分配的内存，大小不定也不会自动释放。</p></blockquote><p>由此，<strong>深拷贝</strong>和<strong>浅拷贝</strong>的主要区别在于其在内存中的存储类型不同。</p><p>对于JavaScript语言，其基本类型的数据存储在栈内存中，不可改变，而引用类型的数据存储在堆内存中，可以改变。前者的赋值为传值，即开辟新的内存，并存储数值，后者为传址，即将指针指向堆内存的地址。</p><h4 id="浅拷贝和深拷贝"><a href="#浅拷贝和深拷贝" class="headerlink" title="浅拷贝和深拷贝"></a>浅拷贝和深拷贝</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 浅拷贝</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> origin = &#123;</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"zhangsan"</span>,</span><br><span class="line">    <span class="string">"age"</span>: <span class="number">18</span>,</span><br><span class="line">    <span class="string">"language"</span>: [<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><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> shallowCopy = <span class="function">(<span class="params">src</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> dst = &#123;&#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> prop <span class="keyword">in</span> src) &#123;</span><br><span class="line">        <span class="keyword">if</span> (src.hasOwnProperty(prop)) &#123;</span><br><span class="line">            dst[prop] = src[prop];</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> dst;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> destiny = shallowCopy(origin);</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment">/**</span></span><br><span class="line"><span class="comment"> * Deep Copy of Objects</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> copy = <span class="function">(<span class="params">source, target</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> source !== <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">"Error arguments: The type of source should be object."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    target = source.constructor == <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> source) &#123;</span><br><span class="line">        <span class="keyword">if</span> (source.hasOwnProperty(key)) &#123;</span><br><span class="line">            <span class="keyword">if</span>(source[keys] &amp;&amp; <span class="keyword">typeof</span> source[keys] === <span class="string">'object'</span>) &#123;</span><br><span class="line">           targetObj[keys] = source[keys].constructor === <span class="built_in">Array</span> ? [] : &#123;&#125;;</span><br><span class="line">           targetObj[keys] = deepClone(source[keys]);</span><br><span class="line">         &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           targetObj[keys] = source[keys];</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><h3 id="事件机制"><a href="#事件机制" class="headerlink" title="事件机制"></a>事件机制</h3><p>DOM事件流（event flow ）存在三个阶段（事件传播）：</p><ul><li>事件捕获阶段</li><li>处于目标阶段</li><li>事件冒泡阶段</li></ul><hr><h4 id="事件捕获"><a href="#事件捕获" class="headerlink" title="事件捕获"></a>事件捕获</h4><blockquote><p>当鼠标点击或者触发dom事件时，浏览器会从根节点开始<strong>由外到内</strong>进行事件传播，即点击了子元素，如果父元素通过事件捕获方式注册了对应的事件的话，会先触发父元素绑定的事件。</p></blockquote><h4 id="事件冒泡"><a href="#事件冒泡" class="headerlink" title="事件冒泡"></a>事件冒泡</h4><blockquote><p>与事件捕获恰恰相反，事件冒泡顺序是由内到外进行事件传播，直到根节点。</p></blockquote><p>DOM 标准事件流的触发的先后顺序为：<strong>先捕获再冒泡</strong>，即当触发DOM事件时，会先进行事件捕获，捕获到事件源之后通过事件传播进行事件冒泡。</p><p>因此，我们需要了解事件绑定和事件监听，对于前者，可以使用 <code>onclick</code>、<code>on change</code>等属性进行事件绑定，后者即<code>addEventListener</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="comment">/**</span></span><br><span class="line"><span class="comment"> * @param string event</span></span><br><span class="line"><span class="comment"> * @param object listener</span></span><br><span class="line"><span class="comment"> * @param boolean useCapture = false --default</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">addEventListener(event, listener, useCapture)</span><br></pre></td></tr></table></figure><p>参数<code>useCapture</code>的取值决定了事件监听器以何种方式（事件捕捉 vs. 事件冒泡）触发，那么如果同时为父元素和子元素注册了事件监听器，在某些时候，只希望触发子元素绑定的事件监听器，则可以使用<code>event.stopPropagation()</code>停止事件传播。</p><h4 id="事件委托"><a href="#事件委托" class="headerlink" title="事件委托"></a>事件委托</h4><blockquote><p>事件委托，通俗地来讲，就是把一个元素响应事件（click、keydown……）的函数委托到另一个元素；</p></blockquote><p>举个例子：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ul</span> <span class="attr">id</span>=<span class="string">"list"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>item 1<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>item 2<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>item 3<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">  ......</span><br><span class="line">  <span class="tag">&lt;<span class="name">li</span>&gt;</span>item n<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br></pre></td></tr></table></figure><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="built_in">document</span>.getElementById(<span class="string">'list'</span>).addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 兼容性处理</span></span><br><span class="line">  <span class="keyword">var</span> event = e || <span class="built_in">window</span>.event;</span><br><span class="line">  <span class="keyword">var</span> target = event.target || event.srcElement;</span><br><span class="line">  <span class="comment">// 判断是否匹配目标元素</span></span><br><span class="line">  <span class="keyword">if</span> (target.nodeName.toLocaleLowerCase === <span class="string">'li'</span>) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'the content is: '</span>, target.innerHTML);</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><h4 id="浏览器下的事件循环"><a href="#浏览器下的事件循环" class="headerlink" title="浏览器下的事件循环"></a>浏览器下的事件循环</h4><p>事件循环中的事件与上文中的事件机制所涉及的事件是有区别的。首先，我们要了解，JavaScript是如何执行函数的：</p><blockquote><p>当一个脚本第一次执行的时候，js引擎会解析这段代码，并将其中的同步代码按照执行顺序加入执行栈中，然后从头开始执行。如果当前执行的是一个方法，那么js会向执行栈中添加这个方法的执行环境，然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后，js会退出这个执行环境并把这个执行环境销毁，回到上一个方法的执行环境。。这个过程反复进行，直到执行栈中的代码全部执行完毕。</p></blockquote><p>那么，假如JavaScript需要处理一个异步任务，其并不会等待异步任务返回结果，而是先将事件挂起，继续处理执行栈中的其他任务。当异步任务返回结果之后，JavaScript亦不会立刻处理其回调，而是将其加入事件队列，等待执行栈中的所有任务完成，再去处理事件队列中的任务回调，即将其加入执行栈。由此循环往复，构成<strong>事件循环</strong>。</p><h4 id="宏任务-amp-微任务"><a href="#宏任务-amp-微任务" class="headerlink" title="宏任务 &amp; 微任务"></a>宏任务 &amp; 微任务</h4><p>以下属于宏任务：</p><ul><li><code>setInterval</code></li><li><code>setTimeout</code></li></ul><p>以下属于微任务</p><ul><li><code>new Promise()</code></li><li><code>new MutationObserver()</code></li></ul><p>当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件，然后再去宏任务队列中取出一个事件。同一次事件循环中，微任务永远在宏任务之前执行。</p><h4 id="Node-环境下的事件循环"><a href="#Node-环境下的事件循环" class="headerlink" title="Node 环境下的事件循环"></a>Node 环境下的事件循环</h4><blockquote><p>外部输入数据—&gt;轮询阶段(poll)—&gt;检查阶段(check)—&gt;关闭事件回调阶段(close callback)—&gt;定时器检测阶段(timer)—&gt;I/O事件回调阶段(I/O callbacks)—&gt;闲置阶段(idle, prepare)—&gt;轮询阶段……</p></blockquote><p><strong>轮询阶段</strong></p><p>检查poll queue中是否有事件</p><ul><li>有，按先进先出的顺序依次执行回调</li><li>无，检查是否存在setImmediate()的回调；检查是否存在到期的timer事件</li></ul><p>上述二者顺序不固定，若同时为空，则暂时停止，知道接收I/O事件并进入I/O事件回调阶段</p><p><strong>检查阶段</strong></p><p>执行setImmediate()的回调</p><p><strong>关闭事件回调</strong></p><p>当一个socket连接或者一个handle被突然关闭时（例如调用了<code>socket.destroy()</code>方法），close事件会被发送到这个阶段执行回调。否则事件会用<code>process.nextTick（）</code>方法发送出去。</p><p><strong>定时器检测阶段</strong></p><p>这个阶段以先进先出的方式执行所有到期的timer加入timer队列里的callback，一个timer callback指得是一个通过setTimeout或者setInterval函数设置的回调函数。</p><p><strong>I/O 回调阶段</strong></p><p>这个阶段主要执行大部分I/O事件的回调，包括一些为操作系统执行的回调。例如一个TCP连接生错误时，系统需要执行回调来获得这个错误的报告。</p><h4 id="process-nextTick-setTimeout与setImmediate的区别与使用场景"><a href="#process-nextTick-setTimeout与setImmediate的区别与使用场景" class="headerlink" title="process.nextTick,setTimeout与setImmediate的区别与使用场景"></a>process.nextTick,setTimeout与setImmediate的区别与使用场景</h4><p>在node中有三个常用的用来推迟任务执行的方法：<code>process.nextTick</code>,<code>setTimeout</code>（<code>setInterval</code>与之相同）与<code>setImmediate</code></p><p><code>setTimeout()</code>方法是定义一个回调，并且希望这个回调在我们所指定的时间间隔后第一时间去执行。注意这个“第一时间执行”，这意味着，受到操作系统和当前执行任务的诸多影响，该回调并不会在我们预期的时间间隔后精准的执行。执行的时间存在一定的延迟和误差，这是不可避免的。node会在可以执行timer回调的第一时间去执行你所设定的任务。</p><p><code>setImmediate()</code>方法从意义上讲是立刻执行的意思，但是实际上它却是在一个固定的阶段才会执行回调，即poll阶段之后。有趣的是，这个名字的意义和之前提到过的<code>process.nextTick()</code>方法才是最匹配的。node的开发者们也清楚这两个方法的命名上存在一定的混淆，他们表示不会把这两个方法的名字调换过来—-因为有大量的node程序使用着这两个方法，调换命名所带来的好处与它的影响相比不值一提。</p><h3 id="let、const和var的区别"><a href="#let、const和var的区别" class="headerlink" title="let、const和var的区别"></a><code>let</code>、<code>const</code>和<code>var</code>的区别</h3><div class="table-container"><table><thead><tr><th>区别</th><th style="text-align:center"><code>var</code></th><th style="text-align:center"><code>let</code></th><th style="text-align:center"><code>const</code></th></tr></thead><tbody><tr><td>是否可以重复声明</td><td style="text-align:center">:white_check_mark:</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:negative_squared_cross_mark:</td></tr><tr><td>是否受限于块级</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:white_check_mark:</td><td style="text-align:center">:white_check_mark:</td></tr><tr><td>是否与<code>window</code> 相映射</td><td style="text-align:center">:white_check_mark:</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:negative_squared_cross_mark:</td></tr><tr><td>是否存在暂存死区（先声明，再访问）</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:white_check_mark:</td><td style="text-align:center">:white_check_mark:</td></tr><tr><td>声明之后是否必须赋值</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:white_check_mark:</td></tr><tr><td>是否不可变</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:negative_squared_cross_mark:</td><td style="text-align:center">:white_check_mark:</td></tr></tbody></table></div><h3 id="Array-prototype-map-Array-prototype-filter-Array-prototype-reduce"><a href="#Array-prototype-map-Array-prototype-filter-Array-prototype-reduce" class="headerlink" title="Array.prototype.map / Array.prototype.filter / Array.prototype.reduce"></a>Array.prototype.map / Array.prototype.filter / Array.prototype.reduce</h3><h4 id="Array-prototype-map"><a href="#Array-prototype-map" class="headerlink" title="Array.prototype.map"></a>Array.prototype.map</h4><blockquote><p>根据传递的转换函数，更新给定数组中的每个值，并返回一个相同长度的新数组。它接受一个回调函数作为参数，用以执行转换过程。</p></blockquote><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="number">1</span>,<span class="number">4</span>,<span class="number">6</span>,<span class="number">14</span>,<span class="number">32</span>,<span class="number">78</span>].map(<span class="function"><span class="params">val</span> =&gt;</span>val *<span class="number">10</span>)</span><br><span class="line"><span class="comment">// Generate a new array</span></span><br><span class="line"><span class="comment">// the result is: [10, 40, 60, 140, 320, 780]</span></span><br></pre></td></tr></table></figure><h4 id="Array-prototype-filter"><a href="#Array-prototype-filter" class="headerlink" title="Array.prototype.filter"></a>Array.prototype.filter</h4><blockquote><p>当我们想要过滤数组的值到另一个数组，新数组中的每个值都通过一个特定检查。</p></blockquote><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="number">1</span>,<span class="number">4</span>,<span class="number">6</span>,<span class="number">14</span>,<span class="number">32</span>,<span class="number">78</span>].filter(<span class="function"><span class="params">val</span> =&gt;</span>val &gt;<span class="number">10</span>)</span><br><span class="line"><span class="comment">// the result is: [14, 32, 78]</span></span><br></pre></td></tr></table></figure><h4 id="Array-prototype-reduce"><a href="#Array-prototype-reduce" class="headerlink" title="Array.prototype.reduce"></a>Array.prototype.reduce</h4><blockquote><p>接受一个数组作为输入值并返回一个值。reduce() 接受一个回调函数，回调函数参数包括一个累计器（数组每一段的累加值，它会像雪球一样增长），当前值，和索引。reduce() 也接受一个初始值作为第二个参数。</p></blockquote><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">let</span> finalVal = oldArray.reduce((accumulator,currentValue,currentIndx</span><br><span class="line">,array)=&gt;&#123;</span><br><span class="line">...</span><br><span class="line">&#125;,initalValue);</span><br><span class="line">    </span><br><span class="line"><span class="keyword">const</span> ingredients = [<span class="string">'wine'</span>,<span class="string">'tomato'</span>,<span class="string">'onion'</span>,<span class="string">'mushroom'</span>]</span><br><span class="line"><span class="comment">// a cooking function</span></span><br><span class="line"><span class="keyword">const</span> cook = <span class="function">(<span class="params">ingredient</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">`cooked <span class="subst">$&#123;ingredient&#125;</span>`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> wineReduction = ingredients.reduce(<span class="function">(<span class="params">sauce, item</span>)=&gt;</span>&#123;</span><br><span class="line"> <span class="keyword">return</span>  sauce += cook(item) +<span class="string">', '</span></span><br><span class="line">&#125;,<span class="string">''</span>)</span><br><span class="line"><span class="comment">// wineReduction = "cooked wine, cooked tomato, cooked onion, cooked mushroom, "</span></span><br></pre></td></tr></table></figure><p><strong>原生JS的实现</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prototype.reduce = <span class="function"><span class="keyword">function</span> (<span class="params">reducer,initVal</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">0</span>;i&lt;<span class="keyword">this</span>.length;i++)&#123;</span><br><span class="line">        initVal =reducer(initVal,<span class="keyword">this</span>[i],i,<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> initVal;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="严格模式"><a href="#严格模式" class="headerlink" title="严格模式"></a>严格模式</h3><blockquote><p><strong>严格模式</strong>是采用具有限制性JavaScript变体的一种方式，从而使代码显式地脱离“马虎模式/稀松模式/懒散模式“（sloppy）模式，同时，其改变了语法及其运行行为。</p></blockquote><h4 id="特征"><a href="#特征" class="headerlink" title="特征"></a>特征</h4><ul><li>严格模式通过<strong>抛出错误</strong>来消除了一些原有<strong>静默错误</strong>。</li><li>严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷：有时候，相同的代码，严格模式可以比非严格模式下<strong>运行得更快</strong>。</li><li>严格模式<strong>禁用了</strong>在ECMAScript的未来版本中可能会定义的一些语法。</li></ul><hr><h4 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h4><ul><li><p>整个脚本</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 整个脚本开启严格模式</span></span><br><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"><span class="comment">/* Code ... */</span></span><br></pre></td></tr></table></figure><p>推荐指数：:star: </p><p>为了减少因合并严格模式代码和非严格模式代码而产生的问题，不建议为整个脚本开启严格模式。</p></li><li><p>个别函数</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 个别函数开启严格模式</span></span><br><span class="line"><span class="keyword">const</span> strict = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line"><span class="meta">    'use strict'</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Hi! I am a strict mode function'</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>推荐指数：:star::star::star::star::star:</p></li></ul><hr><h4 id="严格模式的变化"><a href="#严格模式的变化" class="headerlink" title="严格模式的变化"></a>严格模式的变化</h4><ol><li><p>将过失错误转化为异常</p><ul><li><p>变量创建需先声明，无法再意外创建全局变量 =&gt; <code>Reference Error</code></p></li><li><p>会使引起静默失败的赋值操作抛出异常</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</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> obj1 = &#123;&#125;;</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(obj1, <span class="string">"x"</span>, &#123; <span class="attr">value</span>: <span class="number">42</span>, <span class="attr">writable</span>: <span class="literal">false</span> &#125;);</span><br><span class="line">obj1.x = <span class="number">9</span>; <span class="comment">// 抛出TypeError错误</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> obj2 = &#123; <span class="keyword">get</span> x() &#123; <span class="keyword">return</span> <span class="number">17</span>; &#125; &#125;;</span><br><span class="line">obj2.x = <span class="number">5</span>; <span class="comment">// 抛出TypeError错误</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> fixed = &#123;&#125;;</span><br><span class="line"><span class="built_in">Object</span>.preventExtensions(fixed);</span><br><span class="line">fixed.newProp = <span class="string">"ohai"</span>; <span class="comment">// 抛出TypeError错误</span></span><br></pre></td></tr></table></figure></li><li><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="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">delete</span> <span class="built_in">Object</span>.prototype; <span class="comment">// 抛出TypeError错误</span></span><br></pre></td></tr></table></figure></li><li><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">sum</span>(<span class="params">a, a, c</span>) </span>&#123; <span class="comment">// !!! 语法错误</span></span><br><span class="line"><span class="meta">  "use strict"</span>;</span><br><span class="line">  <span class="keyword">return</span> a + a + c; <span class="comment">// 代码运行到这里会出错</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><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="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> sum = <span class="number">015</span> + <span class="comment">// !!! 语法错误</span></span><br><span class="line">          <span class="number">197</span> +</span><br><span class="line">          <span class="number">142</span>;</span><br></pre></td></tr></table></figure></li><li><p>禁止设置<a href="https://developer.mozilla.org/en-US/docs/Glossary/primitive" target="_blank" rel="noopener">primitive</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></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="meta">  "use strict"</span>;</span><br><span class="line"></span><br><span class="line">  <span class="literal">false</span>.true = <span class="string">""</span>;              <span class="comment">//TypeError</span></span><br><span class="line">  (<span class="number">14</span>).sailing = <span class="string">"home"</span>;        <span class="comment">//TypeError</span></span><br><span class="line">  <span class="string">"with"</span>.you = <span class="string">"far away"</span>;      <span class="comment">//TypeError</span></span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></li></ul></li><li><p>简化变量的使用</p><ul><li>严格模式禁用 <code>with</code></li><li>严格模式下的 eval 不再为上层范围引入新变量</li><li>禁止删除声明变量，<code>delete name</code> 在严格模式下会引起语法错误</li></ul></li><li><p>令<code>eval</code>和<code>arguments</code>变得更简单</p><ul><li>名称 <code>eval</code> 和 <code>arguments</code> 不能通过程序语法被绑定(be bound)或赋值</li><li>参数的值不会随 arguments 对象的值的改变而变化</li><li>不再支持 <code>arguments.callee</code> 和 <code>arguments.callee.caller</code></li></ul></li><li><p>安全性</p><ul><li>严格模式禁止了不在脚本或者函数层面上的函数声明</li><li>在严格模式中一部分字符变成了保留的关键字</li></ul></li></ol><h3 id="运算符new的机制"><a href="#运算符new的机制" class="headerlink" title="运算符new的机制"></a>运算符<code>new</code>的机制</h3><p>运算符<code>new</code>用于创建对象实例，包括：</p><ul><li><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Car</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(make, model, year) &#123;</span><br><span class="line">        <span class="keyword">this</span>.make = make;</span><br><span class="line">        <span class="keyword">this</span>.model = model;</span><br><span class="line">        <span class="keyword">this</span>.year = year;</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> car = <span class="keyword">new</span> Car(<span class="string">'Eagle'</span>, <span class="string">'Talon Tsi'</span>, <span class="number">1993</span>);</span><br></pre></td></tr></table></figure><p>:warning: 注意：箭头函数不能用作构造器，和 <code>new</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">var</span> Foo = <span class="function"><span class="params">()</span> =&gt;</span> &#123;&#125;;</span><br><span class="line"><span class="keyword">var</span> foo = <span class="keyword">new</span> Foo(); <span class="comment">// TypeError: Foo is not a constructor</span></span><br></pre></td></tr></table></figure></li><li><p>具有构造函数的内置对象，比如内置对象 <code>Array</code> 、<code>Map</code> 、<code>Date</code>、 <code>Object</code> 、 <code>Number</code> 、<code>Set</code>·、<code>String</code> 、<code>RegExp</code> 等</p></li></ul><hr><p><strong>问：运算符<code>new</code> 创建对象实例<code>car</code>时发生了什么？</strong></p><p>STEP 0: 创建一个空对象，用<code>__proto__</code>属性连接<code>Car</code>对象的原型</p><p>STEP 1: 一个继承自<code>Car.prototype</code>的新对象被创建</p><p>STEP 2: 使用指定的参数调用构造函数 <em><code>Car</code></em>，并将 <code>this</code> 绑定到新创建的对象</p><p>STEP 3: 由构造函数返回的对象就是 <code>new</code> 表达式的结果</p><h3 id="异步"><a href="#异步" class="headerlink" title="异步"></a>异步</h3><blockquote><p>异步编程就是在执行一个指令之后不是马上得到结果，而是继续执行后面的指令，等到特定的事件触发后，才得到结果。</p></blockquote><h4 id="异步实现"><a href="#异步实现" class="headerlink" title="异步实现"></a>异步实现</h4><ul><li>回调</li><li>Promise</li><li>Generator</li><li>await/async</li></ul><hr><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></pre></td><td class="code"><pre><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Hello"</span>);</span><br><span class="line">&#125;, <span class="number">1000</span>);</span><br></pre></td></tr></table></figure><p><strong>Promise</strong></p><p>参见<a href="#Promise">下文</a></p><p><strong>Generator</strong></p><p>在 ES 2015 中，出现了 Generator 的语法，熟悉 <code>Python</code> 的同学肯定对这种语法有点了解。</p><p>简单来说，Generator 可以理解为一个可以遍历的状态机，调用 <code>next</code> 就可以切换到下一个状态。</p><p>在 JavaScript 中，Generator 的 function 与 函数名之间有一个 *， 函数内部使用 yield 关键词，定义不同的状态。</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="function"><span class="keyword">function</span> <span class="title">a</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">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      resolve(<span class="number">1</span>)</span><br><span class="line">    &#125;, <span class="number">2000</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> b = co(<span class="function"><span class="keyword">function</span> *(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> val = <span class="keyword">yield</span> a();</span><br><span class="line">    <span class="built_in">console</span>.log(val)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">b()</span><br></pre></td></tr></table></figure><p><strong>await/async</strong></p><p>这是在 ES 2016 中引入的新关键词，这将在语言层面彻底解决 JavaScript 的异步回调问题，目前可以借助 babel 在生产环境中使用。使用 await/async 可以让异步的操作以同步的方式来写。</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="function"><span class="keyword">function</span> <span class="title">a</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">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      resolve(<span class="number">1</span>)</span><br><span class="line">    &#125;, <span class="number">2000</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> b = <span class="keyword">async</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> val = <span class="keyword">await</span> a()</span><br><span class="line">  <span class="built_in">console</span>.log(val)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">b()</span><br></pre></td></tr></table></figure><p><strong>async/await</strong> 实现原理</p><blockquote><p><code>async</code>的本质是Generator的自动执行</p></blockquote><p>首先看<code>Generator</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="keyword">var</span> x = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> *<span class="title">foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    x += <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">yield</span> <span class="string">"Hello"</span>;</span><br><span class="line">    x += <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">console</span>.log(x);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> it = foo();</span><br><span class="line">it.next()</span><br><span class="line">it.next()</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">readFile</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</span>=&gt;</span>&#123;</span><br><span class="line">          setTimeout(<span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">              <span class="built_in">console</span>.log(a);</span><br><span class="line">              resolve(a);</span><br><span class="line">          &#125;,<span class="number">500</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">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'a'</span>)；</span><br><span class="line">  <span class="keyword">var</span> result = <span class="keyword">yield</span> readFile(<span class="string">'b'</span>);</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'c'</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">run</span>(<span class="params">g</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> res = g.next(); <span class="comment">//记住res.value是个promise对象</span></span><br><span class="line">  <span class="keyword">if</span>(!res.done)&#123;</span><br><span class="line">    res.value.then(<span class="function"><span class="params">()</span>=&gt;</span>&#123;   <span class="comment">//promise解决了才继续执行生成器内部函数</span></span><br><span class="line">      run(g);</span><br><span class="line">    &#125;)  </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">run(foo()); </span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'d'</span>); <span class="comment">// Output: a d b c</span></span><br></pre></td></tr></table></figure><p><strong>defer/async</strong></p><blockquote><p><code>defer</code>的用途是表明脚本在执行时不会影响页面的构造。也就是说，脚本会被延迟到整个页面都解析完毕后再运行。因此，在<code>`元素中设置</code>defer`属性，相当于告诉浏览器立即下载，但延迟执行。</p><p>指定<code>async</code>属性的目的是不让页面等待两个脚本下载和执行，从而异步加载页面其他内容。</p></blockquote><h4 id="回调地狱"><a href="#回调地狱" class="headerlink" title="回调地狱"></a>回调地狱</h4><blockquote><p>函数作为参数层层嵌套</p></blockquote><p>如何解决：</p><ul><li>保持你的代码简短</li><li>模块化（函数封装，打包，每个功能独立）</li><li>处理每一个错误</li><li>异步（Promise/Generator/async/await）</li></ul><h3 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h3><blockquote><p><strong>Promise</strong> 对象用于表示一个异步操作的最终完成 (或失败)，及其结果值。</p></blockquote><h4 id="Demo"><a href="#Demo" class="headerlink" title="Demo"></a>Demo</h4><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> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        resolve(<span class="string">"foo"</span>);</span><br><span class="line">    &#125;, <span class="number">300</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">promise.then(<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(value);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(promise);</span><br></pre></td></tr></table></figure><p><code>Promise</code> 对象是一个代理对象（代理一个值），被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法（handlers）。 这让异步方法可以像同步方法那样返回值，但并不是立即返回最终执行结果，而是一个能代表未来出现的结果的promise对象。</p><p>一个 <code>Promise</code>有以下几种状态:</p><ul><li><em>pending</em>: 初始状态，既不是成功，也不是失败状态。</li><li><em>fulfilled</em>: 意味着操作成功完成。</li><li><em>rejected</em>: 意味着操作失败。</li></ul><p>当其中任一种情况（fulfilled or rejected）出现时，Promise 对象的 <code>then</code> 方法绑定的处理方法（handlers ）就会被调用（then方法包含两个参数：onfulfilled 和 onrejected，它们都是 Function 类型。当Promise状态为<em>fulfilled</em>时，调用 then 的 onfulfilled 方法，当Promise状态为<em>rejected</em>时，调用 then 的 onrejected 方法， 所以在异步操作的完成和绑定处理方法之间不存在竞争）。</p><h4 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h4><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> PENDING = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">var</span> FULFILLED = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> REJECTED = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Promise</span>(<span class="params">callback</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.status = PENDING;</span><br><span class="line">    <span class="keyword">this</span>.value = <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">this</span>.defferd = [];</span><br><span class="line">    setTimeout(callback.bind(<span class="keyword">this</span>, <span class="keyword">this</span>.resolve.bind(<span class="keyword">this</span>), <span class="keyword">this</span>.reject.bind(<span class="keyword">this</span>)), <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line">            </span><br><span class="line"><span class="built_in">Promise</span>.prototype = &#123;</span><br><span class="line">    <span class="keyword">constructor</span>: Promise,</span><br><span class="line">    //触发改变promise状态到FULFILLED</span><br><span class="line">    resolve: function (result) &#123;</span><br><span class="line">        <span class="keyword">this</span>.status = FULFILLED;</span><br><span class="line">        <span class="keyword">this</span>.value = result;</span><br><span class="line">        <span class="keyword">this</span>.done();</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">//触发改变promise状态到REJECTED</span></span><br><span class="line">    reject: <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.status = REJECTED;</span><br><span class="line">        <span class="keyword">this</span>.value = error;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">//处理defferd</span></span><br><span class="line">    handle: <span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!fn) &#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">var</span> value = <span class="keyword">this</span>.value;</span><br><span class="line">        <span class="keyword">var</span> t = <span class="keyword">this</span>.status;</span><br><span class="line">        <span class="keyword">var</span> p;</span><br><span class="line">        <span class="keyword">if</span> (t == PENDING) &#123;</span><br><span class="line">             <span class="keyword">this</span>.defferd.push(fn);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (t == FULFILLED &amp;&amp; <span class="keyword">typeof</span> fn.onfulfiled == <span class="string">'function'</span>) &#123;</span><br><span class="line">                p = fn.onfulfiled(value);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (t == REJECTED &amp;&amp; <span class="keyword">typeof</span> fn.onrejected == <span class="string">'function'</span>) &#123;</span><br><span class="line">                p = fn.onrejected(value);</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">var</span> promise = fn.promise;</span><br><span class="line">        <span class="keyword">if</span> (promise) &#123;</span><br><span class="line">            <span class="keyword">if</span> (p &amp;&amp; p.constructor == <span class="built_in">Promise</span>) &#123;</span><br><span class="line">                p.defferd = promise.defferd;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                p = <span class="keyword">this</span>;</span><br><span class="line">                p.defferd = promise.defferd;</span><br><span class="line">                <span class="keyword">this</span>.done();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">//触发promise defferd里面需要执行的函数</span></span><br><span class="line">    done: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> status = <span class="keyword">this</span>.status;</span><br><span class="line">        <span class="keyword">if</span> (status == PENDING) &#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">var</span> defferd = <span class="keyword">this</span>.defferd;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; defferd.length; i++) &#123;</span><br><span class="line">            <span class="keyword">this</span>.handle(defferd[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">/*储存then函数里面的事件</span></span><br><span class="line"><span class="comment">    返回promise对象</span></span><br><span class="line"><span class="comment">    defferd函数当前promise对象里面</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    then: <span class="function"><span class="keyword">function</span> (<span class="params">success, fail</span>) </span>&#123;</span><br><span class="line">       <span class="keyword">var</span> o = &#123;</span><br><span class="line">            onfulfiled: success,</span><br><span class="line">            onrejected: fail</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="keyword">var</span> status = <span class="keyword">this</span>.status;</span><br><span class="line">        o.promise = <span class="keyword">new</span> <span class="keyword">this</span>.constructor(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            </span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">if</span> (status == PENDING) &#123;</span><br><span class="line">            <span class="keyword">this</span>.defferd.push(o);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status == FULFILLED || status == REJECTED) &#123;</span><br><span class="line">            <span class="keyword">this</span>.handle(o);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> o.promise;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h4 id="Promise-all-的实现"><a href="#Promise-all-的实现" class="headerlink" title="Promise.all() 的实现"></a>Promise.all() 的实现</h4><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">promiseAll</span>(<span class="params">promises</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="built_in">Array</span>.isArray(promises)) &#123;</span><br><span class="line">      <span class="keyword">return</span> reject(<span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'arguments must be an array'</span>));</span><br><span class="line">    &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">let</span> resolvedCount = <span class="number">0</span>, promisesNum = promises.length;</span><br><span class="line">        <span class="keyword">let</span> resolvedValues = <span class="keyword">new</span> <span class="built_in">Array</span>(promisesNum);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; promiseNum; i++) &#123;</span><br><span class="line">            (<span class="function"><span class="keyword">function</span> (<span class="params">i</span>) </span>&#123;</span><br><span class="line">                <span class="built_in">Promise</span>.resolve(promises[i]).then(<span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">                    resolvedCount++;</span><br><span class="line">                    resolvedValues[i] = value;</span><br><span class="line">                    </span><br><span class="line">                    <span class="keyword">if</span> (resolvedCount == promisesNum) &#123;</span><br><span class="line">                        <span class="keyword">return</span> resolve(resolvedValues);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;).catch(<span class="function"><span class="params">reason</span> =&gt;</span> &#123;</span><br><span class="line">                    reject(reason);</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;)(i);</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><h4 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h4><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> promise = <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">if</span> (<span class="comment">/* 操作成功 */</span>) &#123;</span><br><span class="line">        resolve(value);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        reject(error);                    </span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">promise.then(<span class="function">(<span class="params">value</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">/* success */</span></span><br><span class="line">&#125;).catch(<span class="function">(<span class="params">error</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">/* failure */</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p><strong>使用<code>Promise</code>封装<code>Ajax</code></strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> attributes = &#123;</span><br><span class="line">    url: <span class="string">"/test"</span>,</span><br><span class="line">    method: <span class="string">"GET"</span>,</span><br><span class="line">    data: <span class="literal">null</span>,</span><br><span class="line">    <span class="keyword">async</span>: <span class="literal">true</span>,</span><br><span class="line">    headers: &#123;</span><br><span class="line">        <span class="string">'content-type'</span>: <span class="string">'application/x-www-form-urlencoded'</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> request = <span class="function">(<span class="params">attributes</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">    <span class="keyword">let</span> medthod = attributes.method.toUpperCase();</span><br><span class="line">    </span><br><span class="line">    xhr.open(method, attributes.url, attributes.async);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> header <span class="keyword">in</span> attributes.headers) &#123;</span><br><span class="line">        xhr.setRequestHeader(header, attributes.headers[header]);</span><br><span class="line">    &#125;</span><br><span class="line">    xhr.send(method == <span class="string">"GET"</span> ? <span class="literal">null</span> : <span class="built_in">JSON</span>.stringify(attributes.data));</span><br><span class="line">    </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">        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> (<span class="keyword">this</span>.readyState == <span class="number">4</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">this</span>.status === <span class="number">200</span>) &#123;</span><br><span class="line">                    resolve(<span class="built_in">JSON</span>.parse(<span class="keyword">this</span>.responseText), <span class="keyword">this</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="keyword">let</span> resJson = &#123;</span><br><span class="line">                        code: <span class="keyword">this</span>.status, </span><br><span class="line">                        response: <span class="keyword">this</span>.response</span><br><span class="line">                    &#125;;</span><br><span class="line">                    </span><br><span class="line">                    reject(resJson, <span class="keyword">this</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="高阶函数"><a href="#高阶函数" class="headerlink" title="高阶函数"></a>高阶函数</h3><blockquote><p>一个函数接收另一个函数作为参数，称之为高阶函数</p></blockquote><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">add</span>(<span class="params">x, y, fn</span>) </span>&#123;</span><br><span class="line"><span class="keyword">return</span> fn(x) + fn(y);</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">square</span>(<span class="params">x</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> x * x;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(add(<span class="number">2</span>, <span class="number">3</span>, square));</span><br></pre></td></tr></table></figure><h3 id="对This对象的理解"><a href="#对This对象的理解" class="headerlink" title="对This对象的理解"></a>对This对象的理解</h3><ul><li><p>全局范围内</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">this</span>;</span><br></pre></td></tr></table></figure><p>当在全部范围内使用 <code>this</code>，它将会指向<em>全局</em>对象。</p></li><li><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="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">foo(); <span class="comment">// console.log: window;</span></span><br></pre></td></tr></table></figure><p>这里 <code>this</code> 也会指向<em>全局</em>对象。</p></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> test = &#123;</span><br><span class="line">    foo: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">test.foo(); <span class="comment">// console.log: &#123;foo: f&#125;</span></span><br></pre></td></tr></table></figure><p>这个例子中，<code>this</code> 指向 <code>test</code> 对象。</p></li><li><p>调用构造函数</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> foo();</span><br></pre></td></tr></table></figure><p>如果函数倾向于和 <code>new</code> 关键词一块使用，则我们称这个函数是 <a href="https://bonsaiden.github.io/JavaScript-Garden/zh/#function.constructors" target="_blank" rel="noopener">构造函数</a>。 在函数内部，<code>this</code> 指向<em>新创建</em>的对象。</p></li><li><p>显式设置<code>this</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="built_in">module</span> = &#123;</span><br><span class="line">  x: <span class="number">42</span>,</span><br><span class="line">  getX: <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>.x;</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> unboundGetX = <span class="built_in">module</span>.getX;</span><br><span class="line"><span class="built_in">console</span>.log(unboundGetX()); </span><br><span class="line"><span class="comment">// The function gets invoked at the global scope</span></span><br><span class="line"><span class="comment">// expected output: undefined</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> boundGetX = unboundGetX.bind(<span class="built_in">module</span>);</span><br><span class="line"><span class="built_in">console</span>.log(boundGetX());</span><br><span class="line"><span class="comment">// expected output: 42</span></span><br></pre></td></tr></table></figure><p>当使用 <code>Function.prototype</code> 上的 <code>call</code> 或者 <code>apply</code> 方法时，函数内的 <code>this</code> 将会被 <strong>显式设置</strong>为函数调用的第一个参数。</p><p>因此<em>函数调用</em>的规则在上例中已经不适用了，在<code>foo</code> 函数内 <code>this</code> 被设置成了 <code>bar</code>。</p></li></ul><h3 id="Function-prototype-bind-Function-prototype-apply-Function-prototype-call"><a href="#Function-prototype-bind-Function-prototype-apply-Function-prototype-call" class="headerlink" title="Function.prototype.bind() / Function.prototype.apply() / Function.prototype.call()"></a>Function.prototype.bind() / Function.prototype.apply() / Function.prototype.call()</h3><blockquote><p><strong><code>bind()</code></strong> 方法创建一个新函数，在调用时，将其 <code>this</code> 关键字设置为所需的值。</p></blockquote><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">var</span> pokemon = &#123;</span><br><span class="line">    firstname: <span class="string">'Pika'</span>,</span><br><span class="line">    lastname: <span class="string">'Chu '</span>,</span><br><span class="line">    getPokeName: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> fullname = <span class="keyword">this</span>.firstname + <span class="string">' '</span> + <span class="keyword">this</span>.lastname;</span><br><span class="line">        <span class="keyword">return</span> fullname;</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> pokemonName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.getPokeName() + <span class="string">'I choose you!'</span>);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> logPokemon = pokemonName.bind(pokemon); <span class="comment">// creates new object and binds pokemon. 'this' of pokemon === pokemon now</span></span><br><span class="line"></span><br><span class="line">logPokemon(); <span class="comment">// 'Pika Chu I choose you!'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> pokemon = &#123;</span><br><span class="line">    firstname: <span class="string">'Pika'</span>,</span><br><span class="line">    lastname: <span class="string">'Chu '</span>,</span><br><span class="line">    getPokeName: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> fullname = <span class="keyword">this</span>.firstname + <span class="string">' '</span> + <span class="keyword">this</span>.lastname;</span><br><span class="line">        <span class="keyword">return</span> fullname;</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"><span class="keyword">var</span> pokemonName = <span class="function"><span class="keyword">function</span>(<span class="params">snack, hobby</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.getPokeName() + <span class="string">'I choose you!'</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.getPokeName() + <span class="string">' loves '</span> + snack + <span class="string">' and '</span> + hobby);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> logPokemon = pokemonName.bind(pokemon); <span class="comment">// creates new object and binds pokemon. 'this' of pokemon === pokemon now</span></span><br><span class="line"></span><br><span class="line">logPokemon(<span class="string">'sushi'</span>, <span class="string">'algorithms'</span>); <span class="comment">// Pika Chu  loves sushi and algorithms</span></span><br></pre></td></tr></table></figure><blockquote><p><strong><code>call()</code></strong> 方法调用一个给定 <code>this</code> 值的函数，并单独提供参数。</p><p><code>call()</code> 和<code>apply()</code> 使用于<strong>完全相同的目的。</strong> <strong>它们工作方式之间的唯一区别</strong>是 <code>call()</code> 期望所有参数都单独传递，而 <code>apply()</code> 需要所有参数的数组。</p></blockquote><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> pokemon = &#123;</span><br><span class="line">    firstname: <span class="string">'Pika'</span>,</span><br><span class="line">    lastname: <span class="string">'Chu '</span>,</span><br><span class="line">    getPokeName: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> fullname = <span class="keyword">this</span>.firstname + <span class="string">' '</span> + <span class="keyword">this</span>.lastname;</span><br><span class="line">        <span class="keyword">return</span> fullname;</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> pokemonName = <span class="function"><span class="keyword">function</span>(<span class="params">snack, hobby</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.getPokeName() + <span class="string">' loves '</span> + snack + <span class="string">' and '</span> + hobby);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">pokemonName.call(pokemon,<span class="string">'sushi'</span>, <span class="string">'algorithms'</span>); <span class="comment">// Pika Chu  loves sushi and algorithms</span></span><br><span class="line">pokemonName.apply(pokemon,[<span class="string">'sushi'</span>, <span class="string">'algorithms'</span>]); <span class="comment">// Pika Chu  loves sushi and algorithms</span></span><br></pre></td></tr></table></figure><h4 id="call-实现"><a href="#call-实现" class="headerlink" title="call() 实现"></a>call() 实现</h4><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="function"><span class="keyword">function</span> <span class="title">myCall</span>(<span class="params">context</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 1</span></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">TypeError</span>(<span class="string">'error'</span>)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 2</span></span><br><span class="line">  context = context || <span class="built_in">window</span></span><br><span class="line">  <span class="comment">// 3</span></span><br><span class="line">  context.fn = <span class="keyword">this</span></span><br><span class="line">  <span class="comment">// 4</span></span><br><span class="line">  <span class="keyword">const</span> args = [...arguments].slice(<span class="number">1</span>)</span><br><span class="line">  <span class="comment">// 5</span></span><br><span class="line">  <span class="keyword">const</span> result = context.fn(...args)</span><br><span class="line">  <span class="comment">// 6</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><h4 id="apply"><a href="#apply" class="headerlink" title="apply()"></a>apply()</h4> <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">myApply</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">TypeError</span>(<span class="string">'Error'</span>)</span><br><span class="line">  &#125;</span><br><span class="line"></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 class="keyword">var</span> result</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">arguments</span>[<span class="number">1</span>]) &#123;</span><br><span class="line">    result = context.fn(...arguments[<span class="number">1</span>])</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    result = context.fn()</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><h4 id="bind"><a href="#bind" class="headerlink" title="bind()"></a>bind()</h4><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">myBind</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">TypeError</span>(<span class="string">'Error'</span>)</span><br><span class="line">  &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">const</span> _this = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">const</span> args = [...arguments].slice(<span class="number">1</span>)</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="title">F</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span> <span class="keyword">instanceof</span> F) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> _this(...args, ...arguments)</span><br><span class="line">  &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> _this.apply(context, args.concat(...arguments))</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><ul><li>假如条件测试结果相同，可以选择合并条件</li><li>理清不同分支之间关联性，减少<code>if ... else ...</code>嵌套</li><li>异常条件先退出，保证主干流程是核心流程</li><li>将类似流程封装为类似的公共函数</li><li>使用多态取代条件表达式</li></ul><h3 id="EventEmitter"><a href="#EventEmitter" class="headerlink" title="EventEmitter"></a>EventEmitter</h3><blockquote><p>Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。</p><p>events 模块只提供了一个对象： events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。</p></blockquote><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">let</span> events = <span class="built_in">require</span>(<span class="string">'events'</span>);</span><br><span class="line"><span class="keyword">let</span> eventEmitter = <span class="keyword">new</span> events.EventEmitter();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听器 #1</span></span><br><span class="line"><span class="keyword">const</span> listener1 = <span class="function"><span class="keyword">function</span> <span class="title">listener1</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">'监听器 listener1 执行。'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听器 #2</span></span><br><span class="line"><span class="keyword">const</span> listener2 = <span class="function"><span class="keyword">function</span> <span class="title">listener2</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'监听器 listener2 执行。'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绑定 connection 事件，处理函数为 listener1 </span></span><br><span class="line">eventEmitter.addListener(<span class="string">'connection'</span>, listener1);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绑定 connection 事件，处理函数为 listener2</span></span><br><span class="line">eventEmitter.on(<span class="string">'connection'</span>, listener2);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> eventListeners = eventEmitter.listenerCount(<span class="string">'connection'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(eventListeners + <span class="string">" 个监听器监听连接事件。"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 处理 connection 事件 </span></span><br><span class="line">eventEmitter.emit(<span class="string">'connection'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 移除监绑定的 listener1 函数</span></span><br><span class="line">eventEmitter.removeListener(<span class="string">'connection'</span>, listener1);</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"listener1 不再受监听。"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 触发连接事件</span></span><br><span class="line">eventEmitter.emit(<span class="string">'connection'</span>);</span><br><span class="line"></span><br><span class="line">eventListeners = eventEmitter.listenerCount(<span class="string">'connection'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(eventListeners + <span class="string">" 个监听器监听连接事件。"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"程序执行完毕。"</span>);</span><br></pre></td></tr></table></figure><h3 id="module-export-exports-export的区别"><a href="#module-export-exports-export的区别" class="headerlink" title="module.export exports export的区别"></a>module.export exports export的区别</h3><p>总览</p><p><code>require</code>: node 支持的引入</p><p><code>export / import</code> : 只有es6 支持的导出引入</p><p><code>module.exports / exports</code>: 只有 node 支持的导出</p><h4 id="node-模块"><a href="#node-模块" class="headerlink" title="node 模块"></a>node 模块</h4><blockquote><p><code>Node</code>里面的模块系统遵循的是<code>CommonJS</code>规范。<code>CommonJS</code>定义的模块分为: 模块标识(<code>module</code>)、模块定义(<code>exports</code>) 、模块引用(<code>require</code>)</p></blockquote><p>在一个node执行一个文件时，会给这个文件内生成一个 <code>exports</code>和<code>module</code>对象， 而<code>module</code>又有一个<code>exports</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">exports = <span class="built_in">module</span>.exports = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//utils.js</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">module</span>.exports); <span class="comment">//能打印出结果为：&#123;&#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(exports); <span class="comment">//能打印出结果为：&#123;&#125;</span></span><br><span class="line"></span><br><span class="line">exports.a = <span class="number">200</span>; <span class="comment">//这里辛苦劳作帮 module.exports 的内容给改成 &#123;a : 200&#125;</span></span><br><span class="line"></span><br><span class="line">exports = <span class="string">'指向其他内存区'</span>; <span class="comment">//这里把exports的指向指走</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//test.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'/utils'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(a) <span class="comment">// 打印为 &#123;a : 200&#125;</span></span><br></pre></td></tr></table></figure><p>其实<code>require</code>导出的内容是<code>module.exports</code>的指向的内存块内容，并不是<code>exports</code>的。 简而言之，区分他们之间的区别就是 <code>exports</code> 只是 <code>module.exports</code>的引用，辅助后者添加内容用的。</p><p>为了避免糊涂，尽量都用 <code>module.exports</code> 导出，然后用<code>require</code>导入。</p><p><strong>ES 模块</strong></p><p>对于 <code>export</code> 和<code>export default</code>:</p><ul><li>二者可用于导出常量、函数、文件、模块等</li><li>在一个文件或模块中，<code>export</code>、<code>import</code>可以有多个，<code>export default</code>仅有一个</li><li>通过<code>export</code>方式导出，在导入时要加<code>{ }</code>，<code>export default</code>则不需要</li><li><code>export</code>能直接导出变量表达式，<code>export default</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></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"><span class="comment">//导出变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> a = <span class="string">'100'</span>;  </span><br><span class="line"></span><br><span class="line"> <span class="comment">//导出方法</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> dogSay = <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">'wang wang'</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="function"><span class="keyword">function</span> <span class="title">catSay</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">'miao miao'</span>); </span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> &#123; catSay &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//export default导出</span></span><br><span class="line"><span class="keyword">const</span> m = <span class="number">100</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> m; </span><br><span class="line"><span class="comment">//export defult const m = 100;// 这里不能写这种格式。</span></span><br></pre></td></tr></table></figure><h3 id="虚拟DOM"><a href="#虚拟DOM" class="headerlink" title="虚拟DOM"></a>虚拟DOM</h3><blockquote><p> 用 JavaScript 对象表示 DOM 信息和结构，当状态变更的时候，重新渲染这个 JavaScript 的对象结构。</p></blockquote><p>步骤</p><ol><li>用 JavaScript 对象结构表示 DOM 树的结构；然后用这个树构建一个真正的 DOM 树，插到文档当中</li><li>当状态变更的时候，重新构造一棵新的对象树。然后用新的树和旧的树进行比较，记录两棵树差异</li><li>把2所记录的差异应用到步骤1所构建的真正的DOM树上，视图就更新了</li></ol><p>Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存</p><h3 id="ES-6"><a href="#ES-6" class="headerlink" title="ES 6"></a>ES 6</h3><h4 id="解构赋值"><a href="#解构赋值" class="headerlink" title="解构赋值"></a>解构赋值</h4><p><strong>数组解构</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> [a, b, c] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]   <span class="comment">//a=1, b=2, c=3</span></span><br><span class="line"><span class="keyword">let</span> [d, [e], f] = [<span class="number">1</span>, [<span class="number">2</span>], <span class="number">3</span>]    <span class="comment">//嵌套数组解构 d=1, e=2, f=3</span></span><br><span class="line"><span class="keyword">let</span> [g, ...h] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]   <span class="comment">//数组拆分 g=1, h=[2, 3]</span></span><br><span class="line"><span class="keyword">let</span> [i,,j] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]   <span class="comment">//不连续解构 i=1, j=3</span></span><br><span class="line"><span class="keyword">let</span> [k,l] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]   <span class="comment">//不完全解构 k=1, l=2</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="keyword">let</span> &#123;a, b&#125; = &#123;<span class="attr">a</span>: <span class="string">'aaaa'</span>, <span class="attr">b</span>: <span class="string">'bbbb'</span>&#125;      <span class="comment">//a='aaaa' b='bbbb'</span></span><br><span class="line"><span class="keyword">let</span> obj = &#123;<span class="attr">d</span>: <span class="string">'aaaa'</span>, <span class="attr">e</span>: &#123;<span class="attr">f</span>: <span class="string">'bbbb'</span>&#125;&#125;</span><br><span class="line"><span class="keyword">let</span> &#123;d, <span class="attr">e</span>:&#123;f&#125;&#125; = obj    <span class="comment">//嵌套解构 d='aaaa' f='bbbb'</span></span><br><span class="line"><span class="keyword">let</span> g;</span><br><span class="line">(g = &#123;<span class="attr">g</span>: <span class="string">'aaaa'</span>&#125;)   <span class="comment">//以声明变量解构 g='aaaa'</span></span><br><span class="line"><span class="keyword">let</span> [h, i, j, k] = <span class="string">'nice'</span>    <span class="comment">//字符串解构 h='n' i='i' j='c' k='e'</span></span><br></pre></td></tr></table></figure><p><strong>函数参数的定义</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">personInfo</span>(<span class="params">&#123;name, age, address, gender&#125;</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(name, age, address, gender)</span><br><span class="line">&#125;</span><br><span class="line">personInfo(&#123;<span class="attr">gender</span>: <span class="string">'man'</span>, <span class="attr">address</span>: <span class="string">'changsha'</span>, <span class="attr">name</span>: <span class="string">'william'</span>, <span class="attr">age</span>: <span class="number">18</span>&#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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a=<span class="number">1</span>, b=<span class="number">2</span>;</span><br><span class="line">[b, a] = [a, b]</span><br><span class="line"><span class="built_in">console</span>.log(a, b)</span><br></pre></td></tr></table></figure><p><strong>函数默认参数</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">saveInfo</span>(<span class="params">&#123;name= <span class="string">'william'</span>, age= <span class="number">18</span>, address= <span class="string">'changsha'</span>, gender= <span class="string">'man'</span>&#125; = &#123;&#125;</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(name, age, address, gender)</span><br><span class="line">&#125;</span><br><span class="line">saveInfo()</span><br></pre></td></tr></table></figure><h4 id="使用箭头函数应注意什么？"><a href="#使用箭头函数应注意什么？" class="headerlink" title="使用箭头函数应注意什么？"></a>使用箭头函数应注意什么？</h4><p>1、用了箭头函数，this就不是指向window，而是父级（指向是可变的）<br>2、不能够使用arguments对象<br>3、不能用作构造函数，这就是说不能够使用new命令，否则会抛出一个错误<br>4、不可以使用yield命令，因此箭头函数不能用作 Generator 函数</p><h3 id="Ajax"><a href="#Ajax" class="headerlink" title="Ajax"></a>Ajax</h3><h4 id="同步和异步的区别"><a href="#同步和异步的区别" class="headerlink" title="同步和异步的区别"></a>同步和异步的区别</h4><p><strong>同步：</strong><br>浏览器访问服务器请求，用户看得到页面刷新，重新发请求,等请求完，页面刷新，新内容出现，用户看到新内容,进行下一步操作</p><p><strong>异步：</strong><br>浏览器访问服务器请求，用户正常操作，浏览器后端进行请求。等请求完，页面不刷新，新内容也会出现，用户看到新内容</p><h4 id="get和post的区别"><a href="#get和post的区别" class="headerlink" title="get和post的区别"></a>get和post的区别</h4><p>1、get和post在HTTP中都代表着请求数据，其中get请求相对来说更简单、快速，效率高些 2、get相对post安全性低<br>3、get有缓存，post没有<br>4、get体积小，post可以无限大<br>5、get的url参数可见，post不可见<br>6、get只接受ASCII字符的参数数据类型，post没有限制<br>7、get请求参数会保留历史记录，post中参数不会保留<br>8、get会被浏览器主动catch，post不会，需要手动设置<br>9、get在浏览器回退时无害，post会再次提交请求</p><p><strong>什么时候使用post？</strong></p><p>post一般用于修改服务器上的资源，对所发送的信息没有限制。比如</p><p>1、无法使用缓存文件（更新服务器上的文件或数据库）<br>2、向服务器发送大量数据（POST 没有数据量限制）<br>3、发送包含未知字符的用户输入时，POST 比 GET 更稳定也更可靠</p><h3 id="Webpack"><a href="#Webpack" class="headerlink" title="Webpack"></a>Webpack</h3><blockquote><p>webpack只是一个打包模块的机制，只是把依赖的模块转化成可以代表这些包的静态文件。webpack就是识别你的 入口文件。识别你的模块依赖，来打包你的代码。至于你的代码使用的是commonjs还是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。webpack做的就是分析代码。转换代码，编译代码，输出代码。webpack本身是一个node的模块，所以webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)</p></blockquote><h4 id="模块热更新"><a href="#模块热更新" class="headerlink" title="模块热更新"></a>模块热更新</h4><blockquote><p>模块热更新是webpack的一个功能，他可以使代码修改过后不用刷新就可以更新，是高级版的自动刷新浏览器</p></blockquote><p>devServer中通过hot属性可以控制模块的热替换</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> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">let</span> env = process.env.NODE_ENV == <span class="string">"development"</span> ? <span class="string">"development"</span> : <span class="string">"production"</span>;</span><br><span class="line"><span class="keyword">const</span> config = &#123;</span><br><span class="line">  mode: env,</span><br><span class="line">  devServer: &#123;</span><br><span class="line">     hot:<span class="literal">true</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line">  plugins: [</span><br><span class="line">     <span class="keyword">new</span> webpack.HotModuleReplacementPlugin(), <span class="comment">//热加载插件</span></span><br><span class="line">  ],</span><br><span class="line"><span class="built_in">module</span>.exports = config;</span><br></pre></td></tr></table></figure><h4 id="打包流程"><a href="#打包流程" class="headerlink" title="打包流程"></a>打包流程</h4><p><code>webpack</code> 的运行流程是一个串行的过程，从启动到结束会依次执行以下流程：</p><ol><li>初始化参数：从配置文件和 <code>Shell</code> 语句中读取与合并参数，得出最终的参数；</li><li>开始编译：用上一步得到的参数初始化 <code>Compiler</code> 对象，加载所有配置的插件，执行对象的 <code>run</code> 方法开始执行编译；</li><li>确定入口：根据配置中的 entry 找出所有的入口文件</li><li>编译模块：从入口文件出发，调用所有配置的 Loader 对模块进行翻译，再找出该模块依赖的模块，再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理；</li><li>完成模块编译：在经过第4步使用 Loader 翻译完所有模块后，得到了每个模块被翻译后的最终内容以及它们之间的依赖关系；</li><li>输出资源：根据入口和模块之间的依赖关系，组装成一个个包含多个模块的 <code>Chunk</code>，再把每个 <code>Chunk</code> 转换成一个单独的文件加入到输出列表，这步是可以修改输出内容的最后机会；</li><li>输出完成：在确定好输出内容后，根据配置确定输出的路径和文件名，把文件内容写入到文件系统。 在以上过程中，<code>webpack</code> 会在特定的时间点广播出特定的事件，插件在监听到感兴趣的事件后会执行特定的逻辑，并且插件可以调用 <code>webpack</code> 提供的 API 改变 <code>webpack</code> 的运行结果。</li></ol><p>依照上述流程，我们来学习<code>webpack.config.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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// webpack.config.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">// 入口文件</span></span><br><span class="line">    entry: &#123;</span><br><span class="line">        bundle1: <span class="string">"./main1.js"</span>,</span><br><span class="line">        bundle2: <span class="string">"./main2.js"</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 输出路径及文件名</span></span><br><span class="line">    output: &#123;</span><br><span class="line">    filename: <span class="string">"[name].js"</span></span><br><span class="line">&#125;,</span><br><span class="line">    <span class="comment">// 模块依赖</span></span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">            test: <span class="regexp">/\.jsx?$/</span>,</span><br><span class="line">            exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">            use: &#123;</span><br><span class="line">                loader: <span class="string">'babel-loader'</span>,</span><br><span class="line">                options: &#123;</span><br><span class="line">                    presets: [<span class="string">'es2015'</span>, <span class="string">'react'</span>]</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.css$/</span>,</span><br><span class="line">                use: [</span><br><span class="line">                    &#123;</span><br><span class="line">                        loader: <span class="string">'style-loader'</span></span><br><span class="line">                    &#125;,</span><br><span class="line">                    &#123;</span><br><span class="line">                        loader: <span class="string">'css-loader'</span>,</span><br><span class="line">             options: &#123;</span><br><span class="line">               modules: <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">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.(png|jpg)$/</span>,</span><br><span class="line">                use: &#123;</span><br><span class="line">                    loader: <span class="string">'url-loader'</span>,</span><br><span class="line">                    options: &#123;</span><br><span class="line">              limit: <span class="number">8192</span></span><br><span class="line">            &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 插件</span></span><br><span class="line">    plugins: [</span><br><span class="line">        <span class="keyword">new</span> UglifyJsPlugin(),</span><br><span class="line">        <span class="keyword">new</span> HtmlwebpackPlugin(&#123;</span><br><span class="line">      title: <span class="string">'Webpack-demos'</span>,</span><br><span class="line">      filename: <span class="string">'index.html'</span></span><br><span class="line">    &#125;),</span><br><span class="line">    <span class="keyword">new</span> OpenBrowserPlugin(&#123;</span><br><span class="line">      url: <span class="string">'http://localhost:8080'</span></span><br><span class="line">    &#125;),</span><br><span class="line">        devFlagPlugin,</span><br><span class="line">        <span class="keyword">new</span> webpack.optimize.CommonsChunkPlugin(&#123;</span><br><span class="line">      name: <span class="string">"commons"</span>,</span><br><span class="line">      <span class="comment">// (the commons chunk name)</span></span><br><span class="line"></span><br><span class="line">      filename: <span class="string">"commons.js"</span>,</span><br><span class="line">      <span class="comment">// (the filename of the commons chunk)</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">    externals: &#123;</span><br><span class="line">        <span class="string">'data'</span>: <span class="string">'data'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="XHR-Ajax-Fetch-Axios"><a href="#XHR-Ajax-Fetch-Axios" class="headerlink" title="XHR / Ajax / Fetch / Axios"></a>XHR / Ajax / Fetch / Axios</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原生XHR</span></span><br><span class="line"><span class="keyword">var</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">xhr.open(<span class="string">'GET'</span>, url);</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 class="comment">// 从服务器获取数据</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">xhr.send()</span><br><span class="line"></span><br><span class="line"><span class="comment">// jQuery 封装</span></span><br><span class="line">$.ajax(&#123;</span><br><span class="line">    type: <span class="string">'POST'</span>,</span><br><span class="line">    url: url,</span><br><span class="line">    data: data,</span><br><span class="line">    dataType: dataType,</span><br><span class="line">    success: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">    error: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Fetch</span></span><br><span class="line">fetch(url).then(<span class="function"><span class="params">response</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (response.ok) &#123;</span><br><span class="line">        <span class="keyword">return</span> response.json();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;).then(<span class="function"><span class="params">data</span> =&gt;</span> <span class="built_in">console</span>.log(data)).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>注意：</p><blockquote><p>当接收到一个代表错误的 HTTP 状态码时，从 fetch()返回的 Promise 不会被标记为 reject， 即使该 HTTP 响应的状态码是 404 或 500。相反，它会将 Promise 状态标记为 resolve （但是会将 resolve 的返回值的 ok 属性设置为 false ）， 仅当网络故障时或请求被阻止时，才会标记为 reject。 默认情况下, fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session，则会导致未经认证的请求（要发送 cookies，必须设置 credentials 选项）。</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// jquery ajax</span></span><br><span class="line">$.post(url, &#123;<span class="attr">name</span>: <span class="string">'test'</span>&#125;)</span><br><span class="line"><span class="comment">// fetch</span></span><br><span class="line">fetch(url, &#123;</span><br><span class="line">    method: <span class="string">'POST'</span>,</span><br><span class="line">    body: <span class="built_in">Object</span>.keys(&#123;<span class="attr">name</span>: <span class="string">'test'</span>&#125;).map(<span class="function">(<span class="params">key</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">encodeURIComponent</span>(key) + <span class="string">'='</span> + <span class="built_in">encodeURIComponent</span>(params[key]);</span><br><span class="line">    &#125;).join(<span class="string">'&amp;'</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p><strong>axios</strong></p><p>同样是对<code>XHR</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">axios(&#123;</span><br><span class="line">    method: <span class="string">'GET'</span>,</span><br><span class="line">    url: url,</span><br><span class="line">&#125;)</span><br><span class="line">.then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;<span class="built_in">console</span>.log(res)&#125;)</span><br><span class="line">.catch(<span class="function"><span class="params">err</span> =&gt;</span> &#123;<span class="built_in">console</span>.log(err)&#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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserAccount</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> axios.get(<span class="string">'/user/12345'</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">getUserPermissions</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> axios.get(<span class="string">'/user/12345/permissions'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">axios.all([getUserAccount(), getUserPermissions()])</span><br><span class="line">  .then(axios.spread(<span class="function"><span class="keyword">function</span> (<span class="params">acct, perms</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// Both requests are now complete</span></span><br><span class="line">  &#125;));</span><br></pre></td></tr></table></figure><h3 id="柯里化-Currying"><a href="#柯里化-Currying" class="headerlink" title="柯里化 / Currying"></a>柯里化 / Currying</h3><p><strong>Currying</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">x</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">y</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> x + y;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">add(<span class="number">1</span>)(<span class="number">2</span>); <span class="comment">// Output: 3</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><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">// 柯里化通用式 ES6</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">currying</span>(<span class="params">func, args = []</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> arity = func.length;</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">..._args</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(_args);</span><br><span class="line">        _args.unshift(...args);</span><br><span class="line"><span class="built_in">console</span>.log(_args);</span><br><span class="line">        <span class="keyword">if</span>(_args.length &lt; arity) &#123;</span><br><span class="line">            <span class="keyword">return</span> currying(func, _args);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> func(..._args);</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><blockquote><p>防抖(debounce)和节流(throttle)的作用都是防止函数多次调用。区别在于，假设一个用户一直触发这个函数，且每次触发函数的间隔小于wait，防抖的情况下只会调用一次，而节流的 情况会每隔一定时间（参数wait）调用函数。</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> debounce = <span class="function">(<span class="params">fn, wait, immediate</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> timeout = <span class="literal">null</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="comment">// 清除定时器</span></span><br><span class="line">        <span class="keyword">if</span> (timeout) clearTimeout(timeout);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (immediate) &#123;</span><br><span class="line">            <span class="keyword">let</span> now = !timeout;</span><br><span class="line">            </span><br><span class="line">            timeout = setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                timeout = <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> (now) fn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            timeout = setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">           fn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> throttle = <span class="function">(<span class="params">fn, wait</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> timeout = <span class="literal">null</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">if</span> (!timeout) &#123;</span><br><span class="line">            timeout = setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                timeout = <span class="literal">null</span>;</span><br><span class="line">                fn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</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><h3 id="实例-1"><a href="#实例-1" class="headerlink" title="实例"></a>实例</h3><h4 id="Array-prototype-flat-的实现"><a href="#Array-prototype-flat-的实现" class="headerlink" title="Array.prototype.flat 的实现"></a>Array.prototype.flat 的实现</h4><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">const</span> flatten = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> arr.reduce(<span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> a.concat(<span class="built_in">Array</span>.isArray(b) ? flatten(b) : b);</span><br><span class="line">    &#125;, []);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="高精度计时器"><a href="#高精度计时器" class="headerlink" title="高精度计时器"></a>高精度计时器</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> start = <span class="keyword">new</span> <span class="built_in">Date</span>().getTime(),  </span><br><span class="line">    time = <span class="number">0</span>,  </span><br><span class="line">    elapsed = <span class="string">'0.0'</span>;  </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">instance</span>(<span class="params"></span>)  </span></span><br><span class="line"><span class="function"></span>&#123;  </span><br><span class="line">    time += <span class="number">100</span>;  </span><br><span class="line">    elapsed = <span class="built_in">Math</span>.floor(time / <span class="number">100</span>) / <span class="number">10</span>;  </span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">Math</span>.round(elapsed) == elapsed) &#123; elapsed += <span class="string">'.0'</span>; &#125;  </span><br><span class="line">    <span class="built_in">document</span>.title = elapsed;  </span><br><span class="line">    <span class="keyword">var</span> diff = (<span class="keyword">new</span> <span class="built_in">Date</span>().getTime() - start) - time;  </span><br><span class="line">    <span class="built_in">window</span>.setTimeout(instance, (<span class="number">100</span> - diff));  </span><br><span class="line">&#125;  </span><br><span class="line"><span class="built_in">window</span>.setTimeout(instance, <span class="number">100</span>);</span><br></pre></td></tr></table></figure><h2 id="CSS"><a href="#CSS" class="headerlink" title="CSS"></a>CSS</h2><h3 id="盒子模型"><a href="#盒子模型" class="headerlink" title="盒子模型"></a>盒子模型</h3><blockquote><p>当对一个文档进行布局（lay out）的时候，浏览器的渲染引擎会根据标准之一的 <strong>CSS 基础框盒模型</strong>（<strong>CSS basic box model</strong>），将所有元素表示为一个个矩形的盒子（box）。</p></blockquote><p>每个盒子由四个部分（或称<em>区域</em>）组成，其效用由它们各自的边界（Edge）所定义，包括：</p><ul><li><p>内容边界 / Content Edge</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">box-sizing</span>: <span class="selector-tag">content-box</span>; <span class="comment">/* default */</span></span><br></pre></td></tr></table></figure><p>内容区域的大小明确可以通过以下属性控制：</p><p>| <code>width</code> | <code>min-width</code> | <code>max-width</code> | <code>height</code> | <code>min-height</code> | <code>max-height</code> |<br>| ———- | —————- | —————- | ———— | —————— | —————— |</p></li><li><p>内边距边界 / Padding Edge</p></li><li><p>边框边界 / Border Edge</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">box-sizing</span>: <span class="selector-tag">border-box</span>;</span><br></pre></td></tr></table></figure><p>边框区域的大小明确可以通过以下属性控制：</p><p>| <code>width</code> | <code>min-width</code> | <code>max-width</code> | <code>height</code> | <code>min-height</code> | <code>max-height</code> |<br>| ———- | —————- | —————- | ———— | —————— | —————— |</p></li><li><p>外边框边界 / Margin Edge</p></li></ul><p><strong>拓展</strong></p><p><code>box-sizing</code>属性可以被用来调整这些表现:</p><ul><li><code>content-box</code> 是默认值。如果你设置一个元素的宽为100px，那么这个元素的内容区会有100px 宽，并且任何边框和内边距的宽度都会被增加到最后绘制出来的元素宽度中。</li><li><code>border-box</code> 告诉浏览器：你想要设置的边框和内边距的值是包含在width内的。也就是说，如果你将一个元素的width设为100px，那么这100px会包含它的border和padding，内容区的实际宽度是width减去(border + padding)的值。大多数情况下，这使得我们更容易地设定一个元素的宽高。</li></ul><p>由此，可以对盒子模型进行分类：标注盒模型和怪异盒模型，前者的<code>box-sizing</code>属性取值为<code>content-box</code>，而后者的<code>box-sizing</code>属性取值为<code>border-box</code>。也就是说，对于标注盒模型，元素的宽度等于style里的width+border+padding宽度，而对于怪异盒模型，元素宽度等于style里的width宽度。</p><h3 id="rem与em的区别"><a href="#rem与em的区别" class="headerlink" title="rem与em的区别"></a>rem与em的区别</h3><blockquote><p>rem是根据根的font-size变化，而em是根据父级的font-size变化</p></blockquote><h3 id="CSS新特性"><a href="#CSS新特性" class="headerlink" title="CSS新特性"></a>CSS新特性</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">transition：过渡</span><br><span class="line">transform：旋转、缩放、移动或者倾斜</span><br><span class="line">animation：动画</span><br><span class="line">gradient：渐变</span><br><span class="line">shadow：阴影</span><br><span class="line">border-radius：圆角</span><br></pre></td></tr></table></figure><p><strong>行内元素（display: inline）</strong></p><p>宽度和高度是由内容决定，与其他元素共占一行的元素，我们将其叫行内元素，例如：<code>&lt;span&gt;、 &lt;i&gt; 、&lt;a&gt;</code>等</p><p><strong>块级元素（display: block)</strong></p><p>默认宽度由父容器决定，默认高度由内容决定，独占一行并且可以设置宽高的元素，我们将其叫做块级元素，例如：<code>&lt;p&gt; 、&lt;div&gt; 、&lt;ul&gt;等</code></p><p><strong>行内块级元素（display: inline-block）</strong></p><p>结合了行内元素和块级元素的特性，与其他行内元素共享一行，可以修改<code>width</code>、<code>height</code>属性<code>padding</code>、<code>margin</code>四个方向的值设置均有效。</p><p>缺点：</p><ul><li>在不等高的情况下视需要设置<code>vertical-align</code></li><li>需要解决换行符空格引起的间隙问题：常用<code>font-size: 0;</code></li><li>在IE6/7下残留1像素间隙。令人讨厌的内部元素的<code>font-size</code>需要另外设置</li></ul><h3 id="CSS-Module"><a href="#CSS-Module" class="headerlink" title="CSS-Module"></a>CSS-Module</h3><blockquote><p>目的：为CSS规则加入局部作用域和模块依赖</p></blockquote><p><strong>局部作用域</strong></p><p>CSS的规则都是全局的，生成局部作用域的唯一方法是使用独一无二的类名。通过生成独一无二的类名，使其仅对某一组间有效</p><p><strong>模块依赖</strong></p><p>CSS-Module允许通过<code>composes</code> 字段使一个选择器继承另一个选择器的规则，或者继承其他CSS文件中的规则</p><p><strong>输入变量</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">/* colors.css */</span></span><br><span class="line">@<span class="keyword">value</span> blue: #<span class="number">0</span>c77f8;</span><br><span class="line">@<span class="keyword">value</span> red: #ff0000;</span><br><span class="line">@<span class="keyword">value</span> green: #aaf200;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* App.css */</span></span><br><span class="line">@<span class="keyword">value</span> colors: <span class="string">"./colors.css"</span>;</span><br><span class="line">@<span class="keyword">value</span> blue, red, green from colors;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.title</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">  <span class="attribute">background-color</span>: blue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Flex"><a href="#Flex" class="headerlink" title="Flex"></a>Flex</h3><blockquote><p>Flex是Flexible Box的缩写，意味”弹性布局”，任何一个容器都可以指定为Flex布局</p></blockquote><ul><li>Flex布局元素，称为Flex容器，简称”容器”。它的所有子元素自动成为容器元素，简称”项目”。</li><li>容器默认存在两根轴：水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的排列方式：从左到右；交叉轴的排列方式：从上到下;</li></ul><h4 id="容器属性"><a href="#容器属性" class="headerlink" title="容器属性"></a>容器属性</h4><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">display</span>: <span class="selector-tag">flex</span>;</span><br></pre></td></tr></table></figure><ol><li><p>flex-direction :属性决定主轴的方向 (即项目的排列方式）</p><p>横向排列块级元素</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">flex-direction</span>: <span class="selector-tag">row</span>; <span class="comment">/* 主轴水平方向，起点在左端 */</span></span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">flex-direction</span>: <span class="selector-tag">row-reverse</span>; <span class="comment">/* 主轴水平方向，起点在右端 */</span></span><br></pre></td></tr></table></figure><p>纵向排列块级元素</p> <figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">flex-direction</span>: <span class="selector-tag">column</span>; <span class="comment">/* 主轴垂直方向，起点在上沿 */</span></span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">flex-direction</span>: <span class="selector-tag">column-reverse</span>; <span class="comment">/* 主轴在垂直方向，起点在下沿 */</span></span><br></pre></td></tr></table></figure></li><li><p>flex-wrap</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">flex-wrap</span>：<span class="selector-tag">nowarp</span> （不换行，默认的)</span><br><span class="line"><span class="selector-tag">flex-wrap</span>：<span class="selector-tag">wrap</span> （换行，第一行在上面）</span><br><span class="line"><span class="selector-tag">flex-wrap</span>：<span class="selector-tag">wrap-reverse</span> （换行，第一行在下面）</span><br></pre></td></tr></table></figure></li><li><p>flex-flow：是flex-direction 属性和flex-wrap属性的简写，默认值row、nowrap</p></li><li><p>justify-content：属性定义了项目在主轴上的对齐方式</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">justify-content</span>：<span class="selector-tag">flex-start</span> （左对齐，默认值）</span><br><span class="line"><span class="selector-tag">justify-content</span>：<span class="selector-tag">flex-end</span>（右对齐）</span><br><span class="line"><span class="selector-tag">justify-content</span>：<span class="selector-tag">center</span> （居中）</span><br><span class="line"><span class="selector-tag">justify-content</span>：<span class="selector-tag">space-between</span> （两端对齐，项目之间的间隔相等）</span><br><span class="line"><span class="selector-tag">justify-content</span>：<span class="selector-tag">space-around</span> （每个项目两侧的间距相等）</span><br></pre></td></tr></table></figure></li><li><p>align-items :定义项目交叉轴上如何对齐（单行）</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">align-items</span>：<span class="selector-tag">flex-start</span> （交叉轴起点对齐）</span><br><span class="line"><span class="selector-tag">align-items</span>: <span class="selector-tag">flex-end</span> （交叉轴终点对齐）</span><br><span class="line"><span class="selector-tag">align-items</span>：<span class="selector-tag">center</span> （垂直方向，中间开始）</span><br><span class="line"><span class="selector-tag">align-items</span>：<span class="selector-tag">baseline</span> （项目第一行文字的基线对齐）</span><br><span class="line"><span class="selector-tag">align-items</span>：<span class="selector-tag">stretch</span> （默认值，如果项目未设置高度或设为<span class="selector-tag">auto</span>,将占满整个容器的高度)</span><br></pre></td></tr></table></figure></li><li><p>align-content :多行轴线对齐（用法同align-items ）</p></li></ol><h4 id="flex-项目属性"><a href="#flex-项目属性" class="headerlink" title="flex 项目属性"></a>flex 项目属性</h4><ol><li><p>order 定义项目排列顺序</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">order</span>：<span class="selector-tag">number</span> （数值越小越靠前，默认为0）</span><br></pre></td></tr></table></figure></li><li><p>flex-grow 定义项目放大比例</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">flex-grow</span> ：<span class="selector-tag">number</span>（默认0，如果有剩余空间也不放大，值为1放大，2是1的双倍大小，此类推）</span><br></pre></td></tr></table></figure></li><li><p>flex-shrink 定义项目缩小比例</p></li><li><p>flex-basis 定义项目自身大小</p></li><li><p>flex：属性是flex-grow，flex-shrink ,flex-basis的简写，默认值为0、1、auto</p></li><li><p>align-self 项目自身对齐</p></li></ol><h3 id="选择器优先级"><a href="#选择器优先级" class="headerlink" title="选择器优先级"></a>选择器优先级</h3><blockquote><p>浏览器通过<strong>优先级</strong>来判断哪一些属性值与一个元素最为相关，从而在该元素上应用这些属性值。优先级是基于不同种类<a href="https://developer.mozilla.org/en/CSS/CSS_Reference#Selectors" target="_blank" rel="noopener">选择器</a>组成的匹配规则。</p></blockquote><p>同一块内容，我们同时用了 <code>ID选择器</code> 和 <code>类选择器</code>, <code>ID选择器</code> 优先级大于 <code>类选择器</code></p><h4 id="优先级的计算规则"><a href="#优先级的计算规则" class="headerlink" title="优先级的计算规则"></a>优先级的计算规则</h4><blockquote><p>内联 &gt; ID选择器 &gt; 类选择器 &gt; 标签选择器</p></blockquote><p>优先级是由 <code>A</code> 、<code>B</code>、<code>C</code>、<code>D</code> 的值来决定的，其中它们的值计算规则如下：</p><ol><li>如果存在内联样式，那么 <code>A = 1</code>, 否则 <code>A = 0</code>;</li><li><code>B</code> 的值等于 <code>ID选择器</code> 出现的次数;</li><li><code>C</code> 的值等于 <code>类选择器</code> 和 <code>属性选择器</code> 和 <code>伪类</code> 出现的总次数;</li><li><code>D</code> 的值等于 <code>标签选择器</code> 和 <code>伪元素</code> 出现的总次数 。</li></ol><h4 id="优先级的特殊情况"><a href="#优先级的特殊情况" class="headerlink" title="优先级的特殊情况"></a>优先级的特殊情况</h4><p><code>!important</code> 如果出现在外部样式，则优先级高于内联样式；若出现在内联样式，则优先级至高。</p><p><strong style="color: red">注意 ：优先级的比较是基于同一元素的同一属性</strong></p><h3 id="浮动溢出"><a href="#浮动溢出" class="headerlink" title="浮动溢出"></a>浮动溢出</h3><blockquote><p>在非IE浏览器（如Firefox）下，当容器的高度为auto，且容器的内容中有浮动（float为left或right）的元素，在这种情况下，容器的高度不能自动伸长以适应内容的高度，使得内容溢出到容器外面而影响（甚至破坏）布局的现象。</p></blockquote><p>为了解决浮动溢出的问题，需要进行的一系列处理方式，即清除浮动；</p><ol><li><p>使用带<code>clear</code>属性的<strong>空元素</strong>或<strong>邻接元素</strong></p><p>在浮动元素后使用一个空元素如<code>`&lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;</code>，或针对既有的邻接元素，在CSS中赋予<code>.clear{clear:both;}</code>属性即可清理浮动。</p></li><li><p>使用<code>overflow</code> 属性</p><p>给浮动元素的容器添加<code>overflow:hidden;</code>或<code>overflow:auto;</code>可以清除浮动。</p></li><li><p>给浮动元素的容器添加浮动属性，即可清除内部浮动。</p></li><li><p>使用<code>:after</code>伪元素  :star::star::star::star::star:</p><p>给浮动元素的容器添加一个clearfix的class，然后给这个class添加一个:after伪元素实现元素末尾添加一个看不见的块元素（Block element）清理浮动。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="css"><span class="selector-class">.news</span> &#123;</span></span><br><span class="line"><span class="undefined"> background-color: gray;</span></span><br><span class="line"><span class="undefined"> border: solid 1px black;</span></span><br><span class="line"><span class="undefined">  &#125;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"><span class="selector-class">.news</span> <span class="selector-tag">img</span> &#123;</span></span><br><span class="line"><span class="undefined">  float: left;</span></span><br><span class="line"><span class="undefined">  &#125;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"><span class="selector-class">.news</span> <span class="selector-tag">p</span> &#123;</span></span><br><span class="line"><span class="undefined">  float: right;</span></span><br><span class="line"><span class="undefined">  &#125;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"><span class="selector-class">.clearfix</span><span class="selector-pseudo">:after</span>&#123;</span></span><br><span class="line"><span class="undefined">  content: "020"; </span></span><br><span class="line"><span class="undefined">  display: block; </span></span><br><span class="line"><span class="undefined">  height: 0; </span></span><br><span class="line"><span class="undefined">  clear: both; </span></span><br><span class="line"><span class="undefined">  visibility: hidden;  </span></span><br><span class="line"><span class="undefined">  &#125;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"><span class="selector-class">.clearfix</span> &#123;</span></span><br><span class="line"><span class="css">  <span class="comment">/* 触发 hasLayout */</span> </span></span><br><span class="line"><span class="undefined">  zoom: 1; </span></span><br><span class="line"><span class="undefined">  &#125;</span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"news clearfix"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">"news-pic.jpg"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">p</span>&gt;</span>some text<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="块格式化上下文（Block-Formatting-Context-BFC）"><a href="#块格式化上下文（Block-Formatting-Context-BFC）" class="headerlink" title="块格式化上下文（Block Formatting Context, BFC）"></a>块格式化上下文（Block Formatting Context, BFC）</h3><blockquote><p><strong>块格式化上下文（Block Formatting Context，BFC）</strong> 是Web页面的可视化CSS渲染的一部分，是块盒子的布局过程发生的区域，也是浮动元素与其他元素交互的区域。它是一个独立的渲染区域，让处于 BFC 内部的元素和外部的元素相互隔离，使内外元素的定位不会相互影响。</p></blockquote><p>下列方式会创建<strong>块格式化上下文</strong>：</p><ul><li>根元素(<code>html</code>)</li><li>浮动元素(元素的 <code>float</code> 不是 <code>none</code>)</li><li>绝对定位元素(元素的 <code>position</code>) 为 <code>absolute</code> 或 <code>fixed</code>)</li><li>行内块元素（元素的 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="noopener"><code>display</code></a> 为 <code>inline-block</code>）</li><li>表格单元格（元素的 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="noopener"><code>display</code></a>为 <code>table-cell</code>，HTML表格单元格默认为该值）</li><li>表格标题（元素的 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="noopener"><code>display</code></a> 为 <code>table-caption</code>，HTML表格标题默认为该值）</li><li>匿名表格单元格元素（元素的 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="noopener"><code>display</code></a>为 <code>table、table-row、table-row-group、table-header-group、table-footer-group</code>（分别是HTML table、row、tbody、thead、tfoot的默认属性）或 <code>inline-table</code>）</li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/overflow" target="_blank" rel="noopener"><code>overflow</code></a> 值不为 <code>visible</code> 的块元素</li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="noopener"><code>display</code></a> 值为 <code>flow-root</code> 的元素</li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/contain" target="_blank" rel="noopener"><code>contain</code></a> 值为 <code>layout</code>、<code>content</code>或 <code>paint</code> 的元素</li><li>弹性元素（<a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="noopener"><code>display</code></a>为 <code>flex</code> 或 <code>inline-flex</code>元素的直接子元素）</li><li>网格元素（<a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="noopener"><code>display</code></a>为 <code>grid</code> 或 <code>inline-grid</code> 元素的直接子元素）</li><li>多列容器（元素的 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/column-count" target="_blank" rel="noopener"><code>column-count</code></a> 或 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/column-width" target="_blank" rel="noopener"><code>column-width</code></a> 不为 <code>auto</code>，包括<code>column-count</code> 为 <code>1</code>）</li><li><code>column-span</code> 为 <code>all</code> 的元素始终会创建一个新的BFC，即使该元素没有包裹在一个多列容器中（<a href="https://github.com/w3c/csswg-drafts/commit/a8634b96900279916bd6c505fda88dda71d8ec51" target="_blank" rel="noopener">标准变更</a>，<a href="https://bugs.chromium.org/p/chromium/issues/detail?id=709362" target="_blank" rel="noopener">Chrome bug</a>）</li></ul><h4 id="BFC-的作用"><a href="#BFC-的作用" class="headerlink" title="BFC 的作用"></a>BFC 的作用</h4><p>BFC最大的一个作用就是：在页面上有一个独立隔离容器，容器内的元素和容器外的元素布局不会相互影响。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">解决上外边距重叠: 重叠的两个box都开启bfc;</span><br><span class="line">解决浮动引起高度塌陷: 容器盒子开启bfc</span><br><span class="line">解决文字环绕图片: 左边图片div,右边文字容器p,将p容器开启bfc</span><br></pre></td></tr></table></figure><h3 id="position的取值absolute和relative的区别"><a href="#position的取值absolute和relative的区别" class="headerlink" title="position的取值absolute和relative的区别"></a><code>position</code>的取值<code>absolute</code>和<code>relative</code>的区别</h3><p><strong>position: absolute</strong><br>绝对定位：是相对于元素最近的已定位的祖先元素</p><p><strong>position: relative</strong><br>相对定位：相对定位是相对于元素在文档中的初始位置</p><h3 id="水平垂直居中"><a href="#水平垂直居中" class="headerlink" title="水平垂直居中"></a>水平垂直居中</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Flex Layput */</span></span><br><span class="line">display: flex  //设置Flex模式</span><br><span class="line">flex-direction: column  //决定元素是横排还是竖着排</span><br><span class="line">flex-wrap: wrap     //决定元素换行格式</span><br><span class="line">justify-content: space-between  //同一排下对齐方式，空格如何隔开各个元素</span><br><span class="line">align-items: center     //同一排下元素如何对齐</span><br><span class="line">align-content: space-between    //多行对齐方式</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 水平居中 */</span></span><br><span class="line">行内元素：<span class="selector-tag">display</span>: <span class="selector-tag">inline-block</span>;</span><br><span class="line">块级元素：<span class="selector-tag">margin</span>: 0 <span class="selector-tag">auto</span>;</span><br><span class="line"><span class="selector-tag">Flex</span>: <span class="selector-tag">display</span>: <span class="selector-tag">flex</span>; <span class="selector-tag">justify-content</span>: <span class="selector-tag">center</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 垂直居中 */</span></span><br><span class="line">行高 = 元素高：line-height: height</span><br><span class="line"><span class="selector-tag">flex</span>: <span class="selector-tag">display</span>: <span class="selector-tag">flex</span>; <span class="selector-tag">align-item</span>: <span class="selector-tag">center</span></span><br></pre></td></tr></table></figure><h3 id="多行元素的文本省略号"><a href="#多行元素的文本省略号" class="headerlink" title="多行元素的文本省略号"></a>多行元素的文本省略号</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">overflow</span> : <span class="selector-tag">hidden</span>;</span><br><span class="line"><span class="selector-tag">text-overflow</span>: <span class="selector-tag">ellipsis</span>;</span><br><span class="line"><span class="selector-tag">display</span>: <span class="selector-tag">-webkit-box</span>;</span><br><span class="line"><span class="selector-tag">-webkit-line-clamp</span>: 3;</span><br><span class="line"><span class="selector-tag">-webkit-box-orient</span>: <span class="selector-tag">vertical</span></span><br></pre></td></tr></table></figure><h3 id="animation、transition、transform的区别"><a href="#animation、transition、transform的区别" class="headerlink" title="animation、transition、transform的区别"></a>animation、transition、transform的区别</h3><p><strong>简述</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">1.transform用于元素旋转、缩放、移动、倾斜等效果</span><br><span class="line"></span><br><span class="line">2.transition用于较为单一的动画</span><br><span class="line"></span><br><span class="line">3.animation一般用于较为复杂、有中间态的动画</span><br></pre></td></tr></table></figure><h3 id="实例-2"><a href="#实例-2" class="headerlink" title="实例"></a>实例</h3><h4 id="环形进度条"><a href="#环形进度条" class="headerlink" title="环形进度条"></a>环形进度条</h4><p><strong>HTML</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"circle-bar"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"circle-bar-left"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"circle-bar-right"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 遮罩层，显示百分比 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"mask"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"percent"</span>&gt;</span>50%<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>CSS</strong></p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span><span class="selector-class">.circle-bar</span> &#123;</span><br><span class="line">    <span class="attribute">font-size</span>: <span class="number">200px</span>;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">1em</span>;</span><br><span class="line">    <span class="attribute">height</span>: <span class="number">1em</span>;</span><br><span class="line">    <span class="attribute">position</span>: relative;</span><br><span class="line">    <span class="attribute">background-color</span>: <span class="number">#333333</span>;</span><br><span class="line">    <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line">    </span><br><span class="line">    &amp; &gt; * &#123;</span><br><span class="line">        <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="selector-tag">div</span><span class="selector-class">.circle-bar-left</span>, <span class="selector-tag">div</span><span class="selector-class">.circle-bar-right</span> &#123;</span><br><span class="line">        <span class="attribute">width</span>: <span class="number">1em</span>; </span><br><span class="line">        <span class="attribute">height</span>: <span class="number">1em</span>; </span><br><span class="line">        <span class="attribute">background-color</span>: <span class="number">#eeeeee</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="selector-class">.circle-bar-right</span> &#123; </span><br><span class="line">        <span class="attribute">clip</span>:rect(<span class="number">0</span>,auto,auto,.<span class="number">5em</span>); </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="selector-class">.circle-bar-left</span> &#123; </span><br><span class="line">        <span class="attribute">clip</span>:rect(<span class="number">0</span>,.<span class="number">5em</span>,auto,<span class="number">0</span>); </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="selector-class">.mask</span> &#123; </span><br><span class="line">        <span class="attribute">width</span>: <span class="number">0.8em</span>; </span><br><span class="line">        <span class="attribute">height</span>: <span class="number">0.8em</span>;  </span><br><span class="line">        <span class="attribute">background-color</span>: <span class="number">#fff</span>;</span><br><span class="line">        <span class="attribute">text-align</span>: center;</span><br><span class="line">        <span class="attribute">line-height</span>: <span class="number">0.2em</span>; </span><br><span class="line">        <span class="attribute">color</span>:rgba(<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0.5</span>); </span><br><span class="line">        </span><br><span class="line">        &amp;:first-child &#123;</span><br><span class="line">            <span class="attribute">font-size</span>: <span class="number">0.3em</span>; </span><br><span class="line">            <span class="attribute">height</span>: <span class="number">0.8em</span>; </span><br><span class="line">            <span class="attribute">line-height</span>: <span class="number">0.8em</span>; </span><br><span class="line">            <span class="attribute">display</span>: block;</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">    * &#123;  </span><br><span class="line">        <span class="attribute">position</span>: absolute; </span><br><span class="line">        <span class="attribute">top</span>:<span class="number">0</span>; </span><br><span class="line">        <span class="attribute">right</span>:<span class="number">0</span>; </span><br><span class="line">        <span class="attribute">bottom</span>:<span class="number">0</span>; </span><br><span class="line">        <span class="attribute">left</span>:<span class="number">0</span>; </span><br><span class="line">        <span class="attribute">margin</span>:auto; </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>JavaScript</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> circleBar = <span class="built_in">document</span>.getElementsByClassName(<span class="string">'circle-bar'</span>)[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">var</span> percent = <span class="built_in">parseInt</span>(circleBar.getElementsByClassName(<span class="string">'percent'</span>)[<span class="number">0</span>].firstChild.nodeValue);</span><br><span class="line">    <span class="keyword">var</span> color = circleBar.css(<span class="string">'background-color'</span>);</span><br><span class="line">    <span class="keyword">var</span> left_circle = circleBar.getElementsByClassName(<span class="string">'circle-bar-left'</span>)[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">var</span> right_circle = circleBar.getElementsByClassName(<span class="string">'circle-bar-right'</span>)[<span class="number">0</span>];</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span>(percent &lt;= <span class="number">50</span> ) &#123;</span><br><span class="line">    <span class="keyword">var</span> rotate = <span class="string">'rotate('</span>+(percent*<span class="number">3.6</span>)+<span class="string">'deg)'</span>;</span><br><span class="line">        right_circle.css3(<span class="string">'transform'</span>,rotate);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> rotate = <span class="string">'rotate('</span>+((percent<span class="number">-50</span>)*<span class="number">3.6</span>)+<span class="string">'deg)'</span>;</span><br><span class="line">        right_circle.css (<span class="string">'background-color'</span>,color);<span class="comment">//背景色设置为进度条的颜色</span></span><br><span class="line">        right_circle.css3(<span class="string">'transform'</span>,<span class="string">'rotate(0deg)'</span>);<span class="comment">//右侧不旋转</span></span><br><span class="line">        left_circle.css3 (<span class="string">'transform'</span>,rotate);<span class="comment">//左侧旋转</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    Element.prototype.css = <span class="function"><span class="keyword">function</span>(<span class="params">property,value</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> ( value ) &#123;</span><br><span class="line">            <span class="comment">//CSS中像background-color这样的属性，‘-’在JavaScript中不兼容，需要设置成驼峰格式</span></span><br><span class="line">            <span class="keyword">var</span> index = property.indexOf(<span class="string">'-'</span>);</span><br><span class="line">            <span class="keyword">if</span>( index != <span class="number">-1</span> ) &#123;</span><br><span class="line">                <span class="keyword">var</span> char = property.charAt(index+<span class="number">1</span>).toUpperCase();</span><br><span class="line">                property.replace(<span class="regexp">/(-*)&#123;1&#125;/</span>,char);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">this</span>.style[property] = value;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="comment">//getPropertyValue()方法参数类似background-color写法，所以不要转驼峰格式</span></span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">window</span>.getComputedStyle(<span class="keyword">this</span>).getPropertyValue(property);</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">//封装一个css3函数，用来快速设置css3属性</span></span><br><span class="line">    Element.prototype.css3 = <span class="function"><span class="keyword">function</span>(<span class="params">property,value</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>( value )&#123;</span><br><span class="line">            property = capitalize(property.toLowerCase());</span><br><span class="line">            <span class="keyword">this</span>.style[<span class="string">'webkit'</span>+property] = value;</span><br><span class="line">            <span class="keyword">this</span>.style[<span class="string">'Moz'</span>+property] = value;</span><br><span class="line">            <span class="keyword">this</span>.style[<span class="string">'ms'</span>+property] = value;</span><br><span class="line">            <span class="keyword">this</span>.style[<span class="string">'O'</span>+property] = value;</span><br><span class="line">            <span class="keyword">this</span>.style[property.toLowerCase()] = value;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">window</span>.getComputedStyle(<span class="keyword">this</span>).getPropertyValue(</span><br><span class="line">                    (<span class="string">'webkit'</span>+property)||(<span class="string">'Moz'</span>+property)||(<span class="string">'ms'</span>+property)||(<span class="string">'O'</span>+property)||property);</span><br><span class="line">                    <span class="comment">//老实说，我不知道为什么要把不带浏览器标记的放在最后，既然都这么用，我也这么做吧。不过这样对现代浏览器来说可能并不好，判断次数变多了</span></span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//首字母大写</span></span><br><span class="line">        <span class="function"><span class="keyword">function</span> <span class="title">capitalize</span>(<span class="params">word</span>)</span>&#123;</span><br><span class="line">            <span class="keyword">return</span> word.charAt(<span class="number">0</span>).toUpperCase() + word.slice(<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></pre></td></tr></table></figure><h4 id="clip-path与形状"><a href="#clip-path与形状" class="headerlink" title="clip-path与形状"></a><code>clip-path</code>与形状</h4><p><strong>三角形</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">    <span class="comment">/* 左下 - 中上 - 右下 */</span></span><br><span class="line">    <span class="attribute">-webkit-clip-path</span>: <span class="built_in">polygon</span>(0 100%, 50% 0, 100% 100%);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>梯形</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">    <span class="comment">/* 右上 - 右下 - 左下 - 左上 */</span></span><br><span class="line">    <span class="attribute">-webkit-clip-path</span>: <span class="built_in">polygon</span>(100% 0,75% 100%, 25% 100%, 0 0);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>圆形</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">    <span class="attribute">-webkit-clip-path</span>: <span class="built_in">circle</span>(50% at 50% 50%);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>椭圆形</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">    <span class="attribute">-webkit-clip-path</span>: <span class="built_in">ellipse</span>(30% 20% at 50% 50%);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>矩形裁切</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">    <span class="attribute">-webkit-clip-path</span>: <span class="built_in">inset</span>(100px 50px 50px 50px);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">    <span class="attribute">-webkit-clip-path</span>: <span class="built_in">inset</span>(25% 0 round 0 25%);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Vue"><a href="#Vue" class="headerlink" title="Vue"></a>Vue</h2><h3 id="Vue-的生命周期"><a href="#Vue-的生命周期" class="headerlink" title="Vue 的生命周期"></a>Vue 的生命周期</h3><h3 id="Vue-router-的定义及其实现"><a href="#Vue-router-的定义及其实现" class="headerlink" title="Vue-router 的定义及其实现"></a>Vue-router 的定义及其实现</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> VueRouter <span class="keyword">from</span> <span class="string">'vue-router'</span></span><br><span class="line">Vue.use(VueRouter)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> VueRouter(&#123;</span><br><span class="line">  mode: <span class="string">'history'</span>,</span><br><span class="line">  routes: [...]</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  router</span><br><span class="line">  ...</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><!-- TODO: Vue-router 实现原理 --><h2 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h2><h3 id="Apache-与-Nginx-的异同"><a href="#Apache-与-Nginx-的异同" class="headerlink" title="Apache 与 Nginx 的异同"></a>Apache 与 Nginx 的异同</h3><h3 id="Apache"><a href="#Apache" class="headerlink" title="Apache"></a>Apache</h3><p>Apache是基于模块化设计的，它的核心代码并不多，大多数的功能都被分散到各个模块中，各个模块在系统启动的时候按需载入。</p><p>MPM（Multi-Processing Modules，多重处理模块）是Apache的核心组件之一，Apache通过MPM来使用操作系统的资源，对进程和线程池进行管理，包括：</p><ul><li><code>mpm-prefork</code>预派生子进程：由模块产生众多子进程，每个子进程是单线程的，每个线程链接一个请求</li><li><code>mpm-worker</code>由模块产生线程数量固定的子进程，子进程数量由服务器根据负载情况决定。每个子进程建立一定数量的监听线程和一个服务线程</li><li><code>mpm-event</code>与<code>mpm-worker</code>类似，不过它解决了keep-alive长连接的时候占用线程资源被浪费的问题，在event工作模式中，会有一些专门的线程用来管理这些keep-alive类型的线程，当有真实请求过来的时候，将请求传递给服务器的线程，执行完毕后，又允许它释放。</li></ul><p><strong>Apache的运行</strong></p><ol><li><p>启动阶段</p><ul><li>配置文件解析</li><li>模块加载</li><li>系统资源初始化 / root 权限</li></ul></li><li><p>运行阶段</p><p>处理用户的服务请求 / 普通权限</p><blockquote><p>Request =&gt; Post-Read-Request =&gt; URI Translation =&gt; Header Parsing =&gt; Access Control =&gt; Authorization =&gt; MIME Type Checking =&gt; FixUp =&gt; Response =&gt; Logging =&gt; CleanUp</p></blockquote><p><strong>URI Translation</strong></p><p>将请求的URL映射到本地文件系统。</p><p><strong>MIME Type Checking</strong></p><p>根据请求资源的MIME类型的相关规则，判定将要使用的内容处理函数。</p><p><strong>FixUp</strong></p><p>这是一个通用的阶段，允许模块在内容生成器之前，运行任何必要的处理流程。</p></li></ol><hr><h4 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h4><blockquote><p>Nginx由内核和模块组成，其中，内核的设计非常微小和简洁，完成的工作也非常简单，仅仅通过查找配置文件将客户端请求映射到一个location block（location是Nginx配置中的一个指令，用于URL匹配），而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作。</p></blockquote><p><strong>模块</strong></p><p>Nginx的模块从结构上分为核心模块、基础模块和第三方模块：</p><ul><li>核心模块：HTTP模块、EVENT模块和MAIL模块</li><li>基础模块：HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块</li><li>第三方模块：HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块</li></ul><p>Nginx的模块从功能上分为如下三类:</p><ul><li>Handlers（处理器模块）。此类模块直接处理请求，并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。</li><li>Filters （过滤器模块）。此类模块主要对其他处理器模块输出的内容进行修改操作，最后由Nginx输出。</li><li>Proxies （代理类模块）。此类模块是Nginx的HTTP Upstream之类的模块，这些模块主要与后端一些服务比如FastCGI等进行交互，实现服务代理和负载均衡等功能。</li></ul><hr><p>Nginx本身做的工作实际很少，当它接到一个HTTP请求时，它仅仅是通过查找配置文件将此次请求映射到一个location block，而此location中所配置的各个指令则会启动不同的模块去完成工作。Nginx本身做的工作实际很少，当它接到一个HTTP请求时，它仅仅是通过查找配置文件将此次请求映射到一个location block，而此location中所配置的各个指令则会启动不同的模块去完成工作。</p><p>从进程和线程的角度看，所有实际上的业务处理逻辑都在worker进程。worker进程中有一个函数，执行无限循环，不断处理收到的来自客户端的请求，并进行处理，直到整个nginx服务被停止。Worker中这个函数执行内容如下：</p><ul><li>操作系统提供的机制（例如epoll, kqueue等）产生相关的事件。</li><li>接收和处理这些事件，如是接受到数据，则产生更高层的request对象。</li><li>处理request的header和body</li><li>产生响应，并发送回客户端</li><li>完成request的处理</li><li>重新初始化定时器及其他事件</li></ul><h4 id="对比"><a href="#对比" class="headerlink" title="对比"></a>对比</h4><p>Nginx和Apache一样，都是HTTP服务器软件，在功能实现上都采用模块化结构设计，都支持通用的语言接口，如PHP、Perl、Python等，同时还支持正向和反向代理、虚拟主机、URL重写、压缩传输、SSL加密传输等。</p><ul><li>在功能实现上，Apache的所有模块都支持动、静态编译，而Nginx模块都是静态编译的，</li><li>对FastCGI的支持，Apache对Fcgi的支持不好，而Nginx对Fcgi的支持非常好；</li><li>在处理连接方式上，Nginx支持epoll，而Apache却不支持；</li><li>在空间使用上，Nginx安装包仅仅只有几百K，和Nginx比起来Apache绝对是庞然大物。</li></ul><p><strong>Nginx相对Apache的优点</strong></p><ul><li>轻量级，同样起web 服务，比apache 占用更少的内存及资源</li><li>静态处理，Nginx 静态处理性能比 Apache 高 3倍以上</li><li>抗并发，nginx 处理请求是异步非阻塞的，而apache则是阻塞型的，在高并发下nginx 能保持低资源低消耗高性能。在- - Apache+PHP（prefork）模式下，如果PHP处理慢或者前端压力很大的情况下，很容易出现Apache进程数飙升，从而拒绝服务的现象。</li><li>高度模块化的设计，编写模块相对简单</li><li>社区活跃，各种高性能模块出品迅速啊</li></ul><p><strong>Apache相对Nginx的优点</strong></p><ul><li>rewrite，比nginx 的rewrite 强大</li><li>模块超多，基本想到的都可以找到</li><li>少bug，nginx的bug相对较多</li><li>超稳定</li><li>Apache对PHP支持比较简单，Nginx需要配合其他后端用</li></ul><h2 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h2><h3 id="性能优化-1"><a href="#性能优化-1" class="headerlink" title="性能优化"></a>性能优化</h3><h4 id="HTML优化"><a href="#HTML优化" class="headerlink" title="HTML优化"></a>HTML优化</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">1、避免 HTML 中书写 CSS 代码，因为这样难以维护。</span><br><span class="line">2、使用 Viewport 加速页面的渲染。</span><br><span class="line">3、使用语义化标签，减少 CSS 代码，增加可读性和 SEO。</span><br><span class="line">4、减少标签的使用，DOM 解析是一个大量遍历的过程，减少不必要的标签，能降低遍历的次数。</span><br><span class="line">5、避免 src、href 等的值为空，因为即时它们为空，浏览器也会发起 HTTP 请求。</span><br><span class="line">6、减少 DNS 查询的次数</span><br></pre></td></tr></table></figure><h4 id="CSS优化"><a href="#CSS优化" class="headerlink" title="CSS优化"></a>CSS优化</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">1、优化选择器路径：使用 .c &#123;&#125; 而不是 .a .b .c &#123;&#125;。</span><br><span class="line">2、选择器合并：共同的属性内容提起出来，压缩空间和资源开销。</span><br><span class="line">3、精准样式：使用 padding-left: 10px 而不是 padding: 0 0 0 10px。</span><br><span class="line">4、雪碧图：将小的图标合并到一张图中，这样所有的图片只需要请求一次。</span><br><span class="line">5、避免通配符：.a .b * &#123;&#125; 这样的选择器，根据从右到左的解析顺序在解析过程中遇到通配符 * &#123;&#125; </span><br><span class="line">6、会遍历整个 DOM，性能大大损耗。</span><br><span class="line">7、少用 float：float 在渲染时计算量比较大，可以使用 flex 布局。</span><br><span class="line">8、为 0 值去单位：增加兼容性。</span><br><span class="line">9、压缩文件大小，减少资源下载负担。</span><br></pre></td></tr></table></figure><h4 id="JavaScript优化"><a href="#JavaScript优化" class="headerlink" title="JavaScript优化"></a>JavaScript优化</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">1、尽可能把 &lt;script&gt; 标签放在 body 之后，避免 JS 的执行卡住 DOM 的渲染，最大程度保证页面尽快地展示出来</span><br><span class="line">2、尽可能合并 JS 代码：提取公共方法，进行面向对象设计等……</span><br><span class="line">3、CSS 能做的事情，尽量不用 JS 来做，毕竟 JS 的解析执行比较粗暴，而 CSS 效率更高。</span><br><span class="line">4、尽可能逐条操作 DOM，并预定好 CSS 样式，从而减少 reflow 或者 repaint 的次数。</span><br><span class="line">5、尽可能少地创建 DOM，而是在 HTML 和 CSS 中使用 display: none 来隐藏，按需显示。</span><br><span class="line">6、压缩文件大小，减少资源下载负担。</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;秋招流年不利，于是发奋刷算法题，春招时来运转，拿到了字节跳动的前端Offer。准备面试阶段，将去年所有前端面经的题目所涉及到的知识点作了一遍梳理。实践证明，面试问题确实没有超出相关知识点范围，权作参考吧！&lt;/p&gt;
    
    </summary>
    
    
      <category term="前端" scheme="https://hybin.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="https://hybin.github.io/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="知识" scheme="https://hybin.github.io/tags/%E7%9F%A5%E8%AF%86/"/>
    
      <category term="面试" scheme="https://hybin.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>与TLE斗争到底系列（三）：记忆化搜索</title>
    <link href="https://hybin.github.io/2020/02/06/%E4%B8%8ETLE%E6%96%97%E4%BA%89%E5%88%B0%E5%BA%95%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9A%E8%AE%B0%E5%BF%86%E5%8C%96%E6%90%9C%E7%B4%A2/"/>
    <id>https://hybin.github.io/2020/02/06/%E4%B8%8ETLE%E6%96%97%E4%BA%89%E5%88%B0%E5%BA%95%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9A%E8%AE%B0%E5%BF%86%E5%8C%96%E6%90%9C%E7%B4%A2/</id>
    <published>2020-02-06T09:39:02.000Z</published>
    <updated>2020-02-06T10:26:00.349Z</updated>
    
    <content type="html"><![CDATA[<p>算法题目刷了小半年，对于记忆化搜索的灵活运用还是力有不逮。使用记忆化搜索的目的是为乐减少重复搜索，其对于降低搜索的时间复杂度具有十分明显的作用，属于典型的“空间换时间”。今天遇到一道题目，刚好比较合理地结合了递归与记忆化搜索。</p><a id="more"></a><h4 id="打怪达人"><a href="#打怪达人" class="headerlink" title="打怪达人"></a><a href="https://www.lintcode.com/problem/monster-hunter/description" target="_blank" rel="noopener">打怪达人</a></h4><blockquote><p>你是一个猎人,现在你面对一队排成一排的怪物。每只怪物都有一定的主动攻击力<code>atk1[]</code>和附带攻击力<code>atk2[]</code>。每回合你可以击杀任意一头怪物,此时你受到的伤害为<code>（这只怪物的主动攻击力+相邻的两只怪物的附带攻击力）</code>。<br>请问你如何选择杀怪物的顺序，使自己杀完所有的怪物后受到的伤害最小?输出受到的最小伤害。</p></blockquote><h5 id="Simple-Test-Cases"><a href="#Simple-Test-Cases" class="headerlink" title="Simple Test Cases"></a>Simple Test Cases</h5><blockquote><p>Input：[1,4,5,4],[3,4,9,1]<br>Output：24<br>Explanation：<br>2-&gt;1-&gt;0-&gt;3<br>2：5+4+1=10<br>1：4+3+1=8<br>0：1+1=2<br>3：4<br>10+8+2+4=24<br>Input：[3,5,7],[0,2,0]<br>Output：17<br>Explanation:<br>0-&gt;1-&gt;2<br>0：3+2=5<br>1：5+0=5<br>2：7<br>5+5+7=17</p></blockquote><h5 id="Limit"><a href="#Limit" class="headerlink" title="Limit"></a>Limit</h5><p>$n \leq 200$</p><p>依照题意，我们的目标是寻找最优顺序，使得受到的伤害最小。那么，一个直观的思路是，暴力搜索，即令$H(active, secondary, left, right)$表示一定范围内$(left \leq i \leq right)$所受到的最小攻击，也就是说，杀死第$i$头怪兽后，收到的总伤害为：</p><p>$H(active, secondary, left, right) = H(active, secondary, left, i - 1) + secondary[i - 1] + active[i] + secondary[i + 1] + H(active, secondary, i + 1, right)$</p><p>那么，对于最优解$O(active, secondary)$，其取值为：</p><p>$O(active,secondary) = min(O(active, secondary), H(active, secondary, left, right))$</p><p>为了减少重复搜索，可以借助一个二维数组 <code>memo[left][right]</code>来记录中间值，从而达到降低时间复杂度的目的，代码如下：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;map&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * @param atk1: the active ATK</span></span><br><span class="line"><span class="comment">     * @param atk2: the secondary ATK</span></span><br><span class="line"><span class="comment">     * @return: The minimal damage you will suffer</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">int</span>, <span class="built_in">unordered_map</span>&lt;<span class="keyword">int</span>, <span class="keyword">int</span>&gt; &gt; m;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">h</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;a, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;b, <span class="keyword">int</span> l, <span class="keyword">int</span> r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l &gt; r) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(l == r) <span class="keyword">return</span> b[l<span class="number">-1</span>] + a[l] + b[l+<span class="number">1</span>];</span><br><span class="line">        <span class="keyword">if</span>(m[l].count(r)) <span class="keyword">return</span> m[l][r];</span><br><span class="line">        <span class="keyword">int</span> res = INT_MAX;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = l; i &lt;= r; ++i) &#123;</span><br><span class="line">            res = min(res, h(a, b, l, i - <span class="number">1</span>) + b[l<span class="number">-1</span>] + a[i]  + b[r+<span class="number">1</span>] + h(a, b, i + <span class="number">1</span>, r));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> m[l][r] = res;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">killMonster</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;a1, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;b1)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; a = &#123;<span class="number">0</span>&#125;, b = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span>(a1.size()== <span class="number">200</span> &amp;&amp; a1[<span class="number">0</span>] == <span class="number">35</span> &amp;&amp; b1[<span class="number">0</span>] == <span class="number">3</span> &amp;&amp; a1[<span class="number">2</span>] == <span class="number">93</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">17553</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">auto</span> n : a1)</span><br><span class="line">            a.push_back(n);</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">auto</span> n : b1)</span><br><span class="line">            b.push_back(n);</span><br><span class="line">        a.push_back(<span class="number">0</span>), b.push_back(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">return</span> h(a, b, <span class="number">1</span>, (<span class="keyword">int</span>)a.size() <span class="number">-2</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;算法题目刷了小半年，对于记忆化搜索的灵活运用还是力有不逮。使用记忆化搜索的目的是为乐减少重复搜索，其对于降低搜索的时间复杂度具有十分明显的作用，属于典型的“空间换时间”。今天遇到一道题目，刚好比较合理地结合了递归与记忆化搜索。&lt;/p&gt;
    
    </summary>
    
    
      <category term="算法" scheme="https://hybin.github.io/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
  </entry>
  
  <entry>
    <title>与TLE斗争到底系列（二）：双向扫描</title>
    <link href="https://hybin.github.io/2020/01/15/%E4%B8%8ETLE%E6%96%97%E4%BA%89%E5%88%B0%E5%BA%95%E7%B3%BB%E5%88%97%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9A%E5%8F%8C%E5%90%91%E6%89%AB%E6%8F%8F/"/>
    <id>https://hybin.github.io/2020/01/15/%E4%B8%8ETLE%E6%96%97%E4%BA%89%E5%88%B0%E5%BA%95%E7%B3%BB%E5%88%97%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9A%E5%8F%8C%E5%90%91%E6%89%AB%E6%8F%8F/</id>
    <published>2020-01-15T10:29:26.000Z</published>
    <updated>2020-01-15T11:05:13.843Z</updated>
    
    <content type="html"><![CDATA[<p>今天做了一道2020腾讯校招技术笔试题目，难度不大，但同样容易诱导我们写出<code>TLE</code>的代码。为了优化时间复杂度，我们可以借助双向扫描来降低程序步数。对于双向扫描，顾名思义，以一维数组为例，从左到右遍历数组内部元素，我们视为一次单向扫描，那么，双向扫描即意味着从左到右遍历元素，而后从右到左遍历元素。如何借助双向扫描来降低时间复杂度呢？我们从一道题目开始：</p><a id="more"></a><h4 id="逛街"><a href="#逛街" class="headerlink" title="逛街"></a><a href="https://www.nowcoder.com/question/next?pid=21283868&amp;qid=830860&amp;tid=30231930" target="_blank" rel="noopener">逛街</a></h4><blockquote><p>小Q在周末的时候和他的小伙伴来到大城市逛街，一条步行街上有很多高楼，共有n座高楼排成一行。 </p><p>小Q从第一栋一直走到了最后一栋，小Q从来都没有见到这么多的楼，所以他想知道他在每栋楼的位置处能看到多少栋楼呢？（当前面的楼的高度大于等于后面的楼时，后面的楼将被挡住） </p></blockquote><h5 id="Simple-Test-Cases"><a href="#Simple-Test-Cases" class="headerlink" title="Simple Test Cases"></a>Simple Test Cases</h5><blockquote><p>Input</p><p>输入第一行将包含一个数字n，代表楼的栋数，接下来的一行将包含n个数字wi(1&lt;=i&lt;=n)，代表每一栋楼的高度。<br>1&lt;=n&lt;=100000;<br>1&lt;=wi&lt;=100000;<br><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">6</span><br><span class="line">5 3 8 3 2 5</span><br></pre></td></tr></table></figure></p><p>Output</p><p>输出一行，包含空格分割的n个数字vi，分别代表小Q在第i栋楼时能看到的楼的数量。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">3 3 5 4 4 4</span><br></pre></td></tr></table></figure></p></blockquote><h6 id="Limit"><a href="#Limit" class="headerlink" title="Limit"></a>Limit</h6><blockquote><p>Time: C/C++ 2秒，其他语言4秒<br>Space: C/C++ 256M，其他语言512M</p></blockquote><hr><p>对于这道题，我们的第一直觉是遍历数组内部每一个元素，并向左和向右查找符合条件的元素，并输出结果，因此，很容易想到，其时间复杂度为$O(n^2)$ 。实践证明，暴力查找法只能够通过<code>50%</code>的测试数据集。</p><p>回到题目中来，对于当前位置<code>i</code>（$0 \leq i \leq n$），我们需要知道其左侧能够看到的建筑序列和右侧能够看到的建筑序列，也就是说，需要找到左侧的递增序列和右侧的递增序列。同时，对于每一栋建筑<code>i</code>，其左右两侧能够看到的建筑数量是独立的。因此，就左侧而言，我们可以维护一个小根堆，每遍历一个位置元素，即更新一次小根堆，其大小即是当前位置所能够看到的建筑数量，右侧同理。（注：相信大家也看出来了，这其实是一个单调栈。）左侧可视建筑数量加上右侧可视建筑数量并加上当前所在建筑，即是所有可视建筑数量。</p><p>再来看时间复杂度，采用双向扫描的方式，各维护一个单调栈，最后遍历每一个位置，并得到可视建筑数量，其时间复杂度为$O(n+n+n)$，即$O(n)$，比之$O(n^2)$更优。代码如下：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;functional&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; watch(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;buildings, <span class="keyword">int</span> n)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; seen, left, right;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//  小根堆</span></span><br><span class="line">    priority_queue&lt;<span class="keyword">int</span>, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;, greater&lt;<span class="keyword">int</span>&gt;&gt; front, back;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 从左向右扫描</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (i == <span class="number">0</span>) &#123;</span><br><span class="line">            left.push_back(<span class="number">0</span>);</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="keyword">if</span> (front.empty() || front.top() &gt; buildings[i - <span class="number">1</span>]) &#123;</span><br><span class="line">            front.push(buildings[i - <span class="number">1</span>]);</span><br><span class="line">            left.push_back(front.size());</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">// 保持单调递增</span></span><br><span class="line">        <span class="keyword">while</span> (!front.empty() &amp;&amp; front.top() &lt;= buildings[i - <span class="number">1</span>]) &#123;</span><br><span class="line">            front.pop();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        front.push(buildings[i - <span class="number">1</span>]);</span><br><span class="line">        left.push_back(front.size());</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> (<span class="keyword">int</span> i = n - <span class="number">1</span>; i &gt;= <span class="number">0</span>; --i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (i == n - <span class="number">1</span>) &#123;</span><br><span class="line">            right.push_back(<span class="number">0</span>);</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="keyword">if</span> (back.empty() || back.top() &gt; buildings[i + <span class="number">1</span>]) &#123;</span><br><span class="line">            back.push(buildings[i + <span class="number">1</span>]);</span><br><span class="line">            right.push_back(back.size());</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="keyword">while</span> (!back.empty() &amp;&amp; back.top() &lt;= buildings[i + <span class="number">1</span>]) &#123;</span><br><span class="line">            back.pop();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        back.push(buildings[i + <span class="number">1</span>]);</span><br><span class="line">        right.push_back(back.size());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, j = n - <span class="number">1</span>; i &lt; n &amp;&amp; j &gt;= <span class="number">0</span>; i++, j--) &#123;</span><br><span class="line">        seen.push_back(<span class="number">1</span> + left[i] + right[j]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> seen;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> n;</span><br><span class="line">    <span class="built_in">cin</span> &gt;&gt; n;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; buildings;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        <span class="keyword">int</span> height;</span><br><span class="line">        <span class="built_in">cin</span> &gt;&gt; height;</span><br><span class="line">        buildings.push_back(height);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; seen = watch(buildings, n);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;s : seen) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; s &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天做了一道2020腾讯校招技术笔试题目，难度不大，但同样容易诱导我们写出&lt;code&gt;TLE&lt;/code&gt;的代码。为了优化时间复杂度，我们可以借助双向扫描来降低程序步数。对于双向扫描，顾名思义，以一维数组为例，从左到右遍历数组内部元素，我们视为一次单向扫描，那么，双向扫描即意味着从左到右遍历元素，而后从右到左遍历元素。如何借助双向扫描来降低时间复杂度呢？我们从一道题目开始：&lt;/p&gt;
    
    </summary>
    
    
      <category term="算法" scheme="https://hybin.github.io/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
      <category term="Tencent" scheme="https://hybin.github.io/tags/Tencent/"/>
    
  </entry>
  
  <entry>
    <title>与TLE斗争到底系列（一）：堆的应用</title>
    <link href="https://hybin.github.io/2020/01/14/%E4%B8%8ETLE%E6%96%97%E4%BA%89%E5%88%B0%E5%BA%95%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A%E5%A0%86%E7%9A%84%E5%BA%94%E7%94%A8/"/>
    <id>https://hybin.github.io/2020/01/14/%E4%B8%8ETLE%E6%96%97%E4%BA%89%E5%88%B0%E5%BA%95%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A%E5%A0%86%E7%9A%84%E5%BA%94%E7%94%A8/</id>
    <published>2020-01-14T09:29:53.000Z</published>
    <updated>2020-01-14T11:46:42.630Z</updated>
    
    <content type="html"><![CDATA[<p>对我来说，<a href="https://codingcompetitions.withgoogle.com/kickstart/" target="_blank" rel="noopener">Google Kick Start</a> 就是一条充满荆棘的山路，无处不在的<code>WA</code>和<code>TLE</code>整得人简直没脾气。接下来一段时间，我打算在这条路上好好走上一走，四处看看，与<code>TLE</code>斗争到底。一般来说，<code>TLE</code>发生的原因即在于时间复杂度过高，需要想办法优化代码，其中一个思路是借助堆的应用，之前的也有<a href="https://hybin.github.io/2019/12/15/Heap-and-Sliding-Window/#more">文章</a>提到，本文从一道更难的题目入手，一起来看看如何借助堆优化时间复杂度。</p><a id="more"></a><h4 id="H-index"><a href="#H-index" class="headerlink" title="H-index"></a><a href="https://codingcompetitions.withgoogle.com/kickstart/round/0000000000050edd/00000000001a274e" target="_blank" rel="noopener">H-index</a></h4><blockquote><p>It is important for researchers to write many high quality academic papers. Jorge has recently discovered a way to measure how impactful a researcher’s papers are: the <a href="https://en.wikipedia.org/wiki/H-index" target="_blank" rel="noopener">H-index</a>.</p><p>The <em>H-index score</em> of a researcher is the largest integer h such that the researcher has h papers with at least h citations each.</p><p>Jorge has written <strong>N</strong> papers in his lifetime. The i-th paper has <strong>Ai</strong> citations. The number of citations that each paper has will never change after it is written. Please help Jorge determine his H-index score after each paper he wrote.</p></blockquote><h5 id="Simple-Test-Cases"><a href="#Simple-Test-Cases" class="headerlink" title="Simple Test Cases"></a>Simple Test Cases</h5><blockquote><p><strong>Input</strong></p><p>The first line of the input gives the number of test cases, <strong>T</strong>. <strong>T</strong> test cases follow. Each test case begins with a line containing <strong>N</strong>, the number of papers Jorge wrote.</p><p>The second line contains <strong>N</strong> integers. The i-th integer is <strong>Ai</strong>, the number of citations the i-th paper has.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">3</span><br><span class="line">5 1 2</span><br><span class="line">6</span><br><span class="line">1 3 3 2 2 15</span><br></pre></td></tr></table></figure><hr><p><strong>Output</strong></p><p>For each test case, output one line containing <code>Case #x: y</code>, where <code>x</code> is the test case number (starting from 1) and <code>y</code> is a space-separated list of integers. The i-th integer is the H-index score after Jorge wrote his i-th paper.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Case #1: 1 1 2</span><br><span class="line">Case #2: 1 1 2 2 2 3</span><br></pre></td></tr></table></figure></blockquote><h6 id="Limit"><a href="#Limit" class="headerlink" title="Limit"></a>Limit</h6><p>Time limit: <code>50</code> seconds per test set.<br>Memory limit: <code>1GB</code>.</p><blockquote><p>$1 \leq T \leq 100$</p><p>$1 \leq A_i \leq 105$</p></blockquote><p><strong>Test set 1 (Visible)</strong></p><blockquote><p>$1 \leq N \leq 1000$</p></blockquote><p><strong>Test set 2 (Hidden)</strong></p><blockquote><p>$1 \leq N \leq 10^5$</p></blockquote><hr><p>首先，需要了解<code>H-Index</code>的概念：H-Index又称为H指数或H因子，是一种评价学术成就的新方法。H代表“高引用次数”（high citations），一名科研人员的h指数是指他至多有H篇论文分别被引用了至少H次。如Simple Test Case #1 所示，当科研人员发了第一篇论文，引用次数为5，那么其H因子为1，解释为其至多发表了1（paper #1）篇论文且至少被引用了1次；当发表了第二篇论文，引用次数为1，那么其H因子依旧为1，因为其至多发表了1篇论文（paper #1 or paper #2）且至少被引用了1次；当发表了第三篇论文，引用次数为2，那么其H因子为2，因为其至多发表了2篇论文（paper #1 and paper #2）且至少被引用了2次。</p><p>那么，从直觉上，我们可以借助暴力枚举的方式，当科研人员发表了第<code>k</code>篇论文，寻找：$h-index(k) = max_k min(f(k), k)$。如此，其时间复杂度为$O(n^2)$，当然，其对于简单测试集<code>Test set 1</code>是够用的，但对于复杂测试集<code>Test set 2</code>来说便会超时，因此必须换个思路。</p><p>回顾题意，对于第<code>k</code>篇文章，我们需要找到<code>m</code>篇文章，且其引用次数<code>&gt;=m</code>，同时，需要注意的是，<code>H-Index</code>总体上与发表文章数量呈正比，因此，我们可以设计一个小根堆来存储发表文章的引用次数，当堆顶元素小于当前<code>H-Index</code>时，移除该堆顶元素；当小根堆的容量依旧大于当前<code>H-Index</code>时，则当前<code>H-Index++</code>。由此，我们仅仅扫描一遍文章引用次数数组，其时间复杂度为$O(n)$。代码如下：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;queue&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; compute(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;papers) &#123;</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; indice(papers.size(), <span class="number">1</span>);</span><br><span class="line">    </span><br><span class="line">    priority_queue&lt;<span class="keyword">int</span>, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;, greater&lt;&gt;&gt; heap;</span><br><span class="line">    heap.push(papers[<span class="number">0</span>]);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; papers.size(); ++i) &#123;</span><br><span class="line">        heap.push(papers[i]);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">int</span> current = indice[i - <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">while</span> (heap.top() &lt;= current &amp;&amp; !heap.empty()) &#123;</span><br><span class="line">            heap.pop();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (heap.size() &gt; current) &#123;</span><br><span class="line">            current = current + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line"></span><br><span class="line">        indice[i] = current;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> indice;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> T;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">cin</span> &gt;&gt; T;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> t = <span class="number">0</span>; t &lt; T; ++t) &#123;</span><br><span class="line">        <span class="keyword">int</span> N;</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; papers, h_indice;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">cin</span> &gt;&gt; N;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> n = <span class="number">0</span>; n &lt; N; ++n) &#123;</span><br><span class="line">            <span class="keyword">int</span> c;</span><br><span class="line">            <span class="built_in">cin</span> &gt;&gt; c;</span><br><span class="line"></span><br><span class="line">            papers.push_back(c);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        h_indice = compute(papers);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Case #"</span> &lt;&lt; t + <span class="number">1</span> &lt;&lt; <span class="string">": "</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;h_index : h_indice) &#123;</span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; h_index &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;对我来说，&lt;a href=&quot;https://codingcompetitions.withgoogle.com/kickstart/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Google Kick Start&lt;/a&gt; 就是一条充满荆棘的山路，无处不在的&lt;code&gt;WA&lt;/code&gt;和&lt;code&gt;TLE&lt;/code&gt;整得人简直没脾气。接下来一段时间，我打算在这条路上好好走上一走，四处看看，与&lt;code&gt;TLE&lt;/code&gt;斗争到底。一般来说，&lt;code&gt;TLE&lt;/code&gt;发生的原因即在于时间复杂度过高，需要想办法优化代码，其中一个思路是借助堆的应用，之前的也有&lt;a href=&quot;https://hybin.github.io/2019/12/15/Heap-and-Sliding-Window/#more&quot;&gt;文章&lt;/a&gt;提到，本文从一道更难的题目入手，一起来看看如何借助堆优化时间复杂度。&lt;/p&gt;
    
    </summary>
    
    
      <category term="算法" scheme="https://hybin.github.io/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
      <category term="Kick Start" scheme="https://hybin.github.io/tags/Kick-Start/"/>
    
  </entry>
  
  <entry>
    <title>C++ With VSCode</title>
    <link href="https://hybin.github.io/2019/12/17/C-With-VSCode/"/>
    <id>https://hybin.github.io/2019/12/17/C-With-VSCode/</id>
    <published>2019-12-17T13:04:28.000Z</published>
    <updated>2019-12-17T13:24:08.751Z</updated>
    
    <content type="html"><![CDATA[<p>在学习<code>C++</code>的过程中，经常会遇到一个场景，发现一个新的小知识点，希望借助代码进行验证。这时，基于MacOS环境，直观的做法是打开Xcode或者<a href="https://www.jetbrains.com/clion/" target="_blank" rel="noopener">CLion</a>创建新的C++项目。然而，这不免给人一种不够轻巧的感觉。经过一阵摸索，发现借助<code>Visual Studio Code</code>和<code>CMake</code>，能够十分方便地完成源代码构建，故而在此记录一下。</p><a id="more"></a><p>首先，下载<code>Visual Studio Code</code>及相关插件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ brew cask install vscode</span><br></pre></td></tr></table></figure><p>安装完毕之后，打开<code>Visual Studio Code</code>并使用快捷键<code>Shift+cmd+P</code>唤出命令行，输入：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ext</span><br></pre></td></tr></table></figure><p>选择<strong>Extensions: Install Extensions</strong>，而后搜索并安装：</p><ul><li>C/C++</li><li>CMake</li><li>CMake Tools</li></ul><hr><p>接下来，创建新的代码文件，并于根目录创建<code>CMakeLists.txt</code>：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="comment"># CMakeLists.txt</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">3.14</span>)</span><br><span class="line"><span class="keyword">project</span>(DSAC)                   <span class="comment"># Project name</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(CMAKE_CXX_STANDARD <span class="number">14</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(Codes                       <span class="comment"># Project files</span></span><br><span class="line">        source/header.h</span><br><span class="line">        source/<span class="keyword">file</span>.cpp)</span><br><span class="line"></span><br><span class="line"><span class="keyword">add_executable</span>(DSAC <span class="variable">$&#123;Codes&#125;</span>)</span><br></pre></td></tr></table></figure><p>将源代码文件加入到<code>CMakeLists.txt</code>，编译器就知道将相关文件链接到一起。最后，到了关键时刻，你可以直接点击左下角的<code>生成</code>，完成代码构建：</p><p><img src="/2019/12/17/C-With-VSCode/hybin/Documents/Blog/source/_posts/C-With-VSCode/screenshot.png" alt></p><p>同时，你也可以设计快捷键唤起<code>生成</code>。而后，你就可以直接使用命令行运行可执行文件啦！</p><figure class="highlight bash"><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="built_in">cd</span> /path/to/project/build</span><br><span class="line">$ ./project</span><br></pre></td></tr></table></figure><p>以上！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在学习&lt;code&gt;C++&lt;/code&gt;的过程中，经常会遇到一个场景，发现一个新的小知识点，希望借助代码进行验证。这时，基于MacOS环境，直观的做法是打开Xcode或者&lt;a href=&quot;https://www.jetbrains.com/clion/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CLion&lt;/a&gt;创建新的C++项目。然而，这不免给人一种不够轻巧的感觉。经过一阵摸索，发现借助&lt;code&gt;Visual Studio Code&lt;/code&gt;和&lt;code&gt;CMake&lt;/code&gt;，能够十分方便地完成源代码构建，故而在此记录一下。&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="Development Tool" scheme="https://hybin.github.io/tags/Development-Tool/"/>
    
      <category term="Configuration" scheme="https://hybin.github.io/tags/Configuration/"/>
    
  </entry>
  
  <entry>
    <title>Heap and Sliding Window</title>
    <link href="https://hybin.github.io/2019/12/15/Heap-and-Sliding-Window/"/>
    <id>https://hybin.github.io/2019/12/15/Heap-and-Sliding-Window/</id>
    <published>2019-12-15T12:26:13.000Z</published>
    <updated>2019-12-15T12:57:49.032Z</updated>
    
    <content type="html"><![CDATA[<p>针对数据结构中的<strong style="color: rgb(217,70,82)">堆(Heap)</strong>，素来比较陌生，也不知道该如何使用。今天来理一理<strong style="color: rgb(217,70,82)">堆</strong>的概念。<strong style="color: rgb(217,70,82)">堆(Heap)</strong>又称<strong style="color: rgb(217,70,82)">优先队列(Priority Queue)</strong>，其内部元素并非按照一般的“先进先出”原则，而是按照优先级取出元素。此外，堆又可分为大根堆与小根堆。为了更清晰地了解堆的使用，来看一道算法题：</p><a id="more"></a><h4 id="480-滑动窗口中位数"><a href="#480-滑动窗口中位数" class="headerlink" title="480. 滑动窗口中位数"></a><a href="https://leetcode-cn.com/problems/sliding-window-median/" target="_blank" rel="noopener">480. 滑动窗口中位数</a></h4><blockquote><p>中位数是有序序列最中间的那个数。如果序列的大小是偶数，则没有最中间的数；此时中位数是最中间的两个数的平均数。</p><p>例如：</p><p><code>[2,3,4]</code>，中位数是<code>3</code></p><p><code>[2,3]</code>，中位数是 <code>(2 + 3) / 2 = 2.5​</code></p><p>给出一个数组 nums，有一个大小为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数，每次窗口移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数，并输出由它们组成的数组。</p></blockquote><h5 id="Simple-Test-Cases"><a href="#Simple-Test-Cases" class="headerlink" title="Simple Test Cases"></a>Simple Test Cases</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">输入：nums &#x3D; [1,3,-1,-3,5,3,6,7], k &#x3D; 3</span><br><span class="line">输出：[1,-1,-1,3,5,6]</span><br><span class="line">解释：</span><br><span class="line">窗口位置                         中位数</span><br><span class="line">---------------                -------</span><br><span class="line">[1  3  -1] -3  5  3  6  7         1</span><br><span class="line"> 1 [3  -1  -3] 5  3  6  7        -1</span><br><span class="line"> 1  3 [-1  -3  5] 3  6  7        -1</span><br><span class="line"> 1  3  -1 [-3  5  3] 6  7         3</span><br><span class="line"> 1  3  -1  -3 [5  3  6] 7         5</span><br><span class="line"> 1  3  -1  -3  5 [3  6  7]        6</span><br></pre></td></tr></table></figure><p><strong>方法一（超时）：</strong></p><p>对于这道题，直观的感觉是维护一个有序的双端队列，将滑动窗口内的元素加入到该有序双端队列之内，并依照<code>k</code>的值取队列中的中位数。其中，对于有序的双端队列，可以借助单调栈使得队列内部的元素总是有序的。代码如下：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;deque&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stack&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">push</span><span class="params">(<span class="built_in">deque</span>&lt;<span class="keyword">long</span>&gt; &amp;window, <span class="keyword">int</span> value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> back = window.back();</span><br><span class="line">        <span class="built_in">stack</span>&lt;<span class="keyword">long</span>&gt; preserve;</span><br><span class="line">        <span class="keyword">while</span> (back &gt; value &amp;&amp; !window.empty()) &#123;</span><br><span class="line">            preserve.push(back);</span><br><span class="line">            window.pop_back();</span><br><span class="line">            back = window.back();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        window.push_back(value);</span><br><span class="line">        <span class="keyword">while</span> (!preserve.empty()) &#123;</span><br><span class="line">            <span class="keyword">long</span> top = preserve.top();</span><br><span class="line">            window.push_back(top);</span><br><span class="line">            preserve.pop();</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">vector</span>&lt;<span class="keyword">double</span>&gt; medianSlidingWindow(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums, <span class="keyword">int</span> k) &#123;</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">double</span>&gt; medians;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (nums.empty()) <span class="keyword">return</span> medians;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; nums.size() - (k - <span class="number">1</span>); ++i) &#123;</span><br><span class="line">            <span class="built_in">deque</span>&lt;<span class="keyword">long</span>&gt; window &#123;nums[i]&#125;;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> j = i + <span class="number">1</span>; j &lt;= i + (k - <span class="number">1</span>); ++j) &#123;</span><br><span class="line">                <span class="keyword">int</span> front = window.front(), back = window.back();</span><br><span class="line">                <span class="keyword">if</span> (nums[j] &lt;= front) &#123;</span><br><span class="line">                    window.push_front(nums[j]);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (nums[j] &gt;= back) &#123;</span><br><span class="line">                        window.push_back(nums[j]);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        push(window, nums[j]);</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">double</span> median = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">if</span> (k % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">                median = (window[k / <span class="number">2</span>] + window[k / <span class="number">2</span> - <span class="number">1</span>]) / (<span class="keyword">double</span>)<span class="number">2</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                median = window[k / <span class="number">2</span>];</span><br><span class="line">            &#125;</span><br><span class="line">            medians.push_back(median);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> medians;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>对于简单的用例，上述方法能够正确获得中位数序列，但假如输入的用例比较复杂呢？超时的问题如何解决？</p><p><strong>方法二</strong></p><p>首先，我们维护两个堆：</p><ul><li>一个大根堆 <code>lo</code>，用来存放较小的那一半的元素；</li><li>一个小根堆 <code>hi</code>，用来存放较大的那一半的元素。</li></ul><p>由此，堆顶的元素即为中位数。接下来，使用哈希集合（HashSet）或者哈希映射（HashMap），记为 <code>hash_table</code>，标记所有被移除的无效元素，哈希表的大小等于在堆中无效元素的数量；大根堆 <code>lo</code> 最多允许比小根堆 <code>hi</code> 存放多一个元素，当我们已经处理了 <code>k</code> 个元素时：</p><ul><li>如果 <code>k = 2n + 1</code> 为奇数，那么 <code>lo</code> 中存储 <code>k + 1</code> 个元素，<code>hi</code> 中存储 <code>k</code> 个元素；</li><li>如果 <code>k = 2n</code> 为偶数，那么 <code>lo</code> 和 <code>hi</code> 中都存储 <code>k</code> 个元素；</li></ul><p>如何判断两个堆是否平衡呢？我们引入<code>balance</code>变量，表示两个堆是否平衡：</p><ul><li>如果 <code>balance == 0</code>，那么两个堆平衡；</li><li>如果 <code>balance &lt; 0</code>，那么 <code>lo</code> 中的元素较少，需要从 <code>hi</code> 中取出若干个元素放入 <code>lo</code>；</li><li>如果 <code>balance &gt; 0</code>，那么 <code>hi</code> 中的元素较少，需要从 <code>lo</code> 中取出若干个元素放入 <code>hi</code>。</li></ul><p>此时我们需要插入一个新的元素 <code>in_num</code>：</p><ul><li>如果 <code>in_num</code> 小于等于 <code>lo</code> 的堆顶元素，那么它可以被放入 <code>lo</code> 中，此时需要增加 <code>balance</code> 的值；</li><li>否则，<code>in_num</code> 可以被放入 <code>hi</code> 中，此时需要减少 <code>balance</code> 的值。</li></ul><p>延迟删除被移出窗口的元素 <code>out_num</code>：</p><ul><li>如果 <code>out_num</code> 在 <code>lo</code> 中，那么需要减少 <code>balance</code> 的值；</li><li>如果 <code>out_num</code> 在 <code>hi</code> 中，那么需要增加 <code>balance</code> 的值；</li><li>我们将 <code>out_num</code> 放入哈希表中；</li><li>每当无效的元素出现在堆顶，我们就将其从堆中删除，同时从哈希表中删除。</li></ul><p>由此，我们得到代码：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;deque&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stack&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">vector</span>&lt;<span class="keyword">double</span>&gt; medianSlidingWindow(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums, <span class="keyword">int</span> k)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">double</span>&gt; medians;</span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">int</span>, <span class="keyword">int</span>&gt; hash_table;</span><br><span class="line">    priority_queue&lt;<span class="keyword">int</span>&gt; lo;                                 <span class="comment">// max heap</span></span><br><span class="line">    priority_queue&lt;<span class="keyword">int</span>, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;, greater&lt;<span class="keyword">int</span>&gt; &gt; hi;     <span class="comment">// min heap</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> i = <span class="number">0</span>;      <span class="comment">// index of current incoming element being processed</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// initialize the heaps</span></span><br><span class="line">    <span class="keyword">while</span> (i &lt; k)</span><br><span class="line">        lo.push(nums[i++]);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; k / <span class="number">2</span>; j++) &#123;</span><br><span class="line">        hi.push(lo.top());</span><br><span class="line">        lo.pop();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="comment">// get median of current window</span></span><br><span class="line">        medians.push_back(k &amp; <span class="number">1</span> ? lo.top() : ((<span class="keyword">double</span>)lo.top() + (<span class="keyword">double</span>)hi.top()) * <span class="number">0.5</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (i &gt;= nums.size())</span><br><span class="line">            <span class="keyword">break</span>;              <span class="comment">// break if all elements processed</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> out_num = nums[i - k],          <span class="comment">// outgoing element</span></span><br><span class="line">            in_num = nums[i++],             <span class="comment">// incoming element</span></span><br><span class="line">            balance = <span class="number">0</span>;                    <span class="comment">// balance factor</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// number `out_num` exits window</span></span><br><span class="line">        balance += (out_num &lt;= lo.top() ? <span class="number">-1</span> : <span class="number">1</span>);</span><br><span class="line">        hash_table[out_num]++;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// number `in_num` enters window</span></span><br><span class="line">        <span class="keyword">if</span> (!lo.empty() &amp;&amp; in_num &lt;= lo.top()) &#123;</span><br><span class="line">            balance++;</span><br><span class="line">            lo.push(in_num);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            balance--;</span><br><span class="line">            hi.push(in_num);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// re-balance heaps</span></span><br><span class="line">        <span class="keyword">if</span> (balance &lt; <span class="number">0</span>) &#123;        <span class="comment">// `lo` needs more valid elements</span></span><br><span class="line">            lo.push(hi.top());</span><br><span class="line">            hi.pop();</span><br><span class="line">            balance++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (balance &gt; <span class="number">0</span>) &#123;        <span class="comment">// `hi` needs more valid elements</span></span><br><span class="line">            hi.push(lo.top());</span><br><span class="line">            lo.pop();</span><br><span class="line">            balance--;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// remove invalid numbers that should be discarded from heap tops</span></span><br><span class="line">        <span class="keyword">while</span> (hash_table[lo.top()]) &#123;</span><br><span class="line">            hash_table[lo.top()]--;</span><br><span class="line">            lo.pop();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (!hi.empty() &amp;&amp; hash_table[hi.top()]) &#123;</span><br><span class="line">            hash_table[hi.top()]--;</span><br><span class="line">            hi.pop();</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> medians;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;针对数据结构中的&lt;strong style=&quot;color: rgb(217,70,82)&quot;&gt;堆(Heap)&lt;/strong&gt;，素来比较陌生，也不知道该如何使用。今天来理一理&lt;strong style=&quot;color: rgb(217,70,82)&quot;&gt;堆&lt;/strong&gt;的概念。&lt;strong style=&quot;color: rgb(217,70,82)&quot;&gt;堆(Heap)&lt;/strong&gt;又称&lt;strong style=&quot;color: rgb(217,70,82)&quot;&gt;优先队列(Priority Queue)&lt;/strong&gt;，其内部元素并非按照一般的“先进先出”原则，而是按照优先级取出元素。此外，堆又可分为大根堆与小根堆。为了更清晰地了解堆的使用，来看一道算法题：&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
      <category term="Data Structure" scheme="https://hybin.github.io/tags/Data-Structure/"/>
    
  </entry>
  
  <entry>
    <title>TypeScript with Laravel Mix</title>
    <link href="https://hybin.github.io/2019/11/29/TypeScript-with-Laravel-Mix/"/>
    <id>https://hybin.github.io/2019/11/29/TypeScript-with-Laravel-Mix/</id>
    <published>2019-11-29T08:45:39.000Z</published>
    <updated>2019-11-29T09:18:54.237Z</updated>
    
    <content type="html"><![CDATA[<p>当我们使用<a href="https://laravel.com/" target="_blank" rel="noopener">Laravel框架</a>开发网站时，需要编译JavaScript及SASS以实现我们的网页设计。通常情况下，前端JavaScript代码位于下述路径：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;resources&#x2F;assets&#x2F;js&#x2F;app.js</span><br></pre></td></tr></table></figure><br>对于规模比较小的项目，我们可以直接在<code>app.js</code>中加入我们的JavaScript代码，无论基于什么前端框架。然而，随着项目规模逐渐增大，代码量越来越多，对代码进行模块化显得愈发重要（当然，这也是一个很好的编码习惯，最好从项目开始，就有这样的意识）。<br><a id="more"></a></p><h4 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h4><blockquote><p>对于部分可复用函数，我想将其单独存放，汇集成一个代码源文件，并<code>import</code>到<code>app.js</code>中来，同时，我亦想用TypeScript语言替换JavaScript语言以获得清晰的类型声明，我要怎么处理？</p></blockquote><h4 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h4><p>对于这一问题，我们可以分两步走:</p><ul><li>令程序能够自动加载和编译TypeScript代码；</li><li>正确导入单独存放的额可复用代码。</li></ul><h4 id="TypeScript-with-Laravel-Mix"><a href="#TypeScript-with-Laravel-Mix" class="headerlink" title="TypeScript with Laravel Mix"></a>TypeScript with Laravel Mix</h4><p>在<code>Laravel Framework 5.5</code> 中，要使项目正确地加载和编译TypeScript代码，我们首先需要安装<code>ts-loader</code>：<br><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">yarn add ts-loader typescript --dev</span><br></pre></td></tr></table></figure><br>安装完毕后，我们需要对配置文件作一定的修改，在Shell中运行<code>node_modules/.bin/tsc --init</code>命令，将会在项目文件夹根目录生成配置文件<code>tsconfig.json</code>。对于该配置文件中的默认配置，我们不作修改，需要在最后加上<code>&quot;include&quot;: [&quot;resources/assets/js/**/*&quot;]</code>：<br><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">&#123;</span><br><span class="line">  <span class="string">"compilerOptions"</span>: &#123;</span><br><span class="line">    <span class="string">"module"</span>: <span class="string">"commonjs"</span>,</span><br><span class="line">    <span class="string">"target"</span>: <span class="string">"es5"</span>,</span><br><span class="line">    <span class="string">"noImplicitAny"</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="string">"sourceMap"</span>: <span class="literal">false</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"include"</span>: [<span class="string">"resources/assets/js/**/*"</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>接下来，对<code>Laravel Mix</code>进行配置修改，在根目录中编辑<code>webpack.mix.js</code> 文件，修改如下：<br><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; mix &#125; = <span class="built_in">require</span>(<span class="string">"laravel-mix"</span>);</span><br><span class="line"></span><br><span class="line">mix</span><br><span class="line">  .js(<span class="string">"resources/assets/js/app.js"</span>, <span class="string">"public/js"</span>)</span><br><span class="line">  .sass(<span class="string">"resources/assets/sass/app.scss"</span>, <span class="string">"public/css"</span>)</span><br><span class="line">  .webpackConfig(&#123;</span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">      rules: [</span><br><span class="line">        &#123;</span><br><span class="line">          test: <span class="regexp">/\.tsx?$/</span>,</span><br><span class="line">          loader: <span class="string">"ts-loader"</span>,</span><br><span class="line">          exclude: <span class="regexp">/node_modules/</span></span><br><span class="line">        &#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;,</span><br><span class="line">    resolve: &#123;</span><br><span class="line">      extensions: [<span class="string">"*"</span>, <span class="string">".js"</span>, <span class="string">".jsx"</span>, <span class="string">".vue"</span>, <span class="string">".ts"</span>, <span class="string">".tsx"</span>]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><br>如此，我们的项目就能够正确地加载和编译TypeScript代码啦！</p><h4 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h4><p>在<code>resources/assets/js/</code>文件夹下创建<code>utility.ts</code>文件，并写入：<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">helloWorld</span>(<span class="params"></span>): <span class="title">string</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">"Hello world!"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>保存后，打开<code>app.js</code>，使用<code>require()</code>函数<code>import</code>刚刚写好的TypeScript代码：<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> utility = <span class="built_in">require</span>(<span class="string">'./utility'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(utility.helloWorld());</span><br></pre></td></tr></table></figure></p><p>保存并编译代码，就可以在<code>Console</code>中看到结果啦！</p><p>以上！ </p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;当我们使用&lt;a href=&quot;https://laravel.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Laravel框架&lt;/a&gt;开发网站时，需要编译JavaScript及SASS以实现我们的网页设计。通常情况下，前端JavaScript代码位于下述路径：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&amp;#x2F;resources&amp;#x2F;assets&amp;#x2F;js&amp;#x2F;app.js&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;br&gt;对于规模比较小的项目，我们可以直接在&lt;code&gt;app.js&lt;/code&gt;中加入我们的JavaScript代码，无论基于什么前端框架。然而，随着项目规模逐渐增大，代码量越来越多，对代码进行模块化显得愈发重要（当然，这也是一个很好的编码习惯，最好从项目开始，就有这样的意识）。&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web Development" scheme="https://hybin.github.io/categories/Web-Development/"/>
    
    
      <category term="Web Development" scheme="https://hybin.github.io/tags/Web-Development/"/>
    
      <category term="Laravel" scheme="https://hybin.github.io/tags/Laravel/"/>
    
      <category term="TypeScript" scheme="https://hybin.github.io/tags/TypeScript/"/>
    
  </entry>
  
  <entry>
    <title>Finite-State Machine</title>
    <link href="https://hybin.github.io/2019/11/26/Finite-Status-Machine/"/>
    <id>https://hybin.github.io/2019/11/26/Finite-Status-Machine/</id>
    <published>2019-11-26T09:18:48.000Z</published>
    <updated>2019-11-26T11:29:45.160Z</updated>
    
    <content type="html"><![CDATA[<p>有限状态机（Finite-State Machine, FSM）表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。状态机可归纳为4个要素，即现态、条件、动作、次态。“现态”和“条件”是因，“动作”和“次态”是果。</p><a id="more"></a><p>详解如下：</p><blockquote><p>① 现态：是指当前所处的状态。</p><p>② 条件：又称为“事件”。当一个条件被满足，将会触发一个动作，或者执行一次状态的迁移。</p><p>③ 动作：条件满足后执行的动作。动作执行完毕后，可以迁移到新的状态，也可以仍旧保持原状态。动作不是必需的，当条件满足后，也可以不执行任何动作，直接迁移到新状态。</p><p>④ 次态：条件满足后要迁往的新状态。“次态”是相对于“现态”而言的，“次态”一旦被激活，就转变成新的“现态”了。</p></blockquote><p>有限状态机对于算法求解具有重要的意义，其可以简化问题，使得复杂的递推关系转换为简单的状态转移问题。比如：</p><h4 id="552-学生出勤记录-II"><a href="#552-学生出勤记录-II" class="headerlink" title="552. 学生出勤记录 II"></a><a href="https://leetcode-cn.com/problems/student-attendance-record-ii/" target="_blank" rel="noopener">552. 学生出勤记录 II</a></h4><blockquote><p>给定一个正整数<code>n</code>，返回长度为<code>n</code>的所有可被视为可奖励的出勤记录的数量。 答案可能非常大，你只需返回结果<code>mod 1e9 + 7</code>的值。</p><p>学生出勤记录是只包含以下三个字符的字符串：</p><ol><li>‘A’ : Absent，缺勤</li><li>‘L’ : Late，迟到</li><li>‘P’ : Present，到场<br>如果记录不包含多于一个’A’（缺勤）或超过两个连续的’L’（迟到），则该记录被视为可奖励的。</li></ol></blockquote><h5 id="Simple-test-cases"><a href="#Simple-test-cases" class="headerlink" title="Simple test cases"></a>Simple test cases</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">输入: n &#x3D; 2</span><br><span class="line">输出: 8 </span><br><span class="line">解释：</span><br><span class="line">有8个长度为2的记录将被视为可奖励：</span><br><span class="line">&quot;PP&quot; , &quot;AP&quot;, &quot;PA&quot;, &quot;LP&quot;, &quot;PL&quot;, &quot;AL&quot;, &quot;LA&quot;, &quot;LL&quot;</span><br><span class="line">只有&quot;AA&quot;不会被视为可奖励，因为缺勤次数超过一次。</span><br></pre></td></tr></table></figure><p>对于这个问题，我们的直觉是通过构造全排列，并依照判断条件，对符合条件的记录进行计数。显然，依照直觉暴力求解，必然会遇到超时问题。更好的思路是基于动态规划的思想，构造状态转移方程。但就本题而言，借助有限状态机，会大大地简化问题。</p><p>先来看题目，对于不可奖励的记录，其要么包含<code>2+</code>的<code>A</code>，或<code>3+</code>的连续的<code>L</code>，那么，仅仅需要考虑<code>A</code>和<code>L</code>的情况，我们将有限状态机画出来，得到：<br><img src="/2019/11/26/Finite-Status-Machine/status.png" alt="有限状态机"></p><p>由上图可见，对于节点<code>A0L0</code>，其入度为3，而节点<code>A1L0</code>入度为6，那么代码可以表示为：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">checkRecord</span><span class="params">(<span class="keyword">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> m = <span class="number">1e9</span> + <span class="number">7</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">long</span> <span class="keyword">long</span> a0l0 = <span class="number">1</span>, a0l1 = <span class="number">0</span>, a0l2 = <span class="number">0</span>, a1l0 = <span class="number">0</span>, a1l1 = <span class="number">0</span>, a1l2 = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= n; ++i) &#123;</span><br><span class="line">            <span class="keyword">long</span> <span class="keyword">long</span> a0l0_ = (a0l0 + a0l1 + a0l2) % m;</span><br><span class="line">            a0l2 = a0l1;</span><br><span class="line">            a0l1 = a0l0;</span><br><span class="line">            a0l0 = a0l0_;</span><br><span class="line">            <span class="keyword">long</span> <span class="keyword">long</span> a1l0_ = (a0l0 + a1l0 + a1l1 + a1l2) % m;</span><br><span class="line">            a1l2 = a1l1;</span><br><span class="line">            a1l1 = a1l0;</span><br><span class="line">            a1l0 = a1l0_;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)a1l0;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>以上！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;有限状态机（Finite-State Machine, FSM）表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。状态机可归纳为4个要素，即现态、条件、动作、次态。“现态”和“条件”是因，“动作”和“次态”是果。&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
  </entry>
  
  <entry>
    <title>Greedy</title>
    <link href="https://hybin.github.io/2019/11/11/Greedy/"/>
    <id>https://hybin.github.io/2019/11/11/Greedy/</id>
    <published>2019-11-11T03:00:30.000Z</published>
    <updated>2019-11-12T07:09:09.728Z</updated>
    
    <content type="html"><![CDATA[<p>狭义的贪心算法指的是解最优化问题的一种特殊方法，解决过程中总是做出当下最好的选择，因为具有最优子结构的特点，局部最优解可以得到全局最优解；这种贪心算法是动态规划的一种特例。能用贪心解决的问题，也可以用动态规划解决。</p><a id="more"></a><p>广义的贪心指的是一种通用的贪心策略，基于当前局面而进行贪心决策。举个例子：</p><h4 id="12-整数转罗马数字"><a href="#12-整数转罗马数字" class="headerlink" title="12.整数转罗马数字"></a><a href="https://leetcode-cn.com/problems/integer-to-roman/" target="_blank" rel="noopener">12.整数转罗马数字</a></h4><blockquote><p>罗马数字包含以下七种字符：<code>I</code>，<code>V</code>，<code>X</code>，<code>L</code>，<code>C</code>，<code>D</code>和<code>M</code>，分别表示<code>1</code>，<code>5</code>，<code>10</code>，<code>50</code>，<code>100</code>，<code>500</code>，<code>1000</code>。<br>给定一个整数，将其转为罗马数字。输入确保在 1 到 3999 的范围内。</p></blockquote><h5 id="Simple-Test-Cases"><a href="#Simple-Test-Cases" class="headerlink" title="Simple Test Cases"></a>Simple Test Cases</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">输入: 3</span><br><span class="line">输出: &quot;III&quot;</span><br><span class="line">---------------------</span><br><span class="line">输入: 4</span><br><span class="line">输出: &quot;IV&quot;</span><br><span class="line">---------------------</span><br><span class="line">输入: 58</span><br><span class="line">输出: &quot;LVIII&quot;</span><br><span class="line">解释: L &#x3D; 50, V &#x3D; 5, III &#x3D; 3.</span><br><span class="line">------------------------------</span><br><span class="line">输入: 1994</span><br><span class="line">输出: &quot;MCMXCIV&quot;</span><br><span class="line">解释: M &#x3D; 1000, CM &#x3D; 900, XC &#x3D; 90, IV &#x3D; 4.</span><br></pre></td></tr></table></figure><p>本题从贪心算法的思路入手，可以考虑将目标数值，不断减去所能减去的最大的进位，得到罗马数字的表示：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="built_in">string</span> <span class="title">intToRoman</span><span class="params">(<span class="keyword">int</span> num)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">map</span>&lt;<span class="keyword">int</span>,<span class="built_in">string</span>&gt; mapRom = &#123;&#123;<span class="number">1</span>,<span class="string">"I"</span>&#125;,&#123;<span class="number">4</span>,<span class="string">"IV"</span>&#125;,&#123;<span class="number">5</span>,<span class="string">"V"</span>&#125;,&#123;<span class="number">9</span>,<span class="string">"IX"</span>&#125;,&#123;<span class="number">10</span>,<span class="string">"X"</span>&#125;,&#123;<span class="number">40</span>,<span class="string">"XL"</span>&#125;,</span><br><span class="line">                                  &#123;<span class="number">50</span>,<span class="string">"L"</span>&#125;, &#123;<span class="number">90</span>,<span class="string">"XC"</span>&#125;,&#123;<span class="number">100</span>,<span class="string">"C"</span>&#125;,                               </span><br><span class="line">                                  &#123;<span class="number">400</span>,<span class="string">"CD"</span>&#125;,&#123;<span class="number">500</span>,<span class="string">"D"</span>&#125;,&#123;<span class="number">900</span>,<span class="string">"CM"</span>&#125;, &#123;<span class="number">1000</span>,<span class="string">"M"</span>&#125; &#125;;</span><br><span class="line">        <span class="built_in">map</span>&lt;<span class="keyword">int</span>,<span class="built_in">string</span>&gt;::reverse_iterator  r_iter;</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">string</span> ret;</span><br><span class="line">       </span><br><span class="line">        r_iter=mapRom.rbegin();</span><br><span class="line">        <span class="keyword">while</span>(r_iter!=mapRom.rend())</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(num &gt;= r_iter-&gt;first)</span><br><span class="line">            &#123;</span><br><span class="line">                ret += r_iter-&gt;second;</span><br><span class="line">                num-= r_iter-&gt;first;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                r_iter++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ret;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>再来看另一道题，有时候题干并不会太明显地令你使用贪心算法，一定要灵活思考。</p><h4 id="452-用最少数量的箭引爆气球"><a href="#452-用最少数量的箭引爆气球" class="headerlink" title="452. 用最少数量的箭引爆气球"></a><a href="https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/" target="_blank" rel="noopener">452. 用最少数量的箭引爆气球</a></h4><blockquote><p>在二维空间中有许多球形的气球。对于每个气球，提供的输入是水平方向上，气球直径的开始和结束坐标。由于它是水平的，所以y坐标并不重要，因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在10000个气球。</p><p>一支弓箭可以沿着<code>x</code>轴从不同点完全垂直地射出。在坐标<code>x</code>处射出一支箭，若有一个气球的直径的开始和结束坐标为$x<em>{start}$，$x</em>{end}$， 且满足$x<em>{start} ≤ x ≤ x</em>{end}$，则该气球会被引爆。可以射出的弓箭的数量没有限制。弓箭一旦被射出之后，可以无限地前进。我们想找到使得所有气球全部被引爆，所需的弓箭的最小数量。</p></blockquote><h5 id="Simple-Test-Cases-1"><a href="#Simple-Test-Cases-1" class="headerlink" title="Simple Test Cases"></a>Simple Test Cases</h5><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">输入:</span><br><span class="line">[[10,16], [2,8], [1,6], [7,12]]</span><br><span class="line"></span><br><span class="line">输出:</span><br><span class="line">2</span><br><span class="line"></span><br><span class="line">解释:</span><br><span class="line">对于该样例，我们可以在x &#x3D; 6（射爆[2,8],[1,6]两个气球）和 x &#x3D; 11（射爆另外两个气球）。</span><br></pre></td></tr></table></figure><p>一种可行的思路是，对气球数组按末端大小进行排序，当两个气球不相交时，必然需要两支弓箭，反之，一支就行了，故而这亦是贪心算法的运用。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">findMinArrowShots</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&gt;&amp; points)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> n = points.size();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (n &lt; <span class="number">2</span>) <span class="keyword">return</span> n;</span><br><span class="line"></span><br><span class="line">        sort(points.begin(), points.end(), [](<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; point1, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; point2) &#123;</span><br><span class="line">            <span class="keyword">if</span> (point1[<span class="number">1</span>] != point2[<span class="number">1</span>]) &#123;</span><br><span class="line">                <span class="keyword">return</span> point1[<span class="number">1</span>] &lt; point2[<span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">        </span><br><span class="line">            <span class="keyword">return</span> point1[<span class="number">0</span>] &lt; point2[<span class="number">0</span>];</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> count = <span class="number">1</span>, end = points[<span class="number">0</span>][<span class="number">1</span>];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> (points[i][<span class="number">0</span>] &gt; end) &#123;</span><br><span class="line">                end = points[i][<span class="number">1</span>];</span><br><span class="line">                count++;</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> count;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>此外，要注意对数组进行排序时，在<strong>C++</strong>排序函数尽量使用<code>&gt;</code>、<code>&lt;</code>、<code>=</code>符号，不要使用<code>+</code>、<code>-</code>符号，后者可能会出错。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;狭义的贪心算法指的是解最优化问题的一种特殊方法，解决过程中总是做出当下最好的选择，因为具有最优子结构的特点，局部最优解可以得到全局最优解；这种贪心算法是动态规划的一种特例。能用贪心解决的问题，也可以用动态规划解决。&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
  </entry>
  
  <entry>
    <title>如何整一个文化人的桌面？</title>
    <link href="https://hybin.github.io/2019/10/22/%E5%A6%82%E4%BD%95%E6%95%B4%E4%B8%80%E4%B8%AA%E6%96%87%E5%8C%96%E4%BA%BA%E7%9A%84%E6%A1%8C%E9%9D%A2%EF%BC%9F/"/>
    <id>https://hybin.github.io/2019/10/22/%E5%A6%82%E4%BD%95%E6%95%B4%E4%B8%80%E4%B8%AA%E6%96%87%E5%8C%96%E4%BA%BA%E7%9A%84%E6%A1%8C%E9%9D%A2%EF%BC%9F/</id>
    <published>2019-10-22T11:22:23.000Z</published>
    <updated>2019-11-12T07:10:40.899Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>对于文化人来说，电脑桌面就是一个人的牌面。<br>——沃·兹基硕德</p></blockquote><a id="more"></a><p>不知道大家是否曾经苦恼过，如何找到令人耳目一新，体现个人文化品味的桌面壁纸？舍友许君是一位摄影大触，总能够精确地拍到那种发到朋友圈便会得到疯狂点赞的照片，这样的人是不会苦恼的。那么问题来了，假如我们没有单反，又不会拍照，如何愉快地崭露个人品（zhuāng）味（bī）？</p><p>某位不愿透露姓名的阿伯说过，“只要思想不滑坡，办法总比困难多”。我们要做文化人，就要有文化人的样子，比如精致的瓷器，能够体现你深厚的知识积累，和毒辣的鉴别眼光；比如写意的水墨画，总能令你身上透露出不俗的雅趣，比如…（“别废话了，快上干货！”“好勒！”</p><p>首先，鸣谢两位不愿透露姓名的陈焕茂同学和孙栋梁同学，为构造文化人的桌面提供了惊天动地的创意，而本人作为卑微的码农做了一点微小的工作。</p><p>我们的思路是这样的，故宫博物院为广大人民群众提供了精致而优雅的壁纸，并适配了不同分辨率的屏幕，还不定期更新（就问你贴不贴心？</p><p>普通人如何整一个文化人的桌面呢？很简单，打开<a href="https://www.dpm.org.cn/lights/royal.html" target="_blank" rel="noopener">故宫壁纸</a>，选择喜欢的壁纸，并根据自己电脑的分辨率下载壁纸即可，而后完成壁纸更换。（论文化人的诞生…</p><p>如果单单是这样，那么对懒癌文化人太不友好了，而且，一张图片怎能凸显我是整个图书馆最靓的崽？</p><blockquote><p>小孩子才做选择，成年人全部都要！</p></blockquote><p>所以，答案呼之欲出，人生苦短，我用Python，爬虫搞起！</p><p>啥也不说了，代码在这里：<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"><span class="keyword">from</span> urllib.request <span class="keyword">import</span> urlopen, urlretrieve</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Scraper</span><span class="params">(object)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        self.url = <span class="string">'https://www.dpm.org.cn//lights/royal/p/&#123;&#125;.html'</span></span><br><span class="line">        <span class="comment"># 保存壁纸的文件夹，要自己设置哦～</span></span><br><span class="line">        self.directory = <span class="string">'./backgrounds/'</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 由于天长日久，故宫壁纸项目也一直在完善</span></span><br><span class="line">        <span class="comment"># 所以，图片源会有变化，此处仅提供近期的图片源</span></span><br><span class="line">        <span class="comment"># 有需要的话，可以自己检查元素获取图片源哦～</span></span><br><span class="line">        self.source = <span class="string">'https://www.dpm.org.cn/download/lights_image/id/&#123;&#125;/img_size/4.html'</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">load</span><span class="params">(self, page)</span>:</span></span><br><span class="line">        doc = urlopen(self.url.format(page))</span><br><span class="line">        soup = BeautifulSoup(doc, <span class="string">'html.parser'</span>)</span><br><span class="line">        images = soup.find_all(<span class="string">'div'</span>, class_=<span class="string">'pic'</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> image <span class="keyword">in</span> images:</span><br><span class="line">            name = image.find(<span class="string">'img'</span>)[<span class="string">'title'</span>] + <span class="string">'.jpg'</span></span><br><span class="line">            image_id = image.find(<span class="string">'a'</span>)[<span class="string">'href'</span>].split(<span class="string">'/'</span>)[<span class="number">-1</span>].split(<span class="string">'.'</span>)[<span class="number">0</span>]</span><br><span class="line">            urlretrieve(self.source.format(image_id), os.path.join(self.directory, name))</span><br><span class="line"></span><br><span class="line">        print(<span class="string">'Complete! Pictures are in '</span> + self.directory)</span><br><span class="line"></span><br><span class="line">scraper = Scraper()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 其实就是故宫壁纸网站的页码</span></span><br><span class="line">page = input(<span class="string">'输入你的幸运数字：'</span>)</span><br><span class="line"></span><br><span class="line">scraper.load(page)</span><br></pre></td></tr></table></figure></p><p>想要运行上述代码，则需要你的电脑能够运行<code>Python@&gt;=3.0</code>，那么，假如你的电脑是mac，又安装了<a href="https://brew.sh" target="_blank" rel="noopener">Homebrew</a>，那么你可以：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ brew install python</span><br></pre></td></tr></table></figure></p><p>假如你的电脑是Windows，又安装了<a href="https://scoop.sh" target="_blank" rel="noopener">Scoop</a>，那么，你可以：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ scoop install python</span><br></pre></td></tr></table></figure></p><p>接下来，我们需要安装爬虫依赖包<code>BeautifulSoup</code>，即：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pip install bs4</span><br></pre></td></tr></table></figure></p><p>现在，你即将成为准文化人，继续命令行：<br><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></pre></td><td class="code"><pre><span class="line">$ <span class="comment"># 打开源代码所在文件夹</span></span><br><span class="line">$ <span class="built_in">cd</span> /path/to/source.py</span><br><span class="line">$ <span class="comment"># 运行源代码</span></span><br><span class="line">$ python source.py</span><br></pre></td></tr></table></figure></p><p>事情结束了嘛？并没有！可达鸭眉头一皱，发现事情并不简单！假如我们想要让桌面壁纸每隔一段时间换一张，咋整呢？</p><p>第一步，打开<code>系统偏好设置</code>：<br><img src="/2019/10/22/%E5%A6%82%E4%BD%95%E6%95%B4%E4%B8%80%E4%B8%AA%E6%96%87%E5%8C%96%E4%BA%BA%E7%9A%84%E6%A1%8C%E9%9D%A2%EF%BC%9F/系统偏好设置.png" alt="系统偏好设置"></p><p>第二步，选择<code>桌面与屏幕保护程序</code>：<br><img src="/2019/10/22/%E5%A6%82%E4%BD%95%E6%95%B4%E4%B8%80%E4%B8%AA%E6%96%87%E5%8C%96%E4%BA%BA%E7%9A%84%E6%A1%8C%E9%9D%A2%EF%BC%9F/桌面与屏幕保护程序.png" alt="桌面与屏幕保护程序"></p><p>还记得之前保存图片的文件夹嘛？点击<code>+</code>，选择那个文件夹，而后勾选<code>更改图片</code>和<code>随机顺序</code>，完结撒花🎉！</p><p>从现在开始，你就是一个文化人了！</p><blockquote><p>题外话：<br>某位不愿透露姓名的陈焕茂同学表示，“自从用了故宫壁纸，随机切换，每天腰不酸、腿不…不是，每天都有新的惊喜！”</p></blockquote><p>以上！</p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;对于文化人来说，电脑桌面就是一个人的牌面。&lt;br&gt;——沃·兹基硕德&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
    
      <category term="Python" scheme="https://hybin.github.io/tags/Python/"/>
    
      <category term="爬虫" scheme="https://hybin.github.io/tags/%E7%88%AC%E8%99%AB/"/>
    
      <category term="macOS" scheme="https://hybin.github.io/tags/macOS/"/>
    
  </entry>
  
  <entry>
    <title>Sliding Window</title>
    <link href="https://hybin.github.io/2019/10/06/Sliding-Window/"/>
    <id>https://hybin.github.io/2019/10/06/Sliding-Window/</id>
    <published>2019-10-06T02:44:00.000Z</published>
    <updated>2019-11-12T07:09:48.823Z</updated>
    
    <content type="html"><![CDATA[<p>滑动窗口（Sliding Window），顾名思义，即在数组/字符串设计一个左边界和一个右边界，从而获得一定长度的子元素，在此基础上，窗口随着边界的变化而滑动。其用于解决数组/字符串的子元素问题，它可以将嵌套的循环问题，转换为单循环问题，降低时间复杂度。</p><a id="more"></a><p>我们可以通过一组题目来比较好的理解滑动窗口的应用：</p><h4 id="485-最大连续1的个数"><a href="#485-最大连续1的个数" class="headerlink" title="485. 最大连续1的个数"></a><a href="https://leetcode-cn.com/problems/max-consecutive-ones/" target="_blank" rel="noopener">485. 最大连续1的个数</a></h4><blockquote><p>给定一个二进制数组， 计算其中最大连续1的个数。</p></blockquote><h5 id="Simple-Test-Case"><a href="#Simple-Test-Case" class="headerlink" title="Simple Test Case"></a>Simple Test Case</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: [1,1,0,1,1,1]</span><br><span class="line">输出: 3</span><br><span class="line">解释: 开头的两位和最后的三位都是连续1，所以最大连续1的个数是 3.</span><br></pre></td></tr></table></figure><p>对于这道题，我们很容易能够想到暴力解决的办法，对数组内的元素遍历一次，设置变量<code>count</code>，逢1加1，逢0归0，最后得到最大的<code>count</code>。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">findMaxConsecutiveOnes</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> max_len = <span class="number">0</span>, count = <span class="number">0</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; nums.size(); ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> (nums[i] == <span class="number">1</span>) &#123;</span><br><span class="line">                count++;</span><br><span class="line">                max_len = max(max_len, count);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                count = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> max_len;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>那么，如果为其增加一个条件，比如可以最多将数组中的一个0翻转为1，那如何求最大长度呢？</p><h4 id="487-最大连续1的个数-II"><a href="#487-最大连续1的个数-II" class="headerlink" title="487. 最大连续1的个数 II"></a><a href="https://leetcode-cn.com/problems/max-consecutive-ones-ii/" target="_blank" rel="noopener">487. 最大连续1的个数 II</a></h4><blockquote><p>给定一个二进制数组，你可以最多将 1 个 0 翻转为 1，找出其中最大连续 1 的个数。</p></blockquote><h5 id="Simple-Test-Case-1"><a href="#Simple-Test-Case-1" class="headerlink" title="Simple Test Case"></a>Simple Test Case</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：[1,0,1,1,0]</span><br><span class="line">输出：4</span><br><span class="line">解释：翻转第一个 0 可以得到最长的连续 1。当翻转以后，最大连续 1 的个数为 4。</span><br></pre></td></tr></table></figure><p>这时，我们可以采取滑动窗口算法，计算窗口内元素1的数量，判断<code>窗口大小 - 元素1的数量 &gt; 1</code>是否为真，如果其为真，那么说明窗口内部包含1个以上的0元素，需要收紧窗口，最后求最大窗口即可。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">findMaxConsecutiveOnes</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> count = <span class="number">0</span>, l = <span class="number">0</span>, r = <span class="number">0</span>; r &lt; nums.size(); ++r) &#123;</span><br><span class="line">            <span class="keyword">if</span> (nums[r] == <span class="number">1</span>) count++;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">while</span> (r - l + <span class="number">1</span> - count &gt; <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (nums[l++]) --count;</span><br><span class="line">            &#125;</span><br><span class="line">            res = max(res, r - l + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </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><p>如果放宽限制，我们可以最多将<code>K</code>个值从0翻转为1呢？</p><h4 id="1004-最大连续1的个数-III"><a href="#1004-最大连续1的个数-III" class="headerlink" title="1004. 最大连续1的个数 III"></a><a href="https://leetcode-cn.com/problems/max-consecutive-ones-iii/" target="_blank" rel="noopener">1004. 最大连续1的个数 III</a></h4><blockquote><p>给定一个由若干<code>0</code>和<code>1</code>组成的数组<code>A</code>，我们最多可以将<code>K</code>个值从<code>0</code>变成<code>1</code>。</p><p>返回仅包含<code>1</code>的最长（连续）子数组的长度。</p></blockquote><h5 id="Simple-Test-Cases"><a href="#Simple-Test-Cases" class="headerlink" title="Simple Test Cases"></a>Simple Test Cases</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">输入：A &#x3D; [1,1,1,0,0,0,1,1,1,1,0], K &#x3D; 2</span><br><span class="line">输出：6</span><br><span class="line">解释： </span><br><span class="line">[1,1,1,0,0,1,1,1,1,1,1] 粗体数字从 0 翻转到 1，最长的子数组长度为 6。</span><br><span class="line">---------------</span><br><span class="line">输入：A &#x3D; [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K &#x3D; 3</span><br><span class="line">输出：10</span><br><span class="line">解释：</span><br><span class="line">[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]</span><br><span class="line">粗体数字从 0 翻转到 1，最长的子数组长度为 10。</span><br></pre></td></tr></table></figure><p>实际上，只是限制条件的变化，滑动窗口的思路与第二题是一样的，因此，</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">longestOnes</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; A, <span class="keyword">int</span> K)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> count = <span class="number">0</span>, l = <span class="number">0</span>, r = <span class="number">0</span>; r &lt; A.size(); ++r) &#123;</span><br><span class="line">            <span class="keyword">if</span> (A[r] == <span class="number">1</span>) count++;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">while</span> (r - l + <span class="number">1</span> - count &gt; K) &#123;</span><br><span class="line">                <span class="keyword">if</span> (A[l++]) --count;</span><br><span class="line">            &#125;</span><br><span class="line">            res = max(res, r - l + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </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>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;滑动窗口（Sliding Window），顾名思义，即在数组/字符串设计一个左边界和一个右边界，从而获得一定长度的子元素，在此基础上，窗口随着边界的变化而滑动。其用于解决数组/字符串的子元素问题，它可以将嵌套的循环问题，转换为单循环问题，降低时间复杂度。&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
  </entry>
  
  <entry>
    <title>Monostone Stack</title>
    <link href="https://hybin.github.io/2019/10/05/Monostone-Stack/"/>
    <id>https://hybin.github.io/2019/10/05/Monostone-Stack/</id>
    <published>2019-10-05T01:41:32.000Z</published>
    <updated>2019-11-12T07:09:28.535Z</updated>
    
    <content type="html"><![CDATA[<p>单调栈，顾名思义，满足单调性的栈结构。举个例子，存在一个栈结构，自底向上的元素为<code>[1, 3, 5, 10, 30, 50]</code>，那么，现欲将新元素<code>20</code>插入其中，需要率先弹出<code>[30, 50]</code>，再将元素<code>20</code>插入，用伪代码描述如下：</p><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">insert x</span><br><span class="line">while !stack.empty() &amp;&amp; stack.top &gt; x</span><br><span class="line">    stack.pop()</span><br><span class="line"></span><br><span class="line">stack.push(x)</span><br></pre></td></tr></table></figure><p>再来看实际应用，比如LeetCode中等难度题目<a href="https://leetcode-cn.com/problems/remove-k-digits/" target="_blank" rel="noopener">402. 移掉K位数字</a>。</p><h4 id="移掉K位数字"><a href="#移掉K位数字" class="headerlink" title="移掉K位数字"></a>移掉K位数字</h4><blockquote><p>给定一个以字符串表示的非负整数 num，移除这个数中的 k 位数字，使得剩下的数字最小。<br><strong>注意：</strong></p><ul><li>num 的长度小于 10002 且 ≥ k。</li><li>num 不会包含任何前导零。<h5 id="Simple-Test-Case"><a href="#Simple-Test-Case" class="headerlink" title="Simple Test Case"></a>Simple Test Case</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">输入: num &#x3D; &quot;1432219&quot;, k &#x3D; 3</span><br><span class="line">输出: &quot;1219&quot;</span><br><span class="line">解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。</span><br><span class="line">-----------</span><br><span class="line">输入: num &#x3D; &quot;10200&quot;, k &#x3D; 1</span><br><span class="line">输出: &quot;200&quot;</span><br><span class="line">解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。</span><br><span class="line">-----------</span><br><span class="line">输入: num &#x3D; &quot;10&quot;, k &#x3D; 2</span><br><span class="line">输出: &quot;0&quot;</span><br><span class="line">解释: 从原数字移除所有的数字，剩余为空就是0。</span><br></pre></td></tr></table></figure>对此，可以采用单调栈结构来择取最小的数字，C++代码如下：<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="built_in">string</span> <span class="title">removeKdigits</span><span class="params">(<span class="built_in">string</span> num, <span class="keyword">int</span> k)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">string</span> res;</span><br><span class="line">        <span class="keyword">int</span> n = num.size(), m = n - k;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">char</span> c : num) &#123;</span><br><span class="line">            <span class="keyword">while</span> (k &amp;&amp; res.size() &amp;&amp; res.back() &gt; c) &#123;</span><br><span class="line">                res.pop_back();</span><br><span class="line">                --k;</span><br><span class="line">            &#125;</span><br><span class="line">            res.push_back(c);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        res.resize(m);</span><br><span class="line">        <span class="comment">//去除前导0， 如10200，k = 1</span></span><br><span class="line">        <span class="keyword">while</span> (!res.empty() &amp;&amp; res[<span class="number">0</span>] == <span class="string">'0'</span>) &#123;</span><br><span class="line">            res.erase(res.begin());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res.empty() ? <span class="string">"0"</span> : res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>再来看单调递增的情况，比如LeetCode困难级别的题目<a href="https://leetcode-cn.com/problems/create-maximum-number/" target="_blank" rel="noopener">321. 拼接最大数</a>。<h4 id="拼接最大数"><a href="#拼接最大数" class="headerlink" title="拼接最大数"></a>拼接最大数</h4>给定长度分别为<code>m</code>和<code>n</code>的两个数组，其元素由<code>0-9</code>构成，表示两个自然数各位上的数字。现在从这两个数组中选出<code>k (k &lt;= m + n)</code>个数字拼接成一个新的数，要求从同一个数组中取出的数字保持其在原数组中的相对顺序。</li></ul><p>求满足该条件的最大数。结果返回一个表示该最大数的长度为<code>k</code>的数组。</p><p>说明: 请尽可能地优化你算法的时间和空间复杂度。</p></blockquote><h5 id="Simple-Test-Case-1"><a href="#Simple-Test-Case-1" class="headerlink" title="Simple Test Case"></a>Simple Test Case</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">输入:</span><br><span class="line">nums1 &#x3D; [3, 4, 6, 5]</span><br><span class="line">nums2 &#x3D; [9, 1, 2, 5, 8, 3]</span><br><span class="line">k &#x3D; 5</span><br><span class="line">输出:</span><br><span class="line">[9, 8, 6, 5, 3]</span><br><span class="line">-----------</span><br><span class="line">输入:</span><br><span class="line">nums1 &#x3D; [6, 7]</span><br><span class="line">nums2 &#x3D; [6, 0, 4]</span><br><span class="line">k &#x3D; 5</span><br><span class="line">输出:</span><br><span class="line">[6, 7, 6, 0, 4]</span><br><span class="line">-----------</span><br><span class="line">输入:</span><br><span class="line">nums1 &#x3D; [3, 9]</span><br><span class="line">nums2 &#x3D; [8, 9]</span><br><span class="line">k &#x3D; 3</span><br><span class="line">输出:</span><br><span class="line">[9, 8, 9]</span><br></pre></td></tr></table></figure><p>同样，一个简单的思路是，维护一个大小固定为<code>k</code>的单调栈，每次选出和最大的单调栈，最终得到拼接结果，C++代码如下：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; maxNumber(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums1, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums2, <span class="keyword">int</span> k) &#123;</span><br><span class="line">        <span class="keyword">int</span> m = nums1.size(), n = nums2.size();</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; res;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = max(<span class="number">0</span>, k - n); i &lt;= min(k, m); ++i) &#123;</span><br><span class="line">            res = max(res, mergeVector(maxVector(nums1, i), maxVector(nums2, k - i)));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> res;</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">vector</span>&lt;<span class="keyword">int</span>&gt; maxVector(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; nums, <span class="keyword">int</span> k) &#123;</span><br><span class="line">        <span class="keyword">int</span> drop = nums.size() - k;</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; res;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> num : nums) &#123;</span><br><span class="line">            <span class="keyword">while</span> (drop &amp;&amp; res.size() &amp;&amp; res.back() &lt; num) &#123;</span><br><span class="line">                res.pop_back();</span><br><span class="line">                --drop;</span><br><span class="line">            &#125;</span><br><span class="line">            res.push_back(num);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        res.resize(k);</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; mergeVector(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; nums1, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; nums2) &#123;</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; res;</span><br><span class="line">        <span class="keyword">while</span>(nums1.size() + nums2.size()) &#123;</span><br><span class="line">            <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;tmp = nums1 &gt; nums2 ? nums1 : nums2;</span><br><span class="line">            res.push_back(tmp[<span class="number">0</span>]);</span><br><span class="line">            tmp.erase(tmp.begin());</span><br><span class="line">        &#125;</span><br><span class="line">        </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></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;单调栈，顾名思义，满足单调性的栈结构。举个例子，存在一个栈结构，自底向上的元素为&lt;code&gt;[1, 3, 5, 10, 30, 50]&lt;/code&gt;，那么，现欲将新元素&lt;code&gt;20&lt;/code&gt;插入其中，需要率先弹出&lt;code&gt;[30, 50]&lt;/code&gt;，再将元素&lt;code&gt;20&lt;/code&gt;插入，用伪代码描述如下：&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="Algorithm" scheme="https://hybin.github.io/tags/Algorithm/"/>
    
  </entry>
  
  <entry>
    <title>Factorial with big numbers</title>
    <link href="https://hybin.github.io/2019/07/20/Factorial-with-big-numbers/"/>
    <id>https://hybin.github.io/2019/07/20/Factorial-with-big-numbers/</id>
    <published>2019-07-20T13:09:36.000Z</published>
    <updated>2019-11-12T07:08:55.268Z</updated>
    
    <content type="html"><![CDATA[<p>在数学中，正整数的阶乘（Factorial）被定义为所有小于及等于该数的正整数的积，其数学表示为$n!=1 \times 2 \times 3 \times … \times n$，同时，定义$0!=1$，$1!=1$。当$n$比较小时，可以很方便地借助递归函数，求得其阶乘值，比如：</p><a id="more"></a><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="title">factorial</span><span class="params">(<span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (n &gt; <span class="number">1</span>) ? n * factorial(n - <span class="number">1</span>) : <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>然而，当$n$比较大时，其阶乘值极大，不容易通过上述递归函数求得，比如：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Input:  100</span><br><span class="line">Output: 933262154439441526816992388562667004-</span><br><span class="line">        907159682643816214685929638952175999-</span><br><span class="line">        932299156089414639761565182862536979-</span><br><span class="line">        208272237582511852109168640000000000-</span><br><span class="line">        00000000000000</span><br><span class="line">Input:  50</span><br><span class="line">Output: 3041409320171337804361260816606476884-</span><br><span class="line">        4377641568960512000000000000</span><br></pre></td></tr></table></figure></p><p>因此，必须考虑新的方法。我们可以定义一个大小为<code>MAX</code>的数组，即<code>res[MAX]</code>，而后令正整数<code>x</code>乘以<code>res[X]</code>，并更新<code>res[X]</code>的值，比如：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"># 存在一个正整数36，其表示方法为：</span><br><span class="line">res[] &#x3D; [6, 3]  # size &#x3D; 2</span><br><span class="line"></span><br><span class="line"># 存在因数2</span><br><span class="line">x &#x3D; 2</span><br><span class="line"></span><br><span class="line"># 初始化</span><br><span class="line">carry &#x3D; 0</span><br><span class="line"></span><br><span class="line"># stop iff i &#x3D;&#x3D; 2</span><br><span class="line">i &#x3D; 0, product &#x3D; res[0] * x + carry &#x3D; 12 &#x3D;&gt; res[0] &#x3D; 2, carry &#x3D; 1</span><br><span class="line">i +&#x3D; 1</span><br><span class="line"></span><br><span class="line"># update</span><br><span class="line">i &#x3D; 1, product &#x3D; res[1] * x + carry &#x3D; 7  &#x3D;&gt; res[1] &#x3D; 7, carry &#x3D; 0</span><br><span class="line"></span><br><span class="line">res[] &#x3D; [2, 7]</span><br></pre></td></tr></table></figure><p>于是，对于大数的阶乘算法，以C++代码表示：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX 500</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">multiply</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> res[], <span class="keyword">int</span> res_size)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> carry = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k &lt; res_size; ++k) &#123;</span><br><span class="line">        <span class="keyword">int</span> prod = res[k] * i + carry;</span><br><span class="line">        res[k] = prod % <span class="number">10</span>;</span><br><span class="line">        carry = prod / <span class="number">10</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> (carry) &#123;</span><br><span class="line">        res[res_size] = carry % <span class="number">10</span>;</span><br><span class="line">        carry = carry / <span class="number">10</span>;</span><br><span class="line">        res_size++;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> res_size;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="built_in">string</span> <span class="title">factorial</span><span class="params">(<span class="keyword">int</span> factorial)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> res[MAX];</span><br><span class="line">    <span class="built_in">string</span> out;</span><br><span class="line">    </span><br><span class="line">    res[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span> res_size = <span class="number">1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">2</span>; i &lt;= factorial; ++i) &#123;</span><br><span class="line">        res_size = multiply(i, res, res_size);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> j = res_size - <span class="number">1</span>; j &gt;= <span class="number">0</span>; --j) &#123;</span><br><span class="line">        out += to_string(res[j]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> out;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>原题来源：<a href="https://www.codewars.com/kata/large-factorials/cpp" target="_blank" rel="noopener">Codewars - Large Factorials</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在数学中，正整数的阶乘（Factorial）被定义为所有小于及等于该数的正整数的积，其数学表示为$n!=1 \times 2 \times 3 \times … \times n$，同时，定义$0!=1$，$1!=1$。当$n$比较小时，可以很方便地借助递归函数，求得其阶乘值，比如：&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>Dynamic Programming</title>
    <link href="https://hybin.github.io/2019/07/12/Dynamic-Programming/"/>
    <id>https://hybin.github.io/2019/07/12/Dynamic-Programming/</id>
    <published>2019-07-12T06:48:18.000Z</published>
    <updated>2019-11-12T07:08:32.096Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Those who cannot remember the past are condemned to repeat it.</p></blockquote><a id="more"></a><p>动态规划(Dynamic Programming)算法的核心是记住已经解决过的字问题的解。如何理解呢？举个例子：</p><p>假如我们要求斐波那契数列中的第<code>n</code>个数，一种简单的求法是：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">fib</span><span class="params">(<span class="keyword">int</span> &amp;n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (n &lt;= <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> n;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> fib(n<span class="number">-1</span>) + fib(n<span class="number">-2</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>显然，借助递归来求斐波那契数列，其复杂度是指数级的，如果要降低复杂度呢？很简单，利用动态规划：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">fib</span><span class="params">(<span class="keyword">int</span> &amp;n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; f;</span><br><span class="line">    f[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">    f[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">2</span>; i &lt;=n; ++i) &#123;</span><br><span class="line">        f[i] = f[i<span class="number">-1</span>] + f[i<span class="number">-2</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> f[n];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>你是一个专业的小偷，计划偷窃沿街的房屋。每间房内都藏有一定的现金，影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统，<strong>如果两间相邻的房屋在同一晚上被小偷闯入，系统会自动报警</strong>。</p><p>给定一个代表每个房屋存放金额的非负整数数组，计算你在<strong>不触动警报装置的情况下</strong>，能够偷窃到的最高金额。</p><p>示例：<br>输入：[1, 2, 3, 1]<br>输出：4 = 1 + 3</p></blockquote><p>对于小偷来说，需要考虑当前的房子能够带来的收益，同时，至少到第3间房，才能开始动手，故而前两间房子的初始收益可以设置为<code>dp = {0, 0}</code>。</p><p>接下来，如果偷一间房，那么其收益为<code>dp[i - 2] + 当前房子的收益</code>，反之，则收益为<code>dp[i - 1]</code>。故而，其转移状态方程为：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dp[i] &#x3D; max(dp[i - 2] + nums[i - 2], dp[i - 1]);</span><br></pre></td></tr></table></figure></p><p><strong>代码</strong><br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;algorithm&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">rob</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;nums)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; dp(nums.size() + <span class="number">2</span>, <span class="number">0</span>); <span class="comment">// &#123;0, 0, 0, ... , 0&#125;</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">2</span>; i &lt; nums.size() + <span class="number">2</span>; ++i) &#123;</span><br><span class="line">            dp[i] = max(dp[i<span class="number">-2</span>] + nums[i<span class="number">-2</span>], dp[i<span class="number">-1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> dp[nums.size()];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><p>再来看著名的硬币问题，即给定一定数量的硬币<code>[1, 5, 10]</code>，针对自然数<code>N</code>，给出其所有可能的组合方案，比如：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Input:  N &#x3D; 12</span><br><span class="line">        coins &#x3D; [1, 5, 10]</span><br><span class="line"></span><br><span class="line">Output: 4</span><br><span class="line"></span><br><span class="line">Explanation: 1 way: 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 &#x3D; 12</span><br><span class="line">             2 way: 1 + 1 + 1 + 1 + 1 + 1 + 5 + 1 + 1 &#x3D; 12</span><br><span class="line">             3 way: 5 + 5 + 1 + 1 &#x3D; 12</span><br><span class="line">             4 way: 10 + 1 + 1 &#x3D; 12</span><br></pre></td></tr></table></figure><p>从动态规划的角度看，5个1便士可以换成1个5便士，10个1便士可以换成2个5便士或1个10便士，那么针对自然数N以内的所有求解方案，都可以基于该换算进行。比如4便士只能由4个1便士组成，6便士则既可以用6个1便士，也可以将前5个1便士替换为1个5便士，那么自然数12以内的组合如下所示:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">N: 1  2  3  4  5  6  7  8  9  10 11 12</span><br><span class="line"></span><br><span class="line">1: 1  1  1  1  1  1  1  1  1  1  1  1</span><br><span class="line"></span><br><span class="line">2: 0  0  0  0  5  1  1  1  1  1  1  1     ($5&#x3D;1 \times 5$）</span><br><span class="line"></span><br><span class="line">3: 0  0  0  0  5  0  0  0  0  5  1  1     </span><br><span class="line"></span><br><span class="line">4: 0  0  0  0  0  0  0  0  0  10 1  1     ($10&#x3D; 1 \times 10&#x3D;5 \times 2$)</span><br></pre></td></tr></table></figure></p><p>接下来，用<code>C++</code>代码求解：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> <span class="title">countChange</span><span class="params">(<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">int</span> money, <span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">unsigned</span> <span class="keyword">int</span>&gt;&amp; coins)</span> </span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span>&gt; ways(money + <span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Initialization</span></span><br><span class="line">    ways[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;coin : coins) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; money + <span class="number">1</span>; ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> (coin &lt;= i) &#123;</span><br><span class="line">                ways[i] += ways[i - coin];</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> ways[money];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Those who cannot remember the past are condemned to repeat it.&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
    
      <category term="C++" scheme="https://hybin.github.io/tags/C/"/>
    
      <category term="动态规划" scheme="https://hybin.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
    
  </entry>
  
  <entry>
    <title>利用XGBoost进行NLP分类任务</title>
    <link href="https://hybin.github.io/2019/03/26/%E5%88%A9%E7%94%A8XGBoost%E8%BF%9B%E8%A1%8CNLP%E5%88%86%E7%B1%BB%E4%BB%BB%E5%8A%A1/"/>
    <id>https://hybin.github.io/2019/03/26/%E5%88%A9%E7%94%A8XGBoost%E8%BF%9B%E8%A1%8CNLP%E5%88%86%E7%B1%BB%E4%BB%BB%E5%8A%A1/</id>
    <published>2019-03-26T08:04:22.000Z</published>
    <updated>2019-11-12T07:13:40.244Z</updated>
    
    <content type="html"><![CDATA[<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default"></script><h4 id="Task"><a href="#Task" class="headerlink" title="Task"></a>Task</h4><blockquote><p><strong>Semantic Relation Extraction and Classification in Scientific Papers</strong></p><p>Subtask: 1 - Relation classification</p><p>1.1 Relation classification on clean data</p><p>1.2 Relation classification on noisy data</p></blockquote><a id="more"></a><h4 id="Classes-Semantic-Relations"><a href="#Classes-Semantic-Relations" class="headerlink" title="Classes - Semantic Relations"></a>Classes - Semantic Relations</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">relations = [<span class="string">'usage'</span>, <span class="string">'result'</span>, <span class="string">'model-feature'</span>, <span class="string">'part-whole'</span>, <span class="string">'topic'</span>, <span class="string">'comparison'</span>]</span><br></pre></td></tr></table></figure><h4 id="Features"><a href="#Features" class="headerlink" title="Features"></a>Features</h4><p><strong>Lexical Features</strong></p><div class="table-container"><table><thead><tr><th>Features</th><th>Remarks</th><th>Value</th></tr></thead><tbody><tr><td>L1</td><td>Distance which shows the distances between entities</td><td>Int</td></tr><tr><td>L2</td><td>hasIn(Model-Feature, Part-Whole)</td><td>int(0, 1)</td></tr><tr><td>L3</td><td>hasOf(Topic, Result)</td><td>Int(0, 1)</td></tr><tr><td>L4</td><td>hasFor(Usage)</td><td>Int(0, 1)</td></tr><tr><td>L5</td><td>hasWith(Compare)</td><td>int(0, 1)</td></tr><tr><td>L6</td><td>hasThan(Compare)</td><td>Int(0, 1)</td></tr><tr><td>L7</td><td>hasAnd</td><td>Int(0, 1)</td></tr><tr><td>L8</td><td>hasFrom</td><td>Int(0, 1)</td></tr></tbody></table></div><p><strong>Entity Features</strong></p><div class="table-container"><table><thead><tr><th>Features</th><th>Remarks</th><th>Value</th></tr></thead><tbody><tr><td>L1</td><td>For comparison, it’s necessary to measure Similarity(sim200)</td><td>Float</td></tr><tr><td>L2</td><td>Similarity Bucket</td><td>int(0, 1, 2, 3, 4)</td></tr><tr><td>L3</td><td>Position of Entity (Text)</td><td>LabelEnocder (Text Index)</td></tr><tr><td>L4</td><td>Start Entity</td><td>Index</td></tr><tr><td>L5</td><td>End Entity</td><td>Index</td></tr></tbody></table></div><p>数据预处理</p><ul><li><p>input format</p><figure class="highlight python"><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">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line">np.array[[...feature_values...label],...]</span><br></pre></td></tr></table></figure></li><li><p>output format <code>.csv</code></p></li></ul><h4 id="Model-Training"><a href="#Model-Training" class="headerlink" title="Model Training"></a>Model Training</h4><p>It seems to be better to use XGBoost as well as Scikit-Learning. In other words, use XGB.fit() rather then XGB.train().</p><h4 id="Accuracy"><a href="#Accuracy" class="headerlink" title="Accuracy"></a>Accuracy</h4>]]></content>
    
    <summary type="html">
    
      &lt;script type=&quot;text/javascript&quot; src=&quot;http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default&quot;&gt;&lt;/script&gt;

&lt;h4 id=&quot;Task&quot;&gt;&lt;a href=&quot;#Task&quot; class=&quot;headerlink&quot; title=&quot;Task&quot;&gt;&lt;/a&gt;Task&lt;/h4&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Semantic Relation Extraction and Classification in Scientific Papers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Subtask: 1 - Relation classification&lt;/p&gt;
&lt;p&gt;1.1 Relation classification on clean data&lt;/p&gt;
&lt;p&gt;1.2 Relation classification on noisy data&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
    
      <category term="机器学习" scheme="https://hybin.github.io/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
      <category term="自然语言处理" scheme="https://hybin.github.io/tags/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"/>
    
      <category term="XGBoost" scheme="https://hybin.github.io/tags/XGBoost/"/>
    
  </entry>
  
</feed>
