<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Aoitsuki</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://aoiblog.top/</id>
  <link href="https://aoiblog.top/" rel="alternate"/>
  <link href="https://aoiblog.top/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Aoitsuki</rights>
  <subtitle>乌幕屏，海相续，举眼茫茫，静听细浪语</subtitle>
  <title>Aoitsuki</title>
  <updated>2026-05-06T16:00:00.000Z</updated>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>TypeScript 语法</h2>> 适合读者：已经学过 HTML、CSS、JavaScript，想系统掌握 TypeScript 常用语法，并能把它用到前端开发里。>> 学习目标：不是重新学习 JavaScript，而是掌握 TypeScript 如何通过“类型”帮你提前发现错误、写清楚数据结构、提升代码提示和维护性。<h2 id="0-学习前先建立一个认识"><a href="#0-学习前先建立一个认识" class="headerlink" title="0. 学习前先建立一个认识"></a>0. 学习前先建立一个认识</h2><p>TypeScript 可以理解为 JavaScript 的“带类型版本”。</p><p>你写的 TypeScript 最后还是会被编译成 JavaScript，然后再交给浏览器或 Node.js 运行。TypeScript 本身主要做两件事：</p><ul><li>在代码运行前检查类型错误。</li><li>让编辑器更清楚变量、函数、对象、接口数据到底长什么样。</li></ul><p>例如 JavaScript 里这段代码只有运行时才可能发现问题：</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><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">getUserName</span>(<span class="hljs-params">user</span>) &#123;<br>  <span class="hljs-keyword">return</span> user.<span class="hljs-property">name</span>.<span class="hljs-title function_">toUpperCase</span>();<br>&#125;<br><br><span class="hljs-title function_">getUserName</span>(<span class="hljs-literal">null</span>);<br></code></pre></td></tr></table></figure><p>TypeScript 会更早提醒你：<code>user</code> 可能不是一个合法对象。</p><hr><h2 id="1-TypeScript-是什么"><a href="#1-TypeScript-是什么" class="headerlink" title="1. TypeScript 是什么"></a>1. TypeScript 是什么</h2><h3 id="1-标准语法格式"><a href="#1-标准语法格式" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>TypeScript 的基本写法是在 JavaScript 语法基础上增加类型标注：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 变量名: 类型 = 值;<br><br><span class="hljs-keyword">function</span> 函数名(参数名: 参数类型): 返回值类型 &#123;<br>  <span class="hljs-keyword">return</span> 返回值;<br>&#125;<br><br><span class="hljs-keyword">type</span> 类型名 = 类型结构;<br><br><span class="hljs-keyword">interface</span> 接口名 &#123;<br>  属性名: 属性类型;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子"><a href="#2-基础例子" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">username</span>: <span class="hljs-built_in">string</span> = <span class="hljs-string">&quot;Alice&quot;</span>;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">sayHello</span>(<span class="hljs-params"><span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;Hello, &quot;</span> + name;<br>&#125;<br><br><span class="hljs-keyword">type</span> <span class="hljs-title class_">User</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子"><a href="#3-实际开发例子" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">Product</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">price</span>: <span class="hljs-built_in">number</span>;<br>&#125;;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">renderProduct</span>(<span class="hljs-params"><span class="hljs-attr">product</span>: <span class="hljs-title class_">Product</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;product.title&#125;</span>: ￥<span class="hljs-subst">$&#123;product.price&#125;</span>`</span>;<br>&#125;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">product</span>: <span class="hljs-title class_">Product</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br>  <span class="hljs-attr">title</span>: <span class="hljs-string">&quot;键盘&quot;</span>,<br>  <span class="hljs-attr">price</span>: <span class="hljs-number">199</span>,<br>&#125;;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">renderProduct</span>(product));<br></code></pre></td></tr></table></figure><h3 id="4-语法理解"><a href="#4-语法理解" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>TypeScript 的核心不是让代码变复杂，而是让数据的形状更明确。你告诉 TS 一个变量、参数或对象应该是什么类型，TS 就能帮你检查错误。</p><p>比如 <code>Product</code> 规定商品必须有 <code>id</code>、<code>title</code>、<code>price</code>。如果你少写一个属性，或者把 <code>price</code> 写成字符串，编辑器就会提前报错。</p><h3 id="5-常见错误"><a href="#5-常见错误" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>以为 TypeScript 会改变 JavaScript 的运行逻辑。实际上 TS 编译后还是 JS。</li><li>以为所有地方都必须手写类型。很多简单变量可以交给 TS 自动推断。</li><li>以为加了类型就不会有运行时错误。类型检查主要发生在开发阶段，真实数据仍然需要校验。</li></ul><h3 id="6-本节练习"><a href="#6-本节练习" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>Book</code> 类型，包含：</p><ul><li><code>id: number</code></li><li><code>title: string</code></li><li><code>author: string</code></li><li><code>isPublished: boolean</code></li></ul><p>然后写一个 <code>getBookTitle(book: Book): string</code> 函数，返回书名。</p><h3 id="7-和-JavaScript-的关系"><a href="#7-和-JavaScript-的关系" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>这一节对应 JavaScript 里的变量、函数、对象写法。TypeScript 没有发明一套新运行机制，而是在 JS 语法旁边补充类型信息，让编辑器和编译器提前知道数据长什么样。</p><hr><h2 id="1-5-开发环境与编译流程"><a href="#1-5-开发环境与编译流程" class="headerlink" title="1.5 开发环境与编译流程"></a>1.5 开发环境与编译流程</h2><h3 id="1-标准语法格式-1"><a href="#1-标准语法格式-1" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>安装 TypeScript：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">npm i <span class="hljs-literal">-g</span> typescript<br></code></pre></td></tr></table></figure><p>编译单个文件：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">tsc 文件名.ts<br></code></pre></td></tr></table></figure><p>监听单个文件变化：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">tsc 文件名.ts <span class="hljs-literal">-w</span><br></code></pre></td></tr></table></figure><p>编译整个项目：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">tsc<br></code></pre></td></tr></table></figure><p>项目配置文件：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;compilerOptions&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;target&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ES2020&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;module&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ESNext&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;strict&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;rootDir&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;src&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;outDir&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;dist&quot;</span><br>  <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;include&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;src/**/*.ts&quot;</span><span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><h3 id="2-基础例子-1"><a href="#2-基础例子-1" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><p>创建一个 <code>hello.ts</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span> = <span class="hljs-string">&quot;Hello TypeScript&quot;</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(message);<br></code></pre></td></tr></table></figure><p>编译：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">tsc hello.ts<br></code></pre></td></tr></table></figure><p>编译后会得到 <code>hello.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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> message = <span class="hljs-string">&quot;Hello TypeScript&quot;</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(message);<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-1"><a href="#3-实际开发例子-1" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><p>一个简单项目可以这样组织：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs txt">project<br>├─ src<br>│  └─ main.ts<br>├─ dist<br>└─ tsconfig.json<br></code></pre></td></tr></table></figure><p><code>src/main.ts</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">greet</span>(<span class="hljs-params"><span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello, <span class="hljs-subst">$&#123;name&#125;</span>`</span>;<br>&#125;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">greet</span>(<span class="hljs-string">&quot;Alice&quot;</span>));<br></code></pre></td></tr></table></figure><p>执行：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">tsc <span class="hljs-literal">-w</span><br></code></pre></td></tr></table></figure><p>这样 TS 编译器会持续监听 <code>src</code> 里的文件变化，并把结果输出到 <code>dist</code>。</p><h3 id="4-语法理解-1"><a href="#4-语法理解-1" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>TypeScript 文件不能直接被普通浏览器当作最终代码执行。开发时你写 <code>.ts</code>，编译器检查类型并输出 <code>.js</code>，真正运行的是 JavaScript。</p><p>常见流程是：</p><ol><li>写 TypeScript 源码。</li><li>使用 <code>tsc</code> 或构建工具编译。</li><li>得到 JavaScript。</li><li>浏览器或 Node.js 运行 JavaScript。</li></ol><h3 id="5-常见错误-1"><a href="#5-常见错误-1" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>以为安装 TypeScript 后浏览器就能直接运行 <code>.ts</code> 文件。</li><li>忘记创建 <code>tsconfig.json</code>，导致直接执行 <code>tsc</code> 时不知道项目配置。</li><li><code>tsc -w</code> 只是监听并编译，不会自动刷新浏览器页面。</li><li>真实项目里通常还会配合 Vite、Webpack、Babel 等工具，但初学阶段先理解 <code>tsc</code> 即可。</li></ul><h3 id="6-本节练习-1"><a href="#6-本节练习-1" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>创建一个 <code>src/main.ts</code>，写一个带类型的 <code>add(a: number, b: number): number</code> 函数。再创建 <code>tsconfig.json</code>，让编译结果输出到 <code>dist</code>。</p><h3 id="7-和-JavaScript-的关系-1"><a href="#7-和-JavaScript-的关系-1" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>JavaScript 可以直接运行，TypeScript 需要先编译成 JavaScript。TS 的类型标注会在编译后消失，最终 JS 代码里不会保留 <code>: string</code>、<code>: number</code> 这些类型信息。</p><hr><h2 id="2-变量类型"><a href="#2-变量类型" class="headerlink" title="2. 变量类型"></a>2. 变量类型</h2><h3 id="1-标准语法格式-2"><a href="#1-标准语法格式-2" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 变量名: 类型 = 值;<br><span class="hljs-keyword">const</span> 常量名: 类型 = 值;<br><br><span class="hljs-keyword">let</span> 字符串: <span class="hljs-built_in">string</span> = <span class="hljs-string">&quot;文本&quot;</span>;<br><span class="hljs-keyword">let</span> 数字: <span class="hljs-built_in">number</span> = <span class="hljs-number">123</span>;<br><span class="hljs-keyword">let</span> 布尔值: <span class="hljs-built_in">boolean</span> = <span class="hljs-literal">true</span>;<br><span class="hljs-keyword">let</span> 空值: <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;<br><span class="hljs-keyword">let</span> 未定义: <span class="hljs-literal">undefined</span> = <span class="hljs-literal">undefined</span>;<br><span class="hljs-keyword">let</span> 大整数: <span class="hljs-built_in">bigint</span> = <span class="hljs-number">100n</span>;<br></code></pre></td></tr></table></figure><p>如果 TS 能自动判断类型，可以省略类型标注：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 变量名 = 值;<br></code></pre></td></tr></table></figure><p>其他常见基础类型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 任意值: <span class="hljs-built_in">any</span> = 值;<br><span class="hljs-keyword">let</span> 未知值: <span class="hljs-built_in">unknown</span> = 值;<br><br><span class="hljs-keyword">function</span> 没有返回值(): <span class="hljs-built_in">void</span> &#123;&#125;<br><br><span class="hljs-keyword">function</span> 永远不会正常结束(): <span class="hljs-built_in">never</span> &#123;<br>  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&quot;错误&quot;</span>);<br>&#125;<br><br><span class="hljs-keyword">let</span> 对象值: <span class="hljs-built_in">object</span> = &#123;&#125;;<br><br><span class="hljs-keyword">enum</span> 枚举名 &#123;<br>  成员<span class="hljs-number">1</span>,<br>  成员<span class="hljs-number">2</span>,<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-2"><a href="#2-基础例子-2" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span> = <span class="hljs-string">&quot;Tom&quot;</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span> = <span class="hljs-number">18</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-attr">isLogin</span>: <span class="hljs-built_in">boolean</span> = <span class="hljs-literal">false</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-attr">largeId</span>: <span class="hljs-built_in">bigint</span> = <span class="hljs-number">9007199254740993n</span>;<br><br><span class="hljs-keyword">let</span> city = <span class="hljs-string">&quot;Shanghai&quot;</span>;<br><span class="hljs-keyword">let</span> count = <span class="hljs-number">10</span>;<br><br><span class="hljs-keyword">let</span> <span class="hljs-attr">value</span>: <span class="hljs-built_in">unknown</span> = <span class="hljs-string">&quot;hello&quot;</span>;<br><br><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> value === <span class="hljs-string">&quot;string&quot;</span>) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(value.<span class="hljs-title function_">toUpperCase</span>());<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">showMessage</span>(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(message);<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">fail</span>(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">never</span> &#123;<br>  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(message);<br>&#125;<br><br><span class="hljs-keyword">enum</span> <span class="hljs-title class_">Direction</span> &#123;<br>  <span class="hljs-title class_">Up</span>,<br>  <span class="hljs-title class_">Down</span>,<br>  <span class="hljs-title class_">Left</span>,<br>  <span class="hljs-title class_">Right</span>,<br>&#125;<br></code></pre></td></tr></table></figure><p>这里 <code>city</code> 会被自动推断为 <code>string</code>，<code>count</code> 会被自动推断为 <code>number</code>。</p><h3 id="3-实际开发例子-2"><a href="#3-实际开发例子-2" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> input = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#username&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>;<br><br><span class="hljs-keyword">let</span> <span class="hljs-attr">username</span>: <span class="hljs-built_in">string</span> = input.<span class="hljs-property">value</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-attr">usernameLength</span>: <span class="hljs-built_in">number</span> = username.<span class="hljs-property">length</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-attr">isValid</span>: <span class="hljs-built_in">boolean</span> = usernameLength &gt;= <span class="hljs-number">3</span>;<br><br><span class="hljs-keyword">if</span> (isValid) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;用户名合法&quot;</span>);<br>&#125; <span class="hljs-keyword">else</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;用户名至少需要 3 个字符&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>不确定接口返回值时，可以先用 <code>unknown</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">rawData</span>: <span class="hljs-built_in">unknown</span> = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(<span class="hljs-string">&#x27;&#123;&quot;name&quot;:&quot;Alice&quot;&#125;&#x27;</span>);<br><br><span class="hljs-keyword">if</span> (<br>  <span class="hljs-keyword">typeof</span> rawData === <span class="hljs-string">&quot;object&quot;</span> &amp;&amp;<br>  rawData !== <span class="hljs-literal">null</span> &amp;&amp;<br>  <span class="hljs-string">&quot;name&quot;</span> <span class="hljs-keyword">in</span> rawData<br>) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;这是一个带 name 的对象&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>状态值更推荐字面量联合类型，而不是默认使用 <code>enum</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">Theme</span> = <span class="hljs-string">&quot;light&quot;</span> | <span class="hljs-string">&quot;dark&quot;</span>;<br><br><span class="hljs-keyword">let</span> <span class="hljs-attr">theme</span>: <span class="hljs-title class_">Theme</span> = <span class="hljs-string">&quot;light&quot;</span>;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-2"><a href="#4-语法理解-2" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>变量类型用来限制变量后续能存什么值。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span> = <span class="hljs-number">18</span>;<br>age = <span class="hljs-number">20</span>;<br><span class="hljs-comment">// age = &quot;20&quot;; // 错误：不能把 string 赋值给 number</span><br></code></pre></td></tr></table></figure><p>TypeScript 的类型推断很有用。像下面这种代码没必要重复写类型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> message = <span class="hljs-string">&quot;提交成功&quot;</span>;<br></code></pre></td></tr></table></figure><p>TS 已经知道 <code>message</code> 是字符串。</p><p>几个容易混淆的类型：</p><ul><li><code>any</code>：关闭类型检查，什么都能做，初学时要少用。</li><li><code>unknown</code>：不知道是什么类型，但使用前必须判断，比 <code>any</code> 安全。</li><li><code>void</code>：函数没有返回值。</li><li><code>never</code>：函数永远不会正常返回，比如一定会抛错或死循环。</li><li><code>object</code>：只能表示“非原始类型对象”，通常不如具体对象类型实用。</li><li><code>bigint</code>：表示非常大的整数，字面量后面要加 <code>n</code>。</li><li><code>enum</code>：枚举，能给一组常量命名，但前端业务状态很多时候用字面量联合类型更轻量。</li></ul><h3 id="5-常见错误-2"><a href="#5-常见错误-2" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>number</code> 同时表示整数和小数，没有单独的 <code>int</code> 或 <code>float</code>。</li><li>类型名要小写：<code>string</code>、<code>number</code>、<code>boolean</code>，不是 <code>String</code>、<code>Number</code>、<code>Boolean</code>。</li><li>给变量标注过窄类型后，后续不能随便赋其他类型的值。</li><li>不要为了省事到处写 <code>any</code>，它会让 TS 失去检查意义。</li><li><code>unknown</code> 不能直接调用方法，要先做类型判断。</li><li><code>enum</code> 可以认识，但不是所有状态都必须用枚举。</li></ul><h3 id="6-本节练习-2"><a href="#6-本节练习-2" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义下面几个变量，并写上合适类型：</p><ul><li>商品名称</li><li>商品价格</li><li>是否有库存</li><li>用户积分</li></ul><p>再定义一个 <code>parseValue(value: unknown): string</code> 函数，如果传入字符串就返回原字符串，否则返回 <code>&quot;无法处理&quot;</code>。</p><h3 id="7-和-JavaScript-的关系-2"><a href="#7-和-JavaScript-的关系-2" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>JavaScript 变量可以在运行过程中放入不同类型的值；TypeScript 会把这种自由收窄成更明确的约束。<code>any</code> 接近 JS 的自由写法，<code>unknown</code> 则是在 JS 动态数据外面加了一层安全检查。</p><hr><h2 id="3-数组和元组"><a href="#3-数组和元组" class="headerlink" title="3. 数组和元组"></a>3. 数组和元组</h2><h3 id="1-标准语法格式-3"><a href="#1-标准语法格式-3" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>数组有两种常见写法：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 数组名: 元素类型[] = [元素<span class="hljs-number">1</span>, 元素<span class="hljs-number">2</span>];<br><span class="hljs-keyword">let</span> 数组名: <span class="hljs-title class_">Array</span>&lt;元素类型&gt; = [元素<span class="hljs-number">1</span>, 元素<span class="hljs-number">2</span>];<br></code></pre></td></tr></table></figure><p>元组用于表示固定长度、固定顺序、每一位类型明确的数组：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 元组名: [类型<span class="hljs-number">1</span>, 类型<span class="hljs-number">2</span>] = [值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>];<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-3"><a href="#2-基础例子-3" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">scores</span>: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">90</span>, <span class="hljs-number">85</span>, <span class="hljs-number">100</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-attr">names</span>: <span class="hljs-title class_">Array</span>&lt;<span class="hljs-built_in">string</span>&gt; = [<span class="hljs-string">&quot;Alice&quot;</span>, <span class="hljs-string">&quot;Bob&quot;</span>];<br><br><span class="hljs-keyword">let</span> <span class="hljs-attr">userInfo</span>: [<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>] = [<span class="hljs-string">&quot;Tom&quot;</span>, <span class="hljs-number">18</span>];<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-3"><a href="#3-实际开发例子-3" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">Todo</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">done</span>: <span class="hljs-built_in">boolean</span>;<br>&#125;;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">todos</span>: <span class="hljs-title class_">Todo</span>[] = [<br>  &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">&quot;学习 TypeScript&quot;</span>, <span class="hljs-attr">done</span>: <span class="hljs-literal">false</span> &#125;,<br>  &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">&quot;完成练习&quot;</span>, <span class="hljs-attr">done</span>: <span class="hljs-literal">true</span> &#125;,<br>];<br><br><span class="hljs-keyword">const</span> unfinishedTodos = todos.<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">todo</span>) =&gt;</span> !todo.<span class="hljs-property">done</span>);<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-3"><a href="#4-语法理解-3" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>数组类型规定了数组里每个元素的类型。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">nums</span>: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br><span class="hljs-comment">// nums.push(&quot;4&quot;); // 错误：不能把 string 放进 number[]</span><br></code></pre></td></tr></table></figure><p>元组适合表达“位置有含义”的数据。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">point</span>: [<span class="hljs-built_in">number</span>, <span class="hljs-built_in">number</span>] = [<span class="hljs-number">100</span>, <span class="hljs-number">200</span>];<br></code></pre></td></tr></table></figure><p>这里第一位可以表示 x 坐标，第二位表示 y 坐标。</p><h3 id="5-常见错误-3"><a href="#5-常见错误-3" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>string[]</code> 表示字符串数组，不是字符串本身。</li><li>元组的顺序不能乱：<code>[string, number]</code> 和 <code>[number, string]</code> 不是一回事。</li><li>普通列表优先用数组，对固定结构才用元组。</li></ul><h3 id="6-本节练习-3"><a href="#6-本节练习-3" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>students</code> 数组，每个学生包含：</p><ul><li><code>id: number</code></li><li><code>name: string</code></li><li><code>score: number</code></li></ul><p>然后筛选出分数大于等于 60 的学生。</p><h3 id="7-和-JavaScript-的关系-3"><a href="#7-和-JavaScript-的关系-3" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>数组方法仍然是 JavaScript 的 <code>map</code>、<code>filter</code>、<code>find</code>、<code>reduce</code>。TypeScript 的增强点是：数组元素类型确定后，回调函数里的参数类型会自动推断出来。</p><hr><h2 id="4-函数类型"><a href="#4-函数类型" class="headerlink" title="4. 函数类型"></a>4. 函数类型</h2><h3 id="1-标准语法格式-4"><a href="#1-标准语法格式-4" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>普通函数：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> 函数名(参数名: 参数类型): 返回值类型 &#123;<br>  <span class="hljs-keyword">return</span> 返回值;<br>&#125;<br></code></pre></td></tr></table></figure><p>可选参数：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> 函数名(参数名?: 参数类型): 返回值类型 &#123;<br>  <span class="hljs-keyword">return</span> 返回值;<br>&#125;<br></code></pre></td></tr></table></figure><p>默认参数：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> 函数名(参数名: 参数类型 = 默认值): 返回值类型 &#123;<br>  <span class="hljs-keyword">return</span> 返回值;<br>&#125;<br></code></pre></td></tr></table></figure><p>箭头函数：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> 函数名 = (参数名: 参数类型): 返回值类型 =&gt; &#123;<br>  <span class="hljs-keyword">return</span> 返回值;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-4"><a href="#2-基础例子-4" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">add</span>(<span class="hljs-params"><span class="hljs-attr">a</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">b</span>: <span class="hljs-built_in">number</span></span>): <span class="hljs-built_in">number</span> &#123;<br>  <span class="hljs-keyword">return</span> a + b;<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">greet</span>(<span class="hljs-params"><span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span> = <span class="hljs-string">&quot;Guest&quot;</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello, <span class="hljs-subst">$&#123;name&#125;</span>`</span>;<br>&#125;<br><br><span class="hljs-keyword">const</span> double = (<span class="hljs-attr">num</span>: <span class="hljs-built_in">number</span>): <span class="hljs-function"><span class="hljs-params">number</span> =&gt;</span> &#123;<br>  <span class="hljs-keyword">return</span> num * <span class="hljs-number">2</span>;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-4"><a href="#3-实际开发例子-4" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">getInputValue</span>(<span class="hljs-params"><span class="hljs-attr">input</span>: <span class="hljs-title class_">HTMLInputElement</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> input.<span class="hljs-property">value</span>.<span class="hljs-title function_">trim</span>();<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">validateUsername</span>(<span class="hljs-params"><span class="hljs-attr">username</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">boolean</span> &#123;<br>  <span class="hljs-keyword">return</span> username.<span class="hljs-property">length</span> &gt;= <span class="hljs-number">3</span> &amp;&amp; username.<span class="hljs-property">length</span> &lt;= <span class="hljs-number">12</span>;<br>&#125;<br><br><span class="hljs-keyword">const</span> usernameInput = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#username&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>;<br><span class="hljs-keyword">const</span> username = <span class="hljs-title function_">getInputValue</span>(usernameInput);<br><br><span class="hljs-keyword">if</span> (<span class="hljs-title function_">validateUsername</span>(username)) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;用户名格式正确&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-4"><a href="#4-语法理解-4" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>函数类型主要管两件事：</p><ul><li>参数传进来的值必须符合类型。</li><li>函数返回出去的值必须符合类型。</li></ul><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">formatPrice</span>(<span class="hljs-params"><span class="hljs-attr">price</span>: <span class="hljs-built_in">number</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">`￥<span class="hljs-subst">$&#123;price.toFixed(<span class="hljs-number">2</span>)&#125;</span>`</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>这个函数要求传入数字，并保证返回字符串。</p><h3 id="5-常见错误-4"><a href="#5-常见错误-4" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>可选参数必须处理 <code>undefined</code> 的情况。</li><li>返回值写了 <code>number</code>，就不能返回字符串。</li><li>函数没有返回值时，返回值类型通常写 <code>void</code>。</li></ul><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">logMessage</span>(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(message);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="6-本节练习-4"><a href="#6-本节练习-4" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>calculateTotal(price: number, count: number): number</code> 函数，返回商品总价。</p><p>再写一个 <code>formatTotal(total: number): string</code> 函数，把总价格式化为 <code>总价：￥xxx</code>。</p><h3 id="7-和-JavaScript-的关系-4"><a href="#7-和-JavaScript-的关系-4" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>函数声明、函数表达式、箭头函数都来自 JavaScript。TypeScript 只是给参数、返回值和函数变量补上类型，尤其适合约束回调函数、工具函数和事件处理函数。</p><hr><h2 id="5-对象类型"><a href="#5-对象类型" class="headerlink" title="5. 对象类型"></a>5. 对象类型</h2><h3 id="1-标准语法格式-5"><a href="#1-标准语法格式-5" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 对象名: &#123;<br>  属性名: 属性类型;<br>  可选属性名?: 属性类型;<br>  <span class="hljs-keyword">readonly</span> 只读属性名: 属性类型;<br>&#125; = &#123;<br>  属性名: 值,<br>  只读属性名: 值,<br>&#125;;<br></code></pre></td></tr></table></figure><p>嵌套对象：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 对象名: &#123;<br>  属性<span class="hljs-number">1</span>: 类型;<br>  子对象: &#123;<br>    属性<span class="hljs-number">2</span>: 类型;<br>  &#125;;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-5"><a href="#2-基础例子-5" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">user</span>: &#123;<br>  <span class="hljs-keyword">readonly</span> <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">age</span>?: <span class="hljs-built_in">number</span>;<br>&#125; = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br>  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;Alice&quot;</span>,<br>&#125;;<br><br>user.<span class="hljs-property">name</span> = <span class="hljs-string">&quot;Tom&quot;</span>;<br><span class="hljs-comment">// user.id = 2; // 错误：id 是只读属性</span><br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-5"><a href="#3-实际开发例子-5" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">article</span>: &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">author</span>: &#123;<br>    <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>    <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  &#125;;<br>  <span class="hljs-attr">tags</span>?: <span class="hljs-built_in">string</span>[];<br>&#125; = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-number">1001</span>,<br>  <span class="hljs-attr">title</span>: <span class="hljs-string">&quot;TypeScript 入门&quot;</span>,<br>  <span class="hljs-attr">author</span>: &#123;<br>    <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;小明&quot;</span>,<br>  &#125;,<br>  <span class="hljs-attr">tags</span>: [<span class="hljs-string">&quot;前端&quot;</span>, <span class="hljs-string">&quot;TypeScript&quot;</span>],<br>&#125;;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(article.<span class="hljs-property">author</span>.<span class="hljs-property">name</span>);<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-5"><a href="#4-语法理解-5" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>对象类型描述的是对象应该有哪些属性，以及每个属性是什么类型。</p><p>可选属性用 <code>?</code> 表示：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-attr">age</span>?: <span class="hljs-built_in">number</span>;<br></code></pre></td></tr></table></figure><p>它的意思是：这个属性可以不存在；如果存在，必须是 <code>number</code>。</p><p><code>readonly</code> 表示属性创建后不能被重新赋值，适合用于 ID 这类不应该被改动的数据。</p><h3 id="5-常见错误-5"><a href="#5-常见错误-5" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>对象少了必填属性会报错。</li><li>对象属性类型不匹配会报错。</li><li>可选属性使用前要考虑它可能是 <code>undefined</code>。</li></ul><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">if</span> (article.<span class="hljs-property">tags</span>) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(article.<span class="hljs-property">tags</span>.<span class="hljs-title function_">join</span>(<span class="hljs-string">&quot;,&quot;</span>));<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="6-本节练习-5"><a href="#6-本节练习-5" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>course</code> 对象类型，包含：</p><ul><li>只读 <code>id</code></li><li><code>title</code></li><li><code>teacher</code></li><li>可选 <code>description</code></li><li><code>chapters</code> 字符串数组</li></ul><h3 id="7-和-JavaScript-的关系-5"><a href="#7-和-JavaScript-的关系-5" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>对象仍然是 JavaScript 对象。TypeScript 的对象类型相当于给对象写一份“结构说明书”，提前规定哪些属性必须存在、哪些属性可选、哪些属性不能被修改。</p><hr><h2 id="5-5-类与面向对象"><a href="#5-5-类与面向对象" class="headerlink" title="5.5 类与面向对象"></a>5.5 类与面向对象</h2><h3 id="1-标准语法格式-6"><a href="#1-标准语法格式-6" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>定义类：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">class</span> 类名 &#123;<br>  <span class="hljs-keyword">readonly</span> 只读属性: 类型;<br>  <span class="hljs-keyword">public</span> 公共属性: 类型;<br>  <span class="hljs-keyword">protected</span> 受保护属性: 类型;<br>  <span class="hljs-keyword">private</span> 私有属性: 类型;<br><br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params">参数: 类型</span>) &#123;<br>    <span class="hljs-variable language_">this</span>.属性 = 参数;<br>  &#125;<br><br>  方法名(): 返回值类型 &#123;<br>    <span class="hljs-keyword">return</span> 返回值;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>继承：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">class</span> 子类 <span class="hljs-keyword">extends</span> 父类 &#123;<br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params">参数: 类型</span>) &#123;<br>    <span class="hljs-variable language_">super</span>(父类参数);<br>  &#125;<br><br>  重写方法(): 返回值类型 &#123;<br>    <span class="hljs-keyword">return</span> 返回值;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>实现接口：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">class</span> 类名 <span class="hljs-keyword">implements</span> 接口名 &#123;<br>  <span class="hljs-comment">// 必须实现接口要求的属性和方法</span><br>&#125;<br></code></pre></td></tr></table></figure><p>抽象类：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> 抽象类名 &#123;<br>  <span class="hljs-keyword">abstract</span> 方法名(): 返回值类型;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-6"><a href="#2-基础例子-6" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Person</span> &#123;<br>  <span class="hljs-keyword">readonly</span> <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-keyword">public</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-keyword">private</span> <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span>;<br><br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">age</span>: <span class="hljs-built_in">number</span></span>) &#123;<br>    <span class="hljs-variable language_">this</span>.<span class="hljs-property">id</span> = id;<br>    <span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span> = name;<br>    <span class="hljs-variable language_">this</span>.<span class="hljs-property">age</span> = age;<br>  &#125;<br><br>  <span class="hljs-title function_">sayHello</span>(): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`你好，我是 <span class="hljs-subst">$&#123;<span class="hljs-variable language_">this</span>.name&#125;</span>`</span>);<br>  &#125;<br><br>  <span class="hljs-title function_">getAge</span>(): <span class="hljs-built_in">number</span> &#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">age</span>;<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> person = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-number">1</span>, <span class="hljs-string">&quot;Alice&quot;</span>, <span class="hljs-number">18</span>);<br>person.<span class="hljs-title function_">sayHello</span>();<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-6"><a href="#3-实际开发例子-6" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">StorageService</span> &#123;<br>  <span class="hljs-title function_">get</span>(<span class="hljs-attr">key</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;<br>  <span class="hljs-title function_">set</span>(<span class="hljs-attr">key</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">value</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">void</span>;<br>&#125;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">LocalStorageService</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">StorageService</span> &#123;<br>  <span class="hljs-keyword">static</span> prefix = <span class="hljs-string">&quot;app:&quot;</span>;<br><br>  <span class="hljs-title function_">get</span>(<span class="hljs-attr">key</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span> &#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">getItem</span>(<span class="hljs-title class_">LocalStorageService</span>.<span class="hljs-property">prefix</span> + key);<br>  &#125;<br><br>  <span class="hljs-title function_">set</span>(<span class="hljs-attr">key</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">value</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">setItem</span>(<span class="hljs-title class_">LocalStorageService</span>.<span class="hljs-property">prefix</span> + key, value);<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> storage = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LocalStorageService</span>();<br>storage.<span class="hljs-title function_">set</span>(<span class="hljs-string">&quot;token&quot;</span>, <span class="hljs-string">&quot;abc123&quot;</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(storage.<span class="hljs-title function_">get</span>(<span class="hljs-string">&quot;token&quot;</span>));<br></code></pre></td></tr></table></figure><p>继承和重写：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Animal</span> &#123;<br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">protected</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span></span>) &#123;&#125;<br><br>  <span class="hljs-keyword">abstract</span> <span class="hljs-title function_">speak</span>(): <span class="hljs-built_in">void</span>;<br>&#125;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Dog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">Animal</span> &#123;<br>  <span class="hljs-title function_">speak</span>(): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`<span class="hljs-subst">$&#123;<span class="hljs-variable language_">this</span>.name&#125;</span> 在叫`</span>);<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> dog = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Dog</span>(<span class="hljs-string">&quot;旺财&quot;</span>);<br>dog.<span class="hljs-title function_">speak</span>();<br></code></pre></td></tr></table></figure><p>getter &#x2F; setter：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Counter</span> &#123;<br>  <span class="hljs-keyword">private</span> _value = <span class="hljs-number">0</span>;<br><br>  <span class="hljs-keyword">get</span> <span class="hljs-title function_">value</span>(): <span class="hljs-built_in">number</span> &#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">_value</span>;<br>  &#125;<br><br>  <span class="hljs-keyword">set</span> <span class="hljs-title function_">value</span>(<span class="hljs-params"><span class="hljs-attr">value</span>: <span class="hljs-built_in">number</span></span>) &#123;<br>    <span class="hljs-keyword">if</span> (value &gt;= <span class="hljs-number">0</span>) &#123;<br>      <span class="hljs-variable language_">this</span>.<span class="hljs-property">_value</span> = value;<br>    &#125;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-6"><a href="#4-语法理解-6" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>类是创建对象的模板。属性表示对象的数据，方法表示对象能做什么。</p><p>访问修饰符控制属性或方法能在哪里被访问：</p><ul><li><code>public</code>：默认值，类内部、子类、实例对象都能访问。</li><li><code>protected</code>：类内部和子类能访问，实例对象不能直接访问。</li><li><code>private</code>：只有当前类内部能访问。</li><li><code>readonly</code>：只能在声明或构造函数中赋值，之后不能修改。</li><li><code>static</code>：属于类本身，不属于实例。</li></ul><p><code>implements</code> 用来约束一个类必须具备某些能力。<code>abstract</code> 用来定义不能直接实例化的抽象父类，让子类实现具体逻辑。</p><h3 id="5-常见错误-6"><a href="#5-常见错误-6" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>忘记在构造函数里初始化类属性。</li><li>在类外访问 <code>private</code> 或 <code>protected</code> 属性。</li><li>子类构造函数里使用 <code>this</code> 前没有先调用 <code>super()</code>。</li><li>抽象类不能 <code>new</code>，只能被继承。</li><li><code>static</code> 属性要用类名访问，不是实例访问。</li></ul><h3 id="6-本节练习-6"><a href="#6-本节练习-6" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>User</code> 类：</p><ul><li><code>readonly id: number</code></li><li><code>public name: string</code></li><li><code>private password: string</code></li><li><code>checkPassword(password: string): boolean</code></li></ul><p>再定义一个 <code>AdminUser extends User</code>，增加 <code>role: &quot;admin&quot;</code> 属性。</p><h3 id="7-和-JavaScript-的关系-6"><a href="#7-和-JavaScript-的关系-6" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>JavaScript 本身也有 <code>class</code>、<code>constructor</code>、<code>extends</code>、<code>super</code>。TypeScript 在此基础上增加属性类型、访问修饰符、抽象类和接口实现检查，让面向对象代码的结构更明确。</p><hr><h2 id="6-类型别名-type"><a href="#6-类型别名-type" class="headerlink" title="6. 类型别名 type"></a>6. 类型别名 type</h2><h3 id="1-标准语法格式-7"><a href="#1-标准语法格式-7" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> 类型名 = 类型;<br><br><span class="hljs-keyword">type</span> 对象类型名 = &#123;<br>  属性名: 属性类型;<br>&#125;;<br><br><span class="hljs-keyword">type</span> 联合类型名 = 类型<span class="hljs-number">1</span> | 类型<span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-7"><a href="#2-基础例子-7" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">UserId</span> = <span class="hljs-built_in">number</span>;<br><span class="hljs-keyword">type</span> <span class="hljs-title class_">Status</span> = <span class="hljs-string">&quot;pending&quot;</span> | <span class="hljs-string">&quot;success&quot;</span> | <span class="hljs-string">&quot;error&quot;</span>;<br><br><span class="hljs-keyword">type</span> <span class="hljs-title class_">User</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-title class_">UserId</span>;<br>  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">status</span>: <span class="hljs-title class_">Status</span>;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-7"><a href="#3-实际开发例子-7" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">ButtonType</span> = <span class="hljs-string">&quot;button&quot;</span> | <span class="hljs-string">&quot;submit&quot;</span> | <span class="hljs-string">&quot;reset&quot;</span>;<br><br><span class="hljs-keyword">type</span> <span class="hljs-title class_">ButtonConfig</span> = &#123;<br>  <span class="hljs-attr">text</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">type</span>: <span class="hljs-title class_">ButtonType</span>;<br>  <span class="hljs-attr">disabled</span>: <span class="hljs-built_in">boolean</span>;<br>&#125;;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">submitButton</span>: <span class="hljs-title class_">ButtonConfig</span> = &#123;<br>  <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;提交&quot;</span>,<br>  <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;submit&quot;</span>,<br>  <span class="hljs-attr">disabled</span>: <span class="hljs-literal">false</span>,<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-7"><a href="#4-语法理解-7" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p><code>type</code> 用来给复杂类型起名字。</p><p>如果一个对象结构、联合类型、函数类型会被多次使用，就可以用 <code>type</code> 抽出来。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">Point</span> = &#123;<br>  <span class="hljs-attr">x</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">y</span>: <span class="hljs-built_in">number</span>;<br>&#125;;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">getDistance</span>(<span class="hljs-params"><span class="hljs-attr">point</span>: <span class="hljs-title class_">Point</span></span>): <span class="hljs-built_in">number</span> &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">sqrt</span>(point.<span class="hljs-property">x</span> ** <span class="hljs-number">2</span> + point.<span class="hljs-property">y</span> ** <span class="hljs-number">2</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="5-常见错误-7"><a href="#5-常见错误-7" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>type</code> 只是类型，不是运行时变量。</li><li>类型名一般用大驼峰命名，比如 <code>UserInfo</code>。</li><li>不要把所有简单变量都抽成 <code>type</code>，会增加阅读成本。</li></ul><h3 id="6-本节练习-7"><a href="#6-本节练习-7" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>LoginStatus</code> 类型，只允许：</p><ul><li><code>&quot;idle&quot;</code></li><li><code>&quot;loading&quot;</code></li><li><code>&quot;success&quot;</code></li><li><code>&quot;error&quot;</code></li></ul><p>再定义一个 <code>LoginState</code> 类型，包含：</p><ul><li><code>status: LoginStatus</code></li><li><code>message: string</code></li></ul><h3 id="7-和-JavaScript-的关系-7"><a href="#7-和-JavaScript-的关系-7" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p><code>type</code> 只存在于 TypeScript 类型系统里，编译成 JavaScript 后会消失。它不会创建真实变量，只是帮助你复用复杂类型。</p><hr><h2 id="7-接口-interface"><a href="#7-接口-interface" class="headerlink" title="7. 接口 interface"></a>7. 接口 interface</h2><h3 id="1-标准语法格式-8"><a href="#1-标准语法格式-8" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> 接口名 &#123;<br>  属性名: 属性类型;<br>  可选属性名?: 属性类型;<br>&#125;<br><br><span class="hljs-keyword">interface</span> 子接口名 <span class="hljs-keyword">extends</span> 父接口名 &#123;<br>  新属性名: 新属性类型;<br>&#125;<br><br><span class="hljs-keyword">class</span> 类名 <span class="hljs-keyword">implements</span> 接口名 &#123;<br>  <span class="hljs-comment">// 实现接口要求的属性和方法</span><br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-8"><a href="#2-基础例子-8" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">User</span> &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">email</span>?: <span class="hljs-built_in">string</span>;<br>&#125;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">user</span>: <span class="hljs-title class_">User</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br>  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;Alice&quot;</span>,<br>&#125;;<br><br><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Logger</span> &#123;<br>  <span class="hljs-title function_">log</span>(<span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">void</span>;<br>&#125;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ConsoleLogger</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Logger</span> &#123;<br>  <span class="hljs-title function_">log</span>(<span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(message);<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-8"><a href="#3-实际开发例子-8" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">BaseResponse</span> &#123;<br>  <span class="hljs-attr">code</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>;<br>&#125;<br><br><span class="hljs-keyword">interface</span> <span class="hljs-title class_">UserResponse</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseResponse</span> &#123;<br>  <span class="hljs-attr">data</span>: &#123;<br>    <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>    <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  &#125;;<br>&#125;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">response</span>: <span class="hljs-title class_">UserResponse</span> = &#123;<br>  <span class="hljs-attr">code</span>: <span class="hljs-number">200</span>,<br>  <span class="hljs-attr">message</span>: <span class="hljs-string">&quot;ok&quot;</span>,<br>  <span class="hljs-attr">data</span>: &#123;<br>    <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;Tom&quot;</span>,<br>  &#125;,<br>&#125;;<br></code></pre></td></tr></table></figure><p>接口约束类必须实现的能力：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Flyable</span> &#123;<br>  <span class="hljs-title function_">fly</span>(): <span class="hljs-built_in">void</span>;<br>&#125;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Bird</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Flyable</span> &#123;<br>  <span class="hljs-title function_">fly</span>(): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;鸟在飞&quot;</span>);<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">startFly</span>(<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Flyable</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  target.<span class="hljs-title function_">fly</span>();<br>&#125;<br><br><span class="hljs-title function_">startFly</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Bird</span>());<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-8"><a href="#4-语法理解-8" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p><code>interface</code> 常用于描述对象结构，尤其适合描述接口返回数据、组件属性、类的结构。</p><p><code>interface</code> 可以通过 <code>extends</code> 继承另一个接口。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">AdminUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">User</span> &#123;<br>  <span class="hljs-attr">role</span>: <span class="hljs-string">&quot;admin&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p><code>implements</code> 表示类要实现接口。它不负责生成代码，只负责检查类有没有按接口要求写属性和方法。</p><h3 id="5-常见错误-8"><a href="#5-常见错误-8" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>interface</code> 更适合对象结构，不适合直接写联合类型。</li><li>如果需要写 <code>&quot;success&quot; | &quot;error&quot;</code> 这种联合类型，通常用 <code>type</code> 更自然。</li><li><code>type</code> 和 <code>interface</code> 很多时候都能描述对象，不必过度纠结。初学时可以记住：对象结构优先 <code>interface</code>，联合类型优先 <code>type</code>。</li><li><code>implements</code> 只能检查类的实例侧成员，不能用来强制检查静态属性。</li><li>类实现接口时，接口要求的方法参数和返回值类型必须匹配。</li></ul><h3 id="6-本节练习-8"><a href="#6-本节练习-8" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>Article</code> 接口，包含：</p><ul><li><code>id</code></li><li><code>title</code></li><li><code>content</code></li><li><code>author</code></li></ul><p>再定义一个 <code>ArticleDetail</code> 接口，继承 <code>Article</code>，额外包含 <code>comments</code> 字符串数组。</p><p>再定义一个 <code>Renderable</code> 接口，要求有 <code>render(): string</code> 方法，然后写一个 <code>Card implements Renderable</code>。</p><h3 id="7-和-JavaScript-的关系-8"><a href="#7-和-JavaScript-的关系-8" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p><code>interface</code> 也是 TypeScript 的类型描述工具，编译后不会变成 JavaScript 代码。它适合描述 JS 对象、接口响应、组件参数这类“对象结构”。</p><hr><h2 id="8-联合类型和字面量类型"><a href="#8-联合类型和字面量类型" class="headerlink" title="8. 联合类型和字面量类型"></a>8. 联合类型和字面量类型</h2><h3 id="1-标准语法格式-9"><a href="#1-标准语法格式-9" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>联合类型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 变量名: 类型<span class="hljs-number">1</span> | 类型<span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><p>字面量类型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 变量名: <span class="hljs-string">&quot;固定值1&quot;</span> | <span class="hljs-string">&quot;固定值2&quot;</span>;<br></code></pre></td></tr></table></figure><p>常见组合：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> 状态类型 = <span class="hljs-string">&quot;状态1&quot;</span> | <span class="hljs-string">&quot;状态2&quot;</span> | <span class="hljs-string">&quot;状态3&quot;</span>;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-9"><a href="#2-基础例子-9" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> <span class="hljs-attr">id</span>: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>;<br><br>id = <span class="hljs-number">1001</span>;<br>id = <span class="hljs-string">&quot;user-1001&quot;</span>;<br><br><span class="hljs-keyword">type</span> <span class="hljs-title class_">RequestStatus</span> = <span class="hljs-string">&quot;idle&quot;</span> | <span class="hljs-string">&quot;loading&quot;</span> | <span class="hljs-string">&quot;success&quot;</span> | <span class="hljs-string">&quot;error&quot;</span>;<br><br><span class="hljs-keyword">let</span> <span class="hljs-attr">status</span>: <span class="hljs-title class_">RequestStatus</span> = <span class="hljs-string">&quot;loading&quot;</span>;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-9"><a href="#3-实际开发例子-9" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">ToastType</span> = <span class="hljs-string">&quot;success&quot;</span> | <span class="hljs-string">&quot;error&quot;</span> | <span class="hljs-string">&quot;warning&quot;</span>;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">showToast</span>(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">type</span>: <span class="hljs-title class_">ToastType</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`[<span class="hljs-subst">$&#123;<span class="hljs-keyword">type</span>&#125;</span>] <span class="hljs-subst">$&#123;message&#125;</span>`</span>);<br>&#125;<br><br><span class="hljs-title function_">showToast</span>(<span class="hljs-string">&quot;保存成功&quot;</span>, <span class="hljs-string">&quot;success&quot;</span>);<br><span class="hljs-title function_">showToast</span>(<span class="hljs-string">&quot;保存失败&quot;</span>, <span class="hljs-string">&quot;error&quot;</span>);<br><span class="hljs-comment">// showToast(&quot;未知状态&quot;, &quot;danger&quot;); // 错误：danger 不在 ToastType 中</span><br></code></pre></td></tr></table></figure><h3 id="4-语法理解-9"><a href="#4-语法理解-9" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>联合类型表示一个值可以是多种类型之一。</p><p>字面量类型表示一个值只能是某几个固定值之一。</p><p>前端开发里，字面量联合类型特别适合描述状态：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">TabKey</span> = <span class="hljs-string">&quot;home&quot;</span> | <span class="hljs-string">&quot;profile&quot;</span> | <span class="hljs-string">&quot;settings&quot;</span>;<br><span class="hljs-keyword">type</span> <span class="hljs-title class_">Theme</span> = <span class="hljs-string">&quot;light&quot;</span> | <span class="hljs-string">&quot;dark&quot;</span>;<br></code></pre></td></tr></table></figure><p>这样比直接写 <code>string</code> 更安全，因为 TS 会限制你只能传入合法状态。</p><h3 id="5-常见错误-9"><a href="#5-常见错误-9" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>联合类型不是“同时满足多个类型”，而是“可以是其中一种类型”。</li><li>使用联合类型时，不能直接调用某个类型独有的方法，需要先做类型判断。</li><li>状态值不建议随便写成 <code>string</code>，否则 TS 无法帮你检查拼写错误。</li></ul><h3 id="6-本节练习-9"><a href="#6-本节练习-9" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>OrderStatus</code> 类型，只允许：</p><ul><li><code>&quot;unpaid&quot;</code></li><li><code>&quot;paid&quot;</code></li><li><code>&quot;shipped&quot;</code></li><li><code>&quot;finished&quot;</code></li><li><code>&quot;cancelled&quot;</code></li></ul><p>写一个 <code>getOrderStatusText(status: OrderStatus): string</code> 函数，根据状态返回中文文案。</p><h3 id="7-和-JavaScript-的关系-9"><a href="#7-和-JavaScript-的关系-9" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>JavaScript 通常用字符串表示状态，比如 <code>&quot;loading&quot;</code>、<code>&quot;success&quot;</code>。TypeScript 的字面量联合类型可以把这些状态限制在固定范围内，减少拼写错误。</p><hr><h2 id="9-类型收窄"><a href="#9-类型收窄" class="headerlink" title="9. 类型收窄"></a>9. 类型收窄</h2><h3 id="1-标准语法格式-10"><a href="#1-标准语法格式-10" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>使用 <code>typeof</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> 变量 === <span class="hljs-string">&quot;string&quot;</span>) &#123;<br>  <span class="hljs-comment">// 这里变量会被收窄为 string</span><br>&#125;<br></code></pre></td></tr></table></figure><p>使用 <code>in</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">if</span> (<span class="hljs-string">&quot;属性名&quot;</span> <span class="hljs-keyword">in</span> 对象) &#123;<br>  <span class="hljs-comment">// 这里可以确认对象中存在某个属性</span><br>&#125;<br></code></pre></td></tr></table></figure><p>判空：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">if</span> (变量 !== <span class="hljs-literal">null</span>) &#123;<br>  <span class="hljs-comment">// 这里变量不是 null</span><br>&#125;<br></code></pre></td></tr></table></figure><p>判断数组：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">if</span> (<span class="hljs-title class_">Array</span>.<span class="hljs-title function_">isArray</span>(变量)) &#123;<br>  <span class="hljs-comment">// 这里变量是数组</span><br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-10"><a href="#2-基础例子-10" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">printId</span>(<span class="hljs-params"><span class="hljs-attr">id</span>: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> id === <span class="hljs-string">&quot;string&quot;</span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(id.<span class="hljs-title function_">toUpperCase</span>());<br>  &#125; <span class="hljs-keyword">else</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(id.<span class="hljs-title function_">toFixed</span>(<span class="hljs-number">0</span>));<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-10"><a href="#3-实际开发例子-10" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">TextMessage</span> = &#123;<br>  <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>;<br>  <span class="hljs-attr">content</span>: <span class="hljs-built_in">string</span>;<br>&#125;;<br><br><span class="hljs-keyword">type</span> <span class="hljs-title class_">ImageMessage</span> = &#123;<br>  <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;image&quot;</span>;<br>  <span class="hljs-attr">url</span>: <span class="hljs-built_in">string</span>;<br>&#125;;<br><br><span class="hljs-keyword">type</span> <span class="hljs-title class_">Message</span> = <span class="hljs-title class_">TextMessage</span> | <span class="hljs-title class_">ImageMessage</span>;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">renderMessage</span>(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-title class_">Message</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">if</span> (message.<span class="hljs-property">type</span> === <span class="hljs-string">&quot;text&quot;</span>) &#123;<br>    <span class="hljs-keyword">return</span> message.<span class="hljs-property">content</span>;<br>  &#125;<br><br>  <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;img src=&quot;<span class="hljs-subst">$&#123;message.url&#125;</span>&quot; alt=&quot;图片消息&quot;&gt;`</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-10"><a href="#4-语法理解-10" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>类型收窄就是通过判断条件，让 TypeScript 知道某个变量在当前代码块里更具体是什么类型。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">getLength</span>(<span class="hljs-params"><span class="hljs-attr">value</span>: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">string</span>[]</span>): <span class="hljs-built_in">number</span> &#123;<br>  <span class="hljs-keyword">if</span> (<span class="hljs-title class_">Array</span>.<span class="hljs-title function_">isArray</span>(value)) &#123;<br>    <span class="hljs-keyword">return</span> value.<span class="hljs-property">length</span>;<br>  &#125;<br><br>  <span class="hljs-keyword">return</span> value.<span class="hljs-property">length</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>虽然字符串和数组都有 <code>length</code>，但很多方法是各自独有的。类型收窄能避免误用。</p><h3 id="5-常见错误-10"><a href="#5-常见错误-10" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>对联合类型直接调用某个类型独有的方法。</li><li>只判断了 <code>null</code>，忘记判断 <code>undefined</code>。</li><li>复杂对象联合类型中，没有设计清晰的区分字段，比如 <code>type</code>。</li></ul><h3 id="6-本节练习-10"><a href="#6-本节练习-10" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>Result</code> 类型：</p><ul><li>成功：<code>{ status: &quot;success&quot;; data: string[] }</code></li><li>失败：<code>{ status: &quot;error&quot;; message: string }</code></li></ul><p>写一个 <code>handleResult(result: Result): string</code> 函数，成功时返回数据数量，失败时返回错误信息。</p><h3 id="7-和-JavaScript-的关系-10"><a href="#7-和-JavaScript-的关系-10" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>类型收窄依赖的判断方式大多来自 JavaScript，比如 <code>typeof</code>、<code>in</code>、<code>Array.isArray</code>。TypeScript 会根据这些运行时判断，推断当前分支里的更具体类型。</p><hr><h2 id="10-类型断言"><a href="#10-类型断言" class="headerlink" title="10. 类型断言"></a>10. 类型断言</h2><h3 id="1-标准语法格式-11"><a href="#1-标准语法格式-11" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts">值 <span class="hljs-keyword">as</span> 类型<br></code></pre></td></tr></table></figure><p>常见 DOM 写法：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> 元素 = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;选择器&quot;</span>) <span class="hljs-keyword">as</span> 具体元素类型;<br></code></pre></td></tr></table></figure><p>也可以先判空：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> 元素 = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;选择器&quot;</span>);<br><br><span class="hljs-keyword">if</span> (元素) &#123;<br>  <span class="hljs-comment">// 这里可以安全使用元素</span><br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-11"><a href="#2-基础例子-11" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">value</span>: <span class="hljs-built_in">unknown</span> = <span class="hljs-string">&quot;hello&quot;</span>;<br><span class="hljs-keyword">const</span> text = value <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(text.<span class="hljs-title function_">toUpperCase</span>());<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-11"><a href="#3-实际开发例子-11" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> input = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#email&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>;<br><span class="hljs-keyword">const</span> button = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#submit&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLButtonElement</span>;<br><br>button.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&quot;click&quot;</span>, <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(input.<span class="hljs-property">value</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><p>更稳妥的写法：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> input = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#email&quot;</span>);<br><br><span class="hljs-keyword">if</span> (input <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">HTMLInputElement</span>) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(input.<span class="hljs-property">value</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-11"><a href="#4-语法理解-11" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>类型断言的意思是：你告诉 TypeScript，“我比你更清楚这个值的类型”。</p><p>它不会改变运行时数据，只会影响 TypeScript 的类型检查。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> num = <span class="hljs-string">&quot;123&quot;</span> <span class="hljs-keyword">as</span> <span class="hljs-built_in">unknown</span> <span class="hljs-keyword">as</span> <span class="hljs-built_in">number</span>;<br></code></pre></td></tr></table></figure><p>这种写法不会真的把字符串变成数字，只是骗过了类型检查。</p><h3 id="5-常见错误-11"><a href="#5-常见错误-11" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>把类型断言当成类型转换。</li><li>明明不确定 DOM 元素是否存在，却直接断言。</li><li>过度使用 <code>as</code>，导致 TypeScript 失去检查意义。</li></ul><h3 id="6-本节练习-11"><a href="#6-本节练习-11" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>获取页面中的 <code>#search</code> 输入框，分别写出：</p><ul><li>使用 <code>as HTMLInputElement</code> 的版本</li><li>使用 <code>instanceof HTMLInputElement</code> 判定的版本</li></ul><h3 id="7-和-JavaScript-的关系-11"><a href="#7-和-JavaScript-的关系-11" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>类型断言只影响 TypeScript 检查，不会改变 JavaScript 运行时的值。<code>as number</code> 不会把字符串转成数字，真正的转换仍然要用 <code>Number()</code>、<code>parseInt()</code> 等 JS API。</p><hr><h2 id="11-泛型"><a href="#11-泛型" class="headerlink" title="11. 泛型"></a>11. 泛型</h2><h3 id="1-标准语法格式-12"><a href="#1-标准语法格式-12" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>泛型函数：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> 函数名&lt;T&gt;(参数: T): T &#123;<br>  <span class="hljs-keyword">return</span> 参数;<br>&#125;<br></code></pre></td></tr></table></figure><p>泛型接口：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> 接口名&lt;T&gt; &#123;<br>  属性名: T;<br>&#125;<br></code></pre></td></tr></table></figure><p>Promise 泛型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-title class_">Promise</span>&lt;数据类型&gt;<br></code></pre></td></tr></table></figure><p>泛型约束：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> 函数名&lt;T <span class="hljs-keyword">extends</span> 约束类型&gt;(参数: T): 返回值类型 &#123;<br>  <span class="hljs-keyword">return</span> 返回值;<br>&#125;<br></code></pre></td></tr></table></figure><p>泛型类：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">class</span> 类名&lt;T&gt; &#123;<br>  属性名: T;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-12"><a href="#2-基础例子-12" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> identity&lt;T&gt;(<span class="hljs-attr">value</span>: T): T &#123;<br>  <span class="hljs-keyword">return</span> value;<br>&#125;<br><br><span class="hljs-keyword">const</span> num = identity&lt;<span class="hljs-built_in">number</span>&gt;(<span class="hljs-number">100</span>);<br><span class="hljs-keyword">const</span> text = identity&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">&quot;hello&quot;</span>);<br></code></pre></td></tr></table></figure><p>多数时候可以让 TS 自动推断泛型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> result = <span class="hljs-title function_">identity</span>(<span class="hljs-string">&quot;TypeScript&quot;</span>);<br></code></pre></td></tr></table></figure><p>泛型约束例子：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> getLength&lt;T <span class="hljs-keyword">extends</span> &#123; <span class="hljs-attr">length</span>: <span class="hljs-built_in">number</span> &#125;&gt;(<span class="hljs-attr">value</span>: T): <span class="hljs-built_in">number</span> &#123;<br>  <span class="hljs-keyword">return</span> value.<span class="hljs-property">length</span>;<br>&#125;<br><br><span class="hljs-title function_">getLength</span>(<span class="hljs-string">&quot;hello&quot;</span>);<br><span class="hljs-title function_">getLength</span>([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-12"><a href="#3-实际开发例子-12" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">ApiResponse</span>&lt;T&gt; &#123;<br>  <span class="hljs-attr">code</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">data</span>: T;<br>&#125;<br><br><span class="hljs-keyword">type</span> <span class="hljs-title class_">User</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>&#125;;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">userResponse</span>: <span class="hljs-title class_">ApiResponse</span>&lt;<span class="hljs-title class_">User</span>&gt; = &#123;<br>  <span class="hljs-attr">code</span>: <span class="hljs-number">200</span>,<br>  <span class="hljs-attr">message</span>: <span class="hljs-string">&quot;ok&quot;</span>,<br>  <span class="hljs-attr">data</span>: &#123;<br>    <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;Alice&quot;</span>,<br>  &#125;,<br>&#125;;<br></code></pre></td></tr></table></figure><p>泛型类实际例子：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">class</span> <span class="hljs-title class_">DataStore</span>&lt;T&gt; &#123;<br>  <span class="hljs-keyword">private</span> <span class="hljs-attr">data</span>: T[] = [];<br><br>  <span class="hljs-title function_">add</span>(<span class="hljs-attr">item</span>: T): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">this</span>.<span class="hljs-property">data</span>.<span class="hljs-title function_">push</span>(item);<br>  &#125;<br><br>  <span class="hljs-title function_">getAll</span>(): T[] &#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">data</span>;<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> userStore = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataStore</span>&lt;<span class="hljs-title class_">User</span>&gt;();<br>userStore.<span class="hljs-title function_">add</span>(&#123; <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;Alice&quot;</span> &#125;);<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-12"><a href="#4-语法理解-12" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>泛型可以理解为“类型参数”。</p><p>有些结构是固定的，但里面装的数据类型不固定：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-title class_">ApiResponse</span>&lt;<span class="hljs-title class_">User</span>&gt;<br><span class="hljs-title class_">ApiResponse</span>&lt;<span class="hljs-title class_">Product</span>&gt;<br><span class="hljs-title class_">ApiResponse</span>&lt;<span class="hljs-built_in">string</span>[]&gt;<br></code></pre></td></tr></table></figure><p>它们都有 <code>code</code>、<code>message</code>、<code>data</code>，但 <code>data</code> 的类型不同。泛型可以避免重复定义一堆相似类型。</p><p>泛型约束用来告诉 TS：虽然具体类型还不确定，但它至少要满足某个结构。比如 <code>T extends { length: number }</code> 表示传进来的值必须有 <code>length</code>。</p><h3 id="5-常见错误-12"><a href="#5-常见错误-12" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>泛型不是为了炫技，而是为了解决类型复用。</li><li>如果一个类型不会复用，没必要强行写泛型。</li><li><code>T</code> 只是常见命名，也可以写成 <code>Data</code>、<code>Item</code> 等更语义化的名字。</li><li>泛型不是 <code>any</code>。泛型会保留输入和输出之间的类型关系，<code>any</code> 会放弃检查。</li><li>使用泛型约束时，约束只表示“至少拥有这些属性”，不代表只能有这些属性。</li></ul><h3 id="6-本节练习-12"><a href="#6-本节练习-12" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个泛型接口 <code>ListResponse&lt;T&gt;</code>，包含：</p><ul><li><code>total: number</code></li><li><code>list: T[]</code></li></ul><p>然后用它定义一个文章列表响应类型。</p><p>再写一个泛型类 <code>Cache&lt;T&gt;</code>，支持 <code>set(value: T)</code> 和 <code>get(): T | undefined</code>。</p><h3 id="7-和-JavaScript-的关系-12"><a href="#7-和-JavaScript-的关系-12" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>泛型是 TypeScript 的类型复用机制，编译后不会保留。它常用来描述 JavaScript 中“外层结构固定、内部数据变化”的模式，比如接口响应、列表、Promise。</p><hr><h2 id="12-DOM-和事件类型"><a href="#12-DOM-和事件类型" class="headerlink" title="12. DOM 和事件类型"></a>12. DOM 和事件类型</h2><h3 id="1-标准语法格式-13"><a href="#1-标准语法格式-13" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>获取 DOM：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> 元素 = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;选择器&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLElement</span>;<br></code></pre></td></tr></table></figure><p>输入框：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> 输入框 = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;选择器&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>;<br></code></pre></td></tr></table></figure><p>事件：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts">元素.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&quot;click&quot;</span>, <span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">event</span>: <span class="hljs-title class_">MouseEvent</span></span>) =&gt;</span> &#123;<br>  <span class="hljs-comment">// 事件处理</span><br>&#125;);<br></code></pre></td></tr></table></figure><p>表单事件：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts">表单.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&quot;submit&quot;</span>, <span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">event</span>: <span class="hljs-title class_">SubmitEvent</span></span>) =&gt;</span> &#123;<br>  event.<span class="hljs-title function_">preventDefault</span>();<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-13"><a href="#2-基础例子-13" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> button = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#btn&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLButtonElement</span>;<br><br>button.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&quot;click&quot;</span>, <span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">event</span>: <span class="hljs-title class_">MouseEvent</span></span>) =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(event.<span class="hljs-property">clientX</span>, event.<span class="hljs-property">clientY</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-13"><a href="#3-实际开发例子-13" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> form = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#login-form&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLFormElement</span>;<br><span class="hljs-keyword">const</span> usernameInput = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#username&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>;<br><span class="hljs-keyword">const</span> passwordInput = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#password&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>;<br><br>form.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&quot;submit&quot;</span>, <span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">event</span>: <span class="hljs-title class_">SubmitEvent</span></span>) =&gt;</span> &#123;<br>  event.<span class="hljs-title function_">preventDefault</span>();<br><br>  <span class="hljs-keyword">const</span> username = usernameInput.<span class="hljs-property">value</span>.<span class="hljs-title function_">trim</span>();<br>  <span class="hljs-keyword">const</span> password = passwordInput.<span class="hljs-property">value</span>.<span class="hljs-title function_">trim</span>();<br><br>  <span class="hljs-keyword">if</span> (!username || !password) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;请填写用户名和密码&quot;</span>);<br>    <span class="hljs-keyword">return</span>;<br>  &#125;<br><br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;提交登录&quot;</span>, username, password);<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-13"><a href="#4-语法理解-13" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>不同 DOM 元素有不同属性。</p><p><code>HTMLElement</code> 是比较通用的元素类型，但它没有 <code>value</code>。</p><p>如果你要读取输入框内容，需要使用 <code>HTMLInputElement</code>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> input = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&quot;#keyword&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLInputElement</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(input.<span class="hljs-property">value</span>);<br></code></pre></td></tr></table></figure><p>事件类型能让你获得准确的事件属性提示，比如鼠标位置、键盘按键、表单提交行为。</p><h3 id="5-常见错误-13"><a href="#5-常见错误-13" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>querySelector</code> 可能返回 <code>null</code>。</li><li>不是所有元素都有 <code>value</code> 属性。</li><li><code>event.target</code> 的类型比较宽泛，不能随便当成输入框使用。</li></ul><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ts">input.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&quot;input&quot;</span>, <span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">event</span>: <span class="hljs-title class_">Event</span></span>) =&gt;</span> &#123;<br>  <span class="hljs-keyword">const</span> target = event.<span class="hljs-property">target</span>;<br><br>  <span class="hljs-keyword">if</span> (target <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">HTMLInputElement</span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(target.<span class="hljs-property">value</span>);<br>  &#125;<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="6-本节练习-13"><a href="#6-本节练习-13" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个搜索框逻辑：</p><ul><li>获取 <code>#keyword</code> 输入框</li><li>获取 <code>#search-btn</code> 按钮</li><li>点击按钮后打印输入框内容</li><li>输入内容为空时提示“请输入关键词”</li></ul><h3 id="7-和-JavaScript-的关系-13"><a href="#7-和-JavaScript-的关系-13" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>DOM API 全部来自浏览器 JavaScript。TypeScript 的作用是把 <code>querySelector</code>、事件对象、输入框元素这些浏览器对象标成更准确的类型。</p><hr><h2 id="13-异步请求和接口数据类型"><a href="#13-异步请求和接口数据类型" class="headerlink" title="13. 异步请求和接口数据类型"></a>13. 异步请求和接口数据类型</h2><h3 id="1-标准语法格式-14"><a href="#1-标准语法格式-14" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>Promise 返回值：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> 函数名(): <span class="hljs-title class_">Promise</span>&lt;返回数据类型&gt; &#123;<br>  <span class="hljs-keyword">return</span> 数据;<br>&#125;<br></code></pre></td></tr></table></figure><p>接口响应结构：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">ApiResponse</span>&lt;T&gt; &#123;<br>  <span class="hljs-attr">code</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">data</span>: T;<br>&#125;<br></code></pre></td></tr></table></figure><p>fetch 基础写法：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;接口地址&quot;</span>);<br><span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>();<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-14"><a href="#2-基础例子-14" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getNumber</span>(<span class="hljs-params"></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-built_in">number</span>&gt; &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-number">100</span>;<br>&#125;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getMessage</span>(<span class="hljs-params"></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-built_in">string</span>&gt; &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;请求成功&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-14"><a href="#3-实际开发例子-14" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">User</span> &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">email</span>: <span class="hljs-built_in">string</span>;<br>&#125;<br><br><span class="hljs-keyword">interface</span> <span class="hljs-title class_">ApiResponse</span>&lt;T&gt; &#123;<br>  <span class="hljs-attr">code</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">data</span>: T;<br>&#125;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">fetchUser</span>(<span class="hljs-params"><span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-title class_">ApiResponse</span>&lt;<span class="hljs-title class_">User</span>&gt;&gt; &#123;<br>  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">`/api/users/<span class="hljs-subst">$&#123;id&#125;</span>`</span>);<br>  <span class="hljs-keyword">const</span> data = (<span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>()) <span class="hljs-keyword">as</span> <span class="hljs-title class_">ApiResponse</span>&lt;<span class="hljs-title class_">User</span>&gt;;<br>  <span class="hljs-keyword">return</span> data;<br>&#125;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">renderUser</span>(<span class="hljs-params"></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; &#123;<br>  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetchUser</span>(<span class="hljs-number">1</span>);<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(result.<span class="hljs-property">data</span>.<span class="hljs-property">name</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-14"><a href="#4-语法理解-14" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>异步函数一定返回 Promise。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">fn</span>(<span class="hljs-params"></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-built_in">string</span>&gt; &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>即使你 <code>return &quot;hello&quot;</code>，函数真实返回的也是 <code>Promise&lt;string&gt;</code>。</p><p>接口数据类型的重点是描述后端返回的数据结构。这样你访问 <code>result.data.name</code> 时，编辑器能知道 <code>data</code> 里有什么。</p><h3 id="5-常见错误-14"><a href="#5-常见错误-14" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>response.json()</code> 的结果默认是 <code>any</code>，不能完全依赖它。</li><li>类型断言不能保证后端数据真实正确。</li><li>前端类型和后端真实返回结构不一致时，运行时仍然会出错。</li></ul><p>更严格的项目会对接口数据做运行时校验，但初学阶段先掌握类型描述即可。</p><h3 id="6-本节练习-14"><a href="#6-本节练习-14" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个商品接口 <code>Product</code>，包含：</p><ul><li><code>id</code></li><li><code>title</code></li><li><code>price</code></li></ul><p>再写一个 <code>fetchProducts(): Promise&lt;ApiResponse&lt;Product[]&gt;&gt;</code> 函数。</p><h3 id="7-和-JavaScript-的关系-14"><a href="#7-和-JavaScript-的关系-14" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p><code>fetch</code>、Promise、<code>async/await</code> 都是 JavaScript 异步能力。TypeScript 主要给 Promise 结果、接口响应和错误处理补类型，让你更清楚异步代码最终会得到什么数据。</p><hr><h2 id="14-模块化和类型导入导出"><a href="#14-模块化和类型导入导出" class="headerlink" title="14. 模块化和类型导入导出"></a>14. 模块化和类型导入导出</h2><h3 id="1-标准语法格式-15"><a href="#1-标准语法格式-15" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>导出变量或函数：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> 变量名 = 值;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> 函数名() &#123;<br>  <span class="hljs-comment">// 函数内容</span><br>&#125;<br></code></pre></td></tr></table></figure><p>导出类型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> 类型名 = 类型结构;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> 接口名 &#123;<br>  属性名: 属性类型;<br>&#125;<br></code></pre></td></tr></table></figure><p>导入：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">import</span> &#123; 名称 &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./文件路径&quot;</span>;<br></code></pre></td></tr></table></figure><p>只导入类型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> &#123; 类型名 &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./文件路径&quot;</span>;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-15"><a href="#2-基础例子-15" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// user.ts</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">User</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>&#125;;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getUserName</span>(<span class="hljs-params"><span class="hljs-attr">user</span>: <span class="hljs-title class_">User</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> user.<span class="hljs-property">name</span>;<br>&#125;<br></code></pre></td></tr></table></figure><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// main.ts</span><br><span class="hljs-keyword">import</span> &#123; getUserName &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./user&quot;</span>;<br><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> &#123; <span class="hljs-title class_">User</span> &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./user&quot;</span>;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">user</span>: <span class="hljs-title class_">User</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,<br>  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;Alice&quot;</span>,<br>&#125;;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">getUserName</span>(user));<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-15"><a href="#3-实际开发例子-15" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// types.ts</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Todo</span> &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">done</span>: <span class="hljs-built_in">boolean</span>;<br>&#125;<br></code></pre></td></tr></table></figure><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// todo.ts</span><br><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> &#123; <span class="hljs-title class_">Todo</span> &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./types&quot;</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">createTodo</span>(<span class="hljs-params"><span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-title class_">Todo</span> &#123;<br>  <span class="hljs-keyword">return</span> &#123;<br>    <span class="hljs-attr">id</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>(),<br>    title,<br>    <span class="hljs-attr">done</span>: <span class="hljs-literal">false</span>,<br>  &#125;;<br>&#125;<br></code></pre></td></tr></table></figure><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// main.ts</span><br><span class="hljs-keyword">import</span> &#123; createTodo &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./todo&quot;</span>;<br><br><span class="hljs-keyword">const</span> todo = <span class="hljs-title function_">createTodo</span>(<span class="hljs-string">&quot;学习 TS 模块化&quot;</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(todo);<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-15"><a href="#4-语法理解-15" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>模块化能把类型、函数、业务逻辑拆到不同文件里。</p><p><code>import type</code> 表示只导入类型，不导入运行时代码。它能让代码意图更清晰，也能减少一些构建工具的歧义。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> &#123; <span class="hljs-title class_">User</span> &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./user&quot;</span>;<br></code></pre></td></tr></table></figure><p>如果你只在类型标注里用 <code>User</code>，推荐使用 <code>import type</code>。</p><h3 id="5-常见错误-15"><a href="#5-常见错误-15" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>忘记写相对路径的 <code>./</code>。</li><li>把只用于类型的内容当成普通值使用。</li><li>循环导入会让模块关系变复杂，初学时尽量保持文件依赖简单。</li></ul><h3 id="6-本节练习-15"><a href="#6-本节练习-15" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>创建两个文件的设计：</p><ul><li><code>types.ts</code>：导出 <code>Product</code> 类型</li><li><code>product.ts</code>：导入 <code>Product</code> 类型，并导出 <code>formatProduct(product: Product): string</code></li></ul><h3 id="7-和-JavaScript-的关系-15"><a href="#7-和-JavaScript-的关系-15" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>模块化的 <code>import/export</code> 来自现代 JavaScript。TypeScript 额外提供 <code>import type</code>、<code>export type</code>，专门表达“这里只使用类型，不需要运行时代码”。</p><hr><h2 id="15-tsconfig-基础配置"><a href="#15-tsconfig-基础配置" class="headerlink" title="15. tsconfig 基础配置"></a>15. tsconfig 基础配置</h2><h3 id="1-标准语法格式-16"><a href="#1-标准语法格式-16" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p><code>tsconfig.json</code> 是 TypeScript 项目的配置文件。</p><p>常见结构：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;compilerOptions&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;target&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ES2020&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;module&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ESNext&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;strict&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;rootDir&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;src&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;outDir&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;dist&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;noEmitOnError&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;removeComments&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;noImplicitAny&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br>  <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;include&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;src&quot;</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;exclude&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;node_modules&quot;</span><span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>如果项目很小，也可以用 <code>files</code> 精确指定文件：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;files&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;src/main.ts&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;src/utils.ts&quot;</span><span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><h3 id="2-基础例子-16"><a href="#2-基础例子-16" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;compilerOptions&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;target&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ES2020&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;module&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ESNext&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;strict&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;noEmitOnError&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-16"><a href="#3-实际开发例子-16" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><p>一个简单前端项目可以这样组织：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs txt">project<br>├─ src<br>│  ├─ main.ts<br>│  └─ types.ts<br>├─ dist<br>└─ tsconfig.json<br></code></pre></td></tr></table></figure><p>对应配置：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;compilerOptions&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;target&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ES2020&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;module&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ESNext&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;strict&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;rootDir&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;src&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;outDir&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;dist&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;noEmitOnError&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;removeComments&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br>  <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;include&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;src/**/*.ts&quot;</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;exclude&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;node_modules&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;dist&quot;</span><span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><h3 id="4-语法理解-16"><a href="#4-语法理解-16" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>几个常用配置的含义：</p><ul><li><code>target</code>：编译出来的 JavaScript 版本。</li><li><code>module</code>：模块化规范。</li><li><code>strict</code>：是否开启严格类型检查。</li><li><code>rootDir</code>：源码目录。</li><li><code>outDir</code>：编译输出目录。</li><li><code>include</code>：哪些文件参与编译。</li><li><code>exclude</code>：哪些文件或目录不参与编译。</li><li><code>files</code>：明确指定要编译的文件列表，适合很小的项目。</li><li><code>noEmitOnError</code>：有类型错误时不输出 JS。</li><li><code>removeComments</code>：编译输出时移除注释。</li><li><code>noImplicitAny</code>：不允许隐式 <code>any</code>，参数类型不明确时会报错。</li></ul><p>初学 TypeScript 时，建议开启：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;strict&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;noEmitOnError&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>这样能尽早养成正确的类型习惯。</p><h3 id="5-常见错误-16"><a href="#5-常见错误-16" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>以为 <code>tsconfig.json</code> 会自动运行代码。它只是编译配置。</li><li><code>rootDir</code> 和 <code>outDir</code> 混在一起，导致源码和编译结果不好区分。</li><li>关闭 <code>strict</code> 后，很多本该暴露的问题会被隐藏。</li><li><code>include</code> 和 <code>exclude</code> 写错后，可能导致文件没有参与编译。</li><li><code>noImplicitAny</code> 关闭后，很多没写类型的参数会悄悄变成 <code>any</code>。</li><li><code>files</code> 和 <code>include</code> 不要混乱使用。初学项目一般用 <code>include</code> 就够了。</li></ul><h3 id="6-本节练习-16"><a href="#6-本节练习-16" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>tsconfig.json</code>，要求：</p><ul><li>源码目录是 <code>src</code></li><li>输出目录是 <code>dist</code></li><li>开启严格模式</li><li>编译目标是 <code>ES2020</code></li><li>有类型错误时不输出 JS</li></ul><h3 id="7-和-JavaScript-的关系-16"><a href="#7-和-JavaScript-的关系-16" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p><code>tsconfig.json</code> 决定 TypeScript 如何检查代码、输出什么版本的 JavaScript。最终浏览器或 Node.js 运行的仍然是编译后的 JS 文件。</p><hr><h2 id="16-字符串方法的-TypeScript-写法"><a href="#16-字符串方法的-TypeScript-写法" class="headerlink" title="16. 字符串方法的 TypeScript 写法"></a>16. 字符串方法的 TypeScript 写法</h2><h3 id="1-标准语法格式-17"><a href="#1-标准语法格式-17" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">let</span> 文本: <span class="hljs-built_in">string</span> = <span class="hljs-string">&quot;内容&quot;</span>;<br><br>文本.<span class="hljs-property">length</span>;<br>文本.<span class="hljs-title function_">trim</span>();<br>文本.<span class="hljs-title function_">includes</span>(<span class="hljs-string">&quot;关键词&quot;</span>);<br>文本.<span class="hljs-title function_">slice</span>(开始索引, 结束索引);<br>文本.<span class="hljs-title function_">replace</span>(<span class="hljs-string">&quot;旧内容&quot;</span>, <span class="hljs-string">&quot;新内容&quot;</span>);<br>文本.<span class="hljs-title function_">split</span>(<span class="hljs-string">&quot;分隔符&quot;</span>);<br><br><span class="hljs-keyword">const</span> 模板字符串: <span class="hljs-built_in">string</span> = <span class="hljs-string">`文本 <span class="hljs-subst">$&#123;表达式&#125;</span>`</span>;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-17"><a href="#2-基础例子-17" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span> = <span class="hljs-string">&quot;  TypeScript  &quot;</span>;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">cleanMessage</span>: <span class="hljs-built_in">string</span> = message.<span class="hljs-title function_">trim</span>();<br><span class="hljs-keyword">const</span> <span class="hljs-attr">upperMessage</span>: <span class="hljs-built_in">string</span> = cleanMessage.<span class="hljs-title function_">toUpperCase</span>();<br><span class="hljs-keyword">const</span> <span class="hljs-attr">hasScript</span>: <span class="hljs-built_in">boolean</span> = cleanMessage.<span class="hljs-title function_">includes</span>(<span class="hljs-string">&quot;Script&quot;</span>);<br><span class="hljs-keyword">const</span> <span class="hljs-attr">chars</span>: <span class="hljs-built_in">string</span>[] = cleanMessage.<span class="hljs-title function_">split</span>(<span class="hljs-string">&quot;&quot;</span>);<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-17"><a href="#3-实际开发例子-17" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">normalizeKeyword</span>(<span class="hljs-params"><span class="hljs-attr">keyword</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> keyword.<span class="hljs-title function_">trim</span>().<span class="hljs-title function_">toLowerCase</span>();<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">highlightKeyword</span>(<span class="hljs-params"><span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">keyword</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">const</span> cleanKeyword = <span class="hljs-title function_">normalizeKeyword</span>(keyword);<br><br>  <span class="hljs-keyword">if</span> (!cleanKeyword) &#123;<br>    <span class="hljs-keyword">return</span> title;<br>  &#125;<br><br>  <span class="hljs-keyword">return</span> title.<span class="hljs-title function_">replace</span>(cleanKeyword, <span class="hljs-string">`&lt;mark&gt;<span class="hljs-subst">$&#123;cleanKeyword&#125;</span>&lt;/mark&gt;`</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-17"><a href="#4-语法理解-17" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>字符串方法本身来自 JavaScript。TypeScript 会根据方法返回值自动推断类型：<code>trim()</code> 返回 <code>string</code>，<code>includes()</code> 返回 <code>boolean</code>，<code>split()</code> 返回 <code>string[]</code>。</p><h3 id="5-常见错误-17"><a href="#5-常见错误-17" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>字符串是不可变的，<code>trim()</code>、<code>replace()</code> 会返回新字符串，不会修改原字符串。</li><li><code>replace()</code> 默认只替换第一个匹配项，全部替换可以用 <code>replaceAll()</code> 或正则 <code>g</code>。</li><li><code>split()</code> 返回数组，所以结果类型是 <code>string[]</code>。</li></ul><h3 id="6-本节练习-17"><a href="#6-本节练习-17" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>formatUsername(name: string): string</code> 函数，要求去掉首尾空格，并把名字转成小写。</p><h3 id="7-和-JavaScript-的关系-17"><a href="#7-和-JavaScript-的关系-17" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>参考文章里的字符串方法可以原样在 TypeScript 中使用。TS 的重点是把输入和输出标清楚，比如搜索关键词一定是 <code>string</code>，判断结果一定是 <code>boolean</code>。</p><hr><h2 id="17-数组方法的类型推断"><a href="#17-数组方法的类型推断" class="headerlink" title="17. 数组方法的类型推断"></a>17. 数组方法的类型推断</h2><h3 id="1-标准语法格式-18"><a href="#1-标准语法格式-18" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> 新数组 = 数组.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">元素</span>) =&gt;</span> 返回值);<br><span class="hljs-keyword">const</span> 筛选结果 = 数组.<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">元素</span>) =&gt;</span> 条件);<br><span class="hljs-keyword">const</span> 查找结果 = 数组.<span class="hljs-title function_">find</span>(<span class="hljs-function">(<span class="hljs-params">元素</span>) =&gt;</span> 条件);<br><span class="hljs-keyword">const</span> 累积结果 = 数组.<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">累积值, 当前值</span>) =&gt;</span> 新累积值, 初始值);<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-18"><a href="#2-基础例子-18" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">numbers</span>: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">doubled</span>: <span class="hljs-built_in">number</span>[] = numbers.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">num</span>) =&gt;</span> num * <span class="hljs-number">2</span>);<br><span class="hljs-keyword">const</span> <span class="hljs-attr">evens</span>: <span class="hljs-built_in">number</span>[] = numbers.<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">num</span>) =&gt;</span> num % <span class="hljs-number">2</span> === <span class="hljs-number">0</span>);<br><span class="hljs-keyword">const</span> <span class="hljs-attr">firstLarge</span>: <span class="hljs-built_in">number</span> | <span class="hljs-literal">undefined</span> = numbers.<span class="hljs-title function_">find</span>(<span class="hljs-function">(<span class="hljs-params">num</span>) =&gt;</span> num &gt; <span class="hljs-number">2</span>);<br><span class="hljs-keyword">const</span> <span class="hljs-attr">sum</span>: <span class="hljs-built_in">number</span> = numbers.<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">total, num</span>) =&gt;</span> total + num, <span class="hljs-number">0</span>);<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-18"><a href="#3-实际开发例子-18" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">Product</span> = &#123;<br>  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">price</span>: <span class="hljs-built_in">number</span>;<br>  <span class="hljs-attr">stock</span>: <span class="hljs-built_in">number</span>;<br>&#125;;<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">products</span>: <span class="hljs-title class_">Product</span>[] = [<br>  &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">&quot;鼠标&quot;</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">99</span>, <span class="hljs-attr">stock</span>: <span class="hljs-number">10</span> &#125;,<br>  &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">&quot;键盘&quot;</span>, <span class="hljs-attr">price</span>: <span class="hljs-number">199</span>, <span class="hljs-attr">stock</span>: <span class="hljs-number">0</span> &#125;,<br>];<br><br><span class="hljs-keyword">const</span> visibleProducts = products.<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> product.<span class="hljs-property">stock</span> &gt; <span class="hljs-number">0</span>);<br><span class="hljs-keyword">const</span> productTitles = visibleProducts.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> product.<span class="hljs-property">title</span>);<br><span class="hljs-keyword">const</span> totalPrice = visibleProducts.<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">total, product</span>) =&gt;</span> total + product.<span class="hljs-property">price</span>, <span class="hljs-number">0</span>);<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-18"><a href="#4-语法理解-18" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>只要数组类型明确，TS 就能推断回调函数参数类型。<code>Product[]</code> 里的 <code>product</code> 会自动拥有 <code>id</code>、<code>title</code>、<code>price</code>、<code>stock</code> 的类型提示。</p><h3 id="5-常见错误-18"><a href="#5-常见错误-18" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>find()</code> 可能找不到结果，所以返回类型通常是 <code>T | undefined</code>。</li><li><code>reduce()</code> 最好写初始值，避免空数组或推断不清。</li><li><code>sort()</code> 会修改原数组，如果不想改原数组，先复制：<code>[...arr].sort(...)</code>。</li></ul><h3 id="6-本节练习-18"><a href="#6-本节练习-18" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>定义一个 <code>Student[]</code>，使用 <code>filter</code> 找出及格学生，使用 <code>map</code> 得到学生姓名数组，使用 <code>reduce</code> 计算平均分。</p><h3 id="7-和-JavaScript-的关系-18"><a href="#7-和-JavaScript-的关系-18" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>数组方法完全来自 JavaScript。TypeScript 的优势是让回调参数和返回值更明确，减少把数组元素属性写错的情况。</p><hr><h2 id="18-Date-和时间处理类型"><a href="#18-Date-和时间处理类型" class="headerlink" title="18. Date 和时间处理类型"></a>18. Date 和时间处理类型</h2><h3 id="1-标准语法格式-19"><a href="#1-标准语法格式-19" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> 日期对象: <span class="hljs-title class_">Date</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();<br><span class="hljs-keyword">const</span> 时间戳: <span class="hljs-built_in">number</span> = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>();<br><br><span class="hljs-keyword">function</span> 函数名(<span class="hljs-attr">date</span>: <span class="hljs-title class_">Date</span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> 格式化结果;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-19"><a href="#2-基础例子-19" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">now</span>: <span class="hljs-title class_">Date</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();<br><span class="hljs-keyword">const</span> <span class="hljs-attr">year</span>: <span class="hljs-built_in">number</span> = now.<span class="hljs-title function_">getFullYear</span>();<br><span class="hljs-keyword">const</span> <span class="hljs-attr">month</span>: <span class="hljs-built_in">number</span> = now.<span class="hljs-title function_">getMonth</span>() + <span class="hljs-number">1</span>;<br><span class="hljs-keyword">const</span> <span class="hljs-attr">timestamp</span>: <span class="hljs-built_in">number</span> = now.<span class="hljs-title function_">getTime</span>();<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-19"><a href="#3-实际开发例子-19" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">padZero</span>(<span class="hljs-params"><span class="hljs-attr">value</span>: <span class="hljs-built_in">number</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-title class_">String</span>(value).<span class="hljs-title function_">padStart</span>(<span class="hljs-number">2</span>, <span class="hljs-string">&quot;0&quot;</span>);<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">formatDate</span>(<span class="hljs-params"><span class="hljs-attr">date</span>: <span class="hljs-title class_">Date</span></span>): <span class="hljs-built_in">string</span> &#123;<br>  <span class="hljs-keyword">const</span> year = date.<span class="hljs-title function_">getFullYear</span>();<br>  <span class="hljs-keyword">const</span> month = <span class="hljs-title function_">padZero</span>(date.<span class="hljs-title function_">getMonth</span>() + <span class="hljs-number">1</span>);<br>  <span class="hljs-keyword">const</span> day = <span class="hljs-title function_">padZero</span>(date.<span class="hljs-title function_">getDate</span>());<br><br>  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;year&#125;</span>-<span class="hljs-subst">$&#123;month&#125;</span>-<span class="hljs-subst">$&#123;day&#125;</span>`</span>;<br>&#125;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">formatDate</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>()));<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-19"><a href="#4-语法理解-19" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p><code>Date</code> 是 JavaScript 的内置对象类型。TypeScript 可以识别 <code>Date</code> 实例上的方法，也能约束函数必须传入真正的日期对象。</p><h3 id="5-常见错误-19"><a href="#5-常见错误-19" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>getMonth()</code> 返回 0 到 11，所以真实月份要加 1。</li><li>时间戳一般是 <code>number</code>，单位通常是毫秒。</li><li><code>new Date(&quot;2026-03-27&quot;)</code> 受解析规则和时区影响，复杂项目要更谨慎。</li></ul><h3 id="6-本节练习-19"><a href="#6-本节练习-19" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>formatDateTime(date: Date): string</code> 函数，返回 <code>YYYY-MM-DD HH:mm:ss</code> 格式。</p><h3 id="7-和-JavaScript-的关系-19"><a href="#7-和-JavaScript-的关系-19" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>参考文章里的 Date API 可以直接在 TS 里使用。TS 主要补充 <code>Date</code>、<code>number</code>、<code>string</code> 这些输入输出类型。</p><hr><h2 id="19-定时器、防抖和节流类型"><a href="#19-定时器、防抖和节流类型" class="headerlink" title="19. 定时器、防抖和节流类型"></a>19. 定时器、防抖和节流类型</h2><h3 id="1-标准语法格式-20"><a href="#1-标准语法格式-20" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">timerId</span>: <span class="hljs-title class_">ReturnType</span>&lt;<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">setTimeout</span>&gt; = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// 延迟执行</span><br>&#125;, 毫秒数);<br><br><span class="hljs-built_in">clearTimeout</span>(timerId);<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">intervalId</span>: <span class="hljs-title class_">ReturnType</span>&lt;<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">setInterval</span>&gt; = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// 重复执行</span><br>&#125;, 毫秒数);<br><br><span class="hljs-built_in">clearInterval</span>(intervalId);<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-20"><a href="#2-基础例子-20" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">timerId</span>: <span class="hljs-title class_">ReturnType</span>&lt;<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">setTimeout</span>&gt; = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;2 秒后执行&quot;</span>);<br>&#125;, <span class="hljs-number">2000</span>);<br><br><span class="hljs-built_in">clearTimeout</span>(timerId);<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-20"><a href="#3-实际开发例子-20" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> debounce&lt;<span class="hljs-title class_">Args</span> <span class="hljs-keyword">extends</span> <span class="hljs-built_in">unknown</span>[]&gt;(<br>  <span class="hljs-attr">fn</span>: <span class="hljs-function">(<span class="hljs-params">...<span class="hljs-attr">args</span>: <span class="hljs-title class_">Args</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>,<br>  <span class="hljs-attr">delay</span>: <span class="hljs-built_in">number</span><br>): <span class="hljs-function">(<span class="hljs-params">...<span class="hljs-attr">args</span>: <span class="hljs-title class_">Args</span></span>) =&gt;</span> <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-attr">timerId</span>: <span class="hljs-title class_">ReturnType</span>&lt;<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">setTimeout</span>&gt; | <span class="hljs-literal">undefined</span>;<br><br>  <span class="hljs-keyword">return</span> <span class="hljs-function">(<span class="hljs-params">...<span class="hljs-attr">args</span>: <span class="hljs-title class_">Args</span></span>) =&gt;</span> &#123;<br>    <span class="hljs-keyword">if</span> (timerId) &#123;<br>      <span class="hljs-built_in">clearTimeout</span>(timerId);<br>    &#125;<br><br>    timerId = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>      <span class="hljs-title function_">fn</span>(...args);<br>    &#125;, delay);<br>  &#125;;<br>&#125;<br><br><span class="hljs-keyword">const</span> search = <span class="hljs-title function_">debounce</span>(<span class="hljs-function">(<span class="hljs-params"><span class="hljs-attr">keyword</span>: <span class="hljs-built_in">string</span></span>) =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;搜索：&quot;</span>, keyword);<br>&#125;, <span class="hljs-number">300</span>);<br><br><span class="hljs-title function_">search</span>(<span class="hljs-string">&quot;typescript&quot;</span>);<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-20"><a href="#4-语法理解-20" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>浏览器和 Node.js 对定时器 ID 的类型不完全一样。<code>ReturnType&lt;typeof setTimeout&gt;</code> 是更稳的写法，能根据当前环境自动得到正确类型。</p><h3 id="5-常见错误-20"><a href="#5-常见错误-20" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>不要把定时器 ID 固定写死成 <code>number</code>，跨环境可能不准确。</li><li><code>setInterval</code> 要记得清理，否则可能持续执行。</li><li>防抖&#x2F;节流函数要保留原函数参数类型，否则调用时会失去提示。</li></ul><h3 id="6-本节练习-20"><a href="#6-本节练习-20" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>throttle(fn, delay)</code>，限制滚动事件处理函数在指定时间内最多执行一次，并保留参数类型。</p><h3 id="7-和-JavaScript-的关系-20"><a href="#7-和-JavaScript-的关系-20" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>定时器、防抖、节流都来自 JavaScript 的异步机制。TypeScript 的重点是正确描述回调函数、定时器 ID 和参数列表。</p><hr><h2 id="20-图片和脚本加载事件类型"><a href="#20-图片和脚本加载事件类型" class="headerlink" title="20. 图片和脚本加载事件类型"></a>20. 图片和脚本加载事件类型</h2><h3 id="1-标准语法格式-21"><a href="#1-标准语法格式-21" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">img</span>: <span class="hljs-title class_">HTMLImageElement</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>();<br><br>img.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// 加载成功</span><br>&#125;;<br><br>img.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// 加载失败</span><br>&#125;;<br><br>img.<span class="hljs-property">src</span> = 图片地址;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-21"><a href="#2-基础例子-21" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">const</span> <span class="hljs-attr">img</span>: <span class="hljs-title class_">HTMLImageElement</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>();<br><br>img.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;图片加载成功&quot;</span>);<br>&#125;;<br><br>img.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;图片加载失败&quot;</span>);<br>&#125;;<br><br>img.<span class="hljs-property">src</span> = <span class="hljs-string">&quot;/avatar.png&quot;</span>;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-21"><a href="#3-实际开发例子-21" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">loadImage</span>(<span class="hljs-params"><span class="hljs-attr">src</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-title class_">HTMLImageElement</span>&gt; &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> &#123;<br>    <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>();<br><br>    img.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> <span class="hljs-title function_">resolve</span>(img);<br>    img.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> <span class="hljs-title function_">reject</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`图片加载失败：<span class="hljs-subst">$&#123;src&#125;</span>`</span>));<br>    img.<span class="hljs-property">src</span> = src;<br>  &#125;);<br>&#125;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">renderAvatar</span>(<span class="hljs-params"><span class="hljs-attr">src</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">container</span>: <span class="hljs-title class_">HTMLElement</span></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; &#123;<br>  <span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">await</span> <span class="hljs-title function_">loadImage</span>(src);<br>    container.<span class="hljs-title function_">appendChild</span>(img);<br>  &#125; <span class="hljs-keyword">catch</span> &#123;<br>    container.<span class="hljs-property">textContent</span> = <span class="hljs-string">&quot;头像加载失败&quot;</span>;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-21"><a href="#4-语法理解-21" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p><code>HTMLImageElement</code> 描述图片元素，<code>HTMLScriptElement</code> 描述脚本元素。资源加载成功或失败时，浏览器会触发 <code>onload</code> 和 <code>onerror</code>。</p><h3 id="5-常见错误-21"><a href="#5-常见错误-21" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>src</code> 通常要最后赋值，否则可能错过事件绑定。</li><li><code>onerror</code> 里不要无限切换到同一个失败地址。</li><li>动态脚本加载成功后再调用第三方 SDK，不能假设脚本立刻可用。</li></ul><h3 id="6-本节练习-21"><a href="#6-本节练习-21" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>loadScript(src: string): Promise&lt;HTMLScriptElement&gt;</code> 函数，脚本加载成功时 resolve，失败时 reject。</p><h3 id="7-和-JavaScript-的关系-21"><a href="#7-和-JavaScript-的关系-21" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>参考文章里的 <code>onload/onerror</code> 逻辑可以直接迁移到 TS。TS 负责把图片、脚本、容器元素和 Promise 结果类型说清楚。</p><hr><h2 id="21-try…catch-和错误类型"><a href="#21-try…catch-和错误类型" class="headerlink" title="21. try…catch 和错误类型"></a>21. try…catch 和错误类型</h2><h3 id="1-标准语法格式-22"><a href="#1-标准语法格式-22" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">try</span> &#123;<br>  <span class="hljs-comment">// 可能出错的代码</span><br>&#125; <span class="hljs-keyword">catch</span> (<span class="hljs-attr">error</span>: <span class="hljs-built_in">unknown</span>) &#123;<br>  <span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Error</span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(error.<span class="hljs-property">message</span>);<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-22"><a href="#2-基础例子-22" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">try</span> &#123;<br>  <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(<span class="hljs-string">&quot;&#123; 错误 JSON &#125;&quot;</span>);<br>&#125; <span class="hljs-keyword">catch</span> (<span class="hljs-attr">error</span>: <span class="hljs-built_in">unknown</span>) &#123;<br>  <span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Error</span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(error.<span class="hljs-property">message</span>);<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-22"><a href="#3-实际开发例子-22" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> safeFetchJson&lt;T&gt;(<span class="hljs-attr">url</span>: <span class="hljs-built_in">string</span>): <span class="hljs-title class_">Promise</span>&lt;T | <span class="hljs-literal">null</span>&gt; &#123;<br>  <span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url);<br><br>    <span class="hljs-keyword">if</span> (!response.<span class="hljs-property">ok</span>) &#123;<br>      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`HTTP <span class="hljs-subst">$&#123;response.status&#125;</span>`</span>);<br>    &#125;<br><br>    <span class="hljs-keyword">return</span> (<span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>()) <span class="hljs-keyword">as</span> T;<br>  &#125; <span class="hljs-keyword">catch</span> (<span class="hljs-attr">error</span>: <span class="hljs-built_in">unknown</span>) &#123;<br>    <span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Error</span>) &#123;<br>      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">&quot;请求失败：&quot;</span>, error.<span class="hljs-property">message</span>);<br>    &#125;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-22"><a href="#4-语法理解-22" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>在严格模式下，<code>catch</code> 里的错误更适合按 <code>unknown</code> 处理，因为 JavaScript 允许抛出任何值：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">throw</span> <span class="hljs-string">&quot;错误字符串&quot;</span>;<br><span class="hljs-keyword">throw</span> <span class="hljs-number">123</span>;<br><span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&quot;错误对象&quot;</span>);<br></code></pre></td></tr></table></figure><p>所以访问 <code>error.message</code> 前，应该先判断 <code>error instanceof Error</code>。</p><h3 id="5-常见错误-22"><a href="#5-常见错误-22" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>直接写 <code>error.message</code> 可能出现 <code>error is of type &#39;unknown&#39;</code>。</li><li><code>try...catch</code> 捕获不到语法错误。</li><li>异步回调里的错误要在回调内部捕获，或者用 Promise&#x2F;async 统一处理。</li></ul><h3 id="6-本节练习-22"><a href="#6-本节练习-22" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>parseJson&lt;T&gt;(text: string): T | null</code> 函数，JSON 解析失败时返回 <code>null</code>。</p><h3 id="7-和-JavaScript-的关系-22"><a href="#7-和-JavaScript-的关系-22" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p><code>try...catch</code> 是 JavaScript 错误处理语法。TypeScript 的增强点是提醒你不要假设捕获到的一定是 <code>Error</code> 对象。</p><hr><h2 id="22-JS-代码如何加类型"><a href="#22-JS-代码如何加类型" class="headerlink" title="22. JS 代码如何加类型"></a>22. JS 代码如何加类型</h2><h3 id="1-标准语法格式-23"><a href="#1-标准语法格式-23" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// 第一步：给数据结构起类型</span><br><span class="hljs-keyword">type</span> 数据类型 = &#123;<br>  属性: 类型;<br>&#125;;<br><br><span class="hljs-comment">// 第二步：给函数参数和返回值加类型</span><br><span class="hljs-keyword">function</span> 函数名(参数: 参数类型): 返回值类型 &#123;<br>  <span class="hljs-keyword">return</span> 返回值;<br>&#125;<br><br><span class="hljs-comment">// 第三步：给异步结果加 Promise 类型</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> 异步函数(): <span class="hljs-title class_">Promise</span>&lt;结果类型&gt; &#123;<br>  <span class="hljs-keyword">return</span> 结果;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-23"><a href="#2-基础例子-23" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><p>JavaScript 写法：</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><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">add</span>(<span class="hljs-params">a, b</span>) &#123;<br>  <span class="hljs-keyword">return</span> a + b;<br>&#125;<br></code></pre></td></tr></table></figure><p>TypeScript 写法：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">add</span>(<span class="hljs-params"><span class="hljs-attr">a</span>: <span class="hljs-built_in">number</span>, <span class="hljs-attr">b</span>: <span class="hljs-built_in">number</span></span>): <span class="hljs-built_in">number</span> &#123;<br>  <span class="hljs-keyword">return</span> a + b;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="3-实际开发例子-23"><a href="#3-实际开发例子-23" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><p>JavaScript 写法：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">login</span>(<span class="hljs-params">username, password</span>) &#123;<br>  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;/api/login&quot;</span>, &#123;<br>    <span class="hljs-attr">method</span>: <span class="hljs-string">&quot;POST&quot;</span>,<br>    <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(&#123; username, password &#125;),<br>  &#125;);<br><br>  <span class="hljs-keyword">return</span> response.<span class="hljs-title function_">json</span>();<br>&#125;<br></code></pre></td></tr></table></figure><p>TypeScript 写法：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">LoginResponse</span> = &#123;<br>  <span class="hljs-attr">token</span>: <span class="hljs-built_in">string</span>;<br>  <span class="hljs-attr">user</span>: &#123;<br>    <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;<br>    <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;<br>  &#125;;<br>&#125;;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">login</span>(<span class="hljs-params"><span class="hljs-attr">username</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">password</span>: <span class="hljs-built_in">string</span></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-title class_">LoginResponse</span>&gt; &#123;<br>  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;/api/login&quot;</span>, &#123;<br>    <span class="hljs-attr">method</span>: <span class="hljs-string">&quot;POST&quot;</span>,<br>    <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(&#123; username, password &#125;),<br>  &#125;);<br><br>  <span class="hljs-keyword">return</span> (<span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>()) <span class="hljs-keyword">as</span> <span class="hljs-title class_">LoginResponse</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="4-语法理解-23"><a href="#4-语法理解-23" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>JS 改 TS 的顺序可以很固定：</p><ol><li>先找数据结构，比如用户、商品、订单。</li><li>再给函数参数和返回值加类型。</li><li>再处理 DOM、事件、接口返回值。</li><li>最后逐步消灭 <code>any</code> 和不安全的 <code>as</code>。</li></ol><h3 id="5-常见错误-23"><a href="#5-常见错误-23" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li><code>Object is possibly &#39;null&#39;</code>：通常是 DOM 查询结果可能为空，需要判空或使用更准确的断言。</li><li><code>Property &#39;value&#39; does not exist on type &#39;Element&#39;</code>：说明当前类型太宽，要判断或断言成 <code>HTMLInputElement</code>。</li><li><code>error is of type &#39;unknown&#39;</code>：说明 <code>catch</code> 里的错误要先判断 <code>instanceof Error</code>。</li><li><code>Type &#39;string&#39; is not assignable to type ...</code>：说明你传入的字符串不在目标类型允许范围内，常见于字面量联合类型。</li></ul><h3 id="6-本节练习-23"><a href="#6-本节练习-23" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>把下面 JS 思路改成 TS：输入商品价格数组，筛选出大于 100 的商品价格，计算总价，并返回格式化字符串。</p><h3 id="7-和-JavaScript-的关系-23"><a href="#7-和-JavaScript-的关系-23" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>TypeScript 学习的关键不是抛弃 JavaScript，而是给已经会写的 JS 补上类型边界。你越熟悉 JS 的字符串、数组、函数、Promise，学 TS 时就越能理解类型写在哪里。</p><hr><h2 id="22-5-装饰器入门与认识"><a href="#22-5-装饰器入门与认识" class="headerlink" title="22.5 装饰器入门与认识"></a>22.5 装饰器入门与认识</h2><h3 id="1-标准语法格式-24"><a href="#1-标准语法格式-24" class="headerlink" title="1. 标准语法格式"></a>1. 标准语法格式</h3><p>开启实验性装饰器配置：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;compilerOptions&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;experimentalDecorators&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>类装饰器：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> 装饰器名(<span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span>): <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-comment">// target 是被装饰的类</span><br>&#125;<br><br>@装饰器名<br><span class="hljs-keyword">class</span> 类名 &#123;&#125;<br></code></pre></td></tr></table></figure><p>装饰器工厂：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> 装饰器工厂(参数: 参数类型) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span></span>): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-comment">// 装饰逻辑</span><br>  &#125;;<br>&#125;<br><br>@装饰器工厂(参数)<br><span class="hljs-keyword">class</span> 类名 &#123;&#125;<br></code></pre></td></tr></table></figure><p>常见装饰器类型：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// 类装饰器</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">ClassDecorator</span>(<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span></span>): <span class="hljs-built_in">void</span> &#123;&#125;<br><br><span class="hljs-comment">// 属性装饰器</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">PropertyDecorator</span>(<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-built_in">object</span>, <span class="hljs-attr">propertyKey</span>: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">symbol</span></span>): <span class="hljs-built_in">void</span> &#123;&#125;<br><br><span class="hljs-comment">// 方法装饰器</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">MethodDecorator</span>(<span class="hljs-params"></span><br><span class="hljs-params">  <span class="hljs-attr">target</span>: <span class="hljs-built_in">object</span>,</span><br><span class="hljs-params">  <span class="hljs-attr">propertyKey</span>: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">symbol</span>,</span><br><span class="hljs-params">  <span class="hljs-attr">descriptor</span>: <span class="hljs-title class_">PropertyDescriptor</span></span><br><span class="hljs-params"></span>): <span class="hljs-built_in">void</span> &#123;&#125;<br><br><span class="hljs-comment">// 参数装饰器</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">ParameterDecorator</span>(<span class="hljs-params"></span><br><span class="hljs-params">  <span class="hljs-attr">target</span>: <span class="hljs-built_in">object</span>,</span><br><span class="hljs-params">  <span class="hljs-attr">propertyKey</span>: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">symbol</span> | <span class="hljs-literal">undefined</span>,</span><br><span class="hljs-params">  <span class="hljs-attr">parameterIndex</span>: <span class="hljs-built_in">number</span></span><br><span class="hljs-params"></span>): <span class="hljs-built_in">void</span> &#123;&#125;<br></code></pre></td></tr></table></figure><h3 id="2-基础例子-24"><a href="#2-基础例子-24" class="headerlink" title="2. 基础例子"></a>2. 基础例子</h3><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">LogClass</span>(<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;类被定义了：&quot;</span>, target.<span class="hljs-property">name</span>);<br>&#125;<br><br><span class="hljs-meta">@LogClass</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Person</span> &#123;<br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span></span>) &#123;&#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>装饰器会在类定义时执行，而不是创建实例时才执行。</p><h3 id="3-实际开发例子-24"><a href="#3-实际开发例子-24" class="headerlink" title="3. 实际开发例子"></a>3. 实际开发例子</h3><p>给类的原型补一个方法：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">User</span> &#123;<br>  <span class="hljs-title function_">introduce</span>(): <span class="hljs-built_in">void</span>;<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">AddIntroduce</span>(<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  target.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">introduce</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`我是 <span class="hljs-subst">$&#123;<span class="hljs-variable language_">this</span>.name&#125;</span>`</span>);<br>  &#125;;<br>&#125;<br><br><span class="hljs-meta">@AddIntroduce</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> &#123;<br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span></span>) &#123;&#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> user = <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>(<span class="hljs-string">&quot;Alice&quot;</span>);<br>user.<span class="hljs-title function_">introduce</span>();<br></code></pre></td></tr></table></figure><p>装饰器工厂：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">RepeatLog</span>(<span class="hljs-params"><span class="hljs-attr">count</span>: <span class="hljs-built_in">number</span></span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span></span>): <span class="hljs-built_in">void</span> &#123;<br>    target.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">logName</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>): <span class="hljs-built_in">void</span> &#123;<br>      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; count; i++) &#123;<br>        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span>);<br>      &#125;<br>    &#125;;<br>  &#125;;<br>&#125;<br><br><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Product</span> &#123;<br>  <span class="hljs-title function_">logName</span>(): <span class="hljs-built_in">void</span>;<br>&#125;<br><br><span class="hljs-meta">@RepeatLog</span>(<span class="hljs-number">3</span>)<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Product</span> &#123;<br>  <span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span></span>) &#123;&#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>装饰器组合执行顺序：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">A</span>(<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span></span>): <span class="hljs-built_in">void</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;A&quot;</span>);<br>&#125;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">B</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;B 工厂&quot;</span>);<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-attr">target</span>: <span class="hljs-title class_">Function</span></span>): <span class="hljs-built_in">void</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;B&quot;</span>);<br>  &#125;;<br>&#125;<br><br><span class="hljs-meta">@A</span><br><span class="hljs-meta">@B</span>()<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo</span> &#123;&#125;<br></code></pre></td></tr></table></figure><p>装饰器工厂会先从上到下执行，真正的装饰器函数再从下到上执行。</p><h3 id="4-语法理解-24"><a href="#4-语法理解-24" class="headerlink" title="4. 语法理解"></a>4. 语法理解</h3><p>装饰器本质是函数。它可以在类、属性、方法、访问器、参数上附加逻辑，常见于框架和库，比如依赖注入、路由注册、权限校验、日志记录。</p><p>初学阶段重点理解三件事：</p><ul><li><code>@Decorator</code> 是把函数应用到目标上。</li><li>装饰器通常在定义阶段执行。</li><li>装饰器会受 TypeScript 版本、编译配置、框架约定影响。</li></ul><h3 id="5-常见错误-24"><a href="#5-常见错误-24" class="headerlink" title="5. 常见错误"></a>5. 常见错误</h3><ul><li>忘记在 <code>tsconfig.json</code> 中开启 <code>experimentalDecorators</code>。</li><li>以为装饰器是普通函数调用，忽略它的执行时机。</li><li>通过装饰器动态添加实例方法后，忘记用接口声明补充类型。</li><li>不同 TS 版本和不同框架的装饰器语义可能有差异，实际项目要看项目配置。</li><li>初学阶段不要把装饰器当成必学核心语法，它更偏进阶和框架应用。</li></ul><h3 id="6-本节练习-24"><a href="#6-本节练习-24" class="headerlink" title="6. 本节练习"></a>6. 本节练习</h3><p>写一个 <code>@LogCreate</code> 类装饰器，在类定义时打印类名。再写一个 <code>@AddCreatedTime</code> 类装饰器，给实例添加 <code>createdTime: Date</code> 属性，并用接口补充类型声明。</p><h3 id="7-和-JavaScript-的关系-24"><a href="#7-和-JavaScript-的关系-24" class="headerlink" title="7. 和 JavaScript 的关系"></a>7. 和 JavaScript 的关系</h3><p>装饰器不是普通 JavaScript 的稳定基础语法。它是 TypeScript 和部分现代框架常用的增强写法，最终仍然会被编译成 JavaScript。学习装饰器的重点是能看懂框架代码，而不是一开始就大量使用。</p><hr><h2 id="23-学习路线总结"><a href="#23-学习路线总结" class="headerlink" title="23. 学习路线总结"></a>23. 学习路线总结</h2><p>如果你刚学完 JavaScript，建议按下面顺序掌握 TypeScript：</p><ol><li>先掌握变量、函数、数组、对象的类型写法。</li><li>再掌握 <code>type</code>、<code>interface</code>、联合类型。</li><li>然后学习类与面向对象、DOM、事件、fetch 这些常见开发场景。</li><li>再学习泛型、模块化、<code>tsconfig.json</code>。</li><li>最后把装饰器当作进阶内容，用于看懂框架和库。</li></ol><p>不要一开始就钻复杂类型和装饰器。初学阶段最重要的是能把日常 JS 代码写成清楚、稳定、可维护的 TS 代码。</p><h2 id="24-综合练习"><a href="#24-综合练习" class="headerlink" title="24. 综合练习"></a>24. 综合练习</h2><p>用 TypeScript 写一个简单 Todo 页面逻辑，要求：</p><ol><li>定义 <code>Todo</code> 类型：<ul><li><code>id: number</code></li><li><code>title: string</code></li><li><code>done: boolean</code></li></ul></li><li>获取输入框和按钮。</li><li>点击按钮时创建一个 Todo。</li><li>把 Todo 放进 <code>Todo[]</code> 数组。</li><li>渲染 Todo 标题列表。</li><li>输入为空时不允许添加。</li></ol><p>可以先不写复杂样式，重点是把类型写清楚。</p><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1778150721669" data-twikoo-path="article_1778150721669"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/05/06/typescript-yu-fa/</id>
    <link href="https://aoiblog.top/2026/05/06/typescript-yu-fa/"/>
    <published>2026-05-06T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>TypeScript 语法</h2>
> 适合读者：已经学过 HTML、CSS、JavaScript，想系统掌握 TypeScript 常用语法，并能把它用到前端开发里。
>
> 学习目标：不是重新学习 JavaScript，而是掌握 TypeScript 如何通过“类型]]>
    </summary>
    <title>TypeScript 语法</title>
    <updated>2026-05-06T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="数据结构与算法" scheme="https://aoiblog.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<h2>C++ 速通笔记</h2># C++ 速通学习笔记：从 C 到 C++ 语法迁移<blockquote><p>适合读者：已经学完 C 语言，想快速掌握 C++ 常用语法，并能看懂、写出算法题里的 C++ 代码。</p><p>默认标准：C++17。</p><p>学习目标：不是重新学编程基础，而是回答一个核心问题：<strong>这块 C++ 和 C 有什么不一样？我作为 C 使用者应该怎么迁移？</strong></p></blockquote><h2 id="0-先给你一张迁移地图"><a href="#0-先给你一张迁移地图" class="headerlink" title="0. 先给你一张迁移地图"></a>0. 先给你一张迁移地图</h2><p>如果你已经会 C，那么 C++ 里最值得优先掌握的是这些东西：</p><table><thead><tr><th>C 语言常用写法</th><th>C++ 常用写法</th><th>迁移重点</th></tr></thead><tbody><tr><td><code>printf</code></td><td><code>cout</code></td><td>输出类型自动匹配，不用写格式占位符</td></tr><tr><td><code>scanf</code></td><td><code>cin</code></td><td>输入类型自动匹配，但要注意速度优化</td></tr><tr><td><code>char s[100]</code></td><td><code>string s</code></td><td>字符串可动态增长，支持拼接、比较、查找</td></tr><tr><td><code>int a[100]</code></td><td><code>vector&lt;int&gt; a</code></td><td>动态数组，自动扩容，算法题最常用</td></tr><tr><td>手写链表&#x2F;栈&#x2F;队列&#x2F;堆</td><td><code>list</code> &#x2F; <code>stack</code> &#x2F; <code>queue</code> &#x2F; <code>priority_queue</code></td><td>STL 提供常见数据结构</td></tr><tr><td>手写哈希表</td><td><code>unordered_map</code> &#x2F; <code>unordered_set</code></td><td>平均 O(1) 查找</td></tr><tr><td><code>qsort</code> + 函数指针</td><td><code>sort</code> + lambda</td><td>类型安全，可读性更强</td></tr><tr><td>指针传参修改变量</td><td>引用传参 <code>int&amp;</code></td><td>写法更简洁，语义更清晰</td></tr><tr><td><code>malloc/free</code></td><td><code>new/delete</code>，更推荐 RAII 和智能指针</td><td>工程 C++ 尽量少裸写资源释放</td></tr><tr><td><code>struct</code> 只放数据</td><td><code>struct/class</code> 可放数据和函数</td><td>C++ 支持面向对象</td></tr></tbody></table><p>刷算法的优先级建议：</p><ol><li>基础语法：输入输出、<code>string</code>、<code>vector</code>、引用、<code>auto</code>。</li><li>STL 容器：<code>vector</code>、<code>string</code>、<code>unordered_map</code>、<code>unordered_set</code>、<code>queue</code>、<code>stack</code>、<code>priority_queue</code>。</li><li>算法库：<code>sort</code>、<code>reverse</code>、<code>lower_bound</code>、<code>max/min</code>。</li><li>类和模板：先能看懂，不急着深挖模板元编程。</li><li>内存、异常、智能指针：刷题少用，但读工程代码必须认识。</li></ol><hr><h2 id="1-C-与-C-的总体差异"><a href="#1-C-与-C-的总体差异" class="headerlink" title="1. C++ 与 C 的总体差异"></a>1. C++ 与 C 的总体差异</h2><h3 id="这是什么"><a href="#这是什么" class="headerlink" title="这是什么"></a>这是什么</h3><p>C++ 可以看作在 C 的基础上增加了更强的抽象能力：</p><ul><li>命名空间：避免名字冲突。</li><li>引用：比指针更简洁的别名机制。</li><li>函数重载：同名函数可以根据参数不同自动区分。</li><li>类和对象：把数据和操作数据的函数组织在一起。</li><li>模板：写泛型代码，比如 <code>vector&lt;int&gt;</code>、<code>vector&lt;string&gt;</code>。</li><li>异常：用 <code>throw/try/catch</code> 处理错误。</li><li>STL：标准模板库，提供常用容器和算法。</li></ul><h3 id="C-里怎么写"><a href="#C-里怎么写" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><br><span class="hljs-type">void</span> <span class="hljs-title function_">print_int</span><span class="hljs-params">(<span class="hljs-type">int</span> x)</span> &#123;<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d\n&quot;</span>, x);<br>&#125;<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-type">int</span> x = <span class="hljs-number">10</span>;<br>    print_int(x);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写"><a href="#C-怎么写" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print</span><span class="hljs-params">(<span class="hljs-type">int</span> x)</span> </span>&#123;<br>    cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-type">int</span> x = <span class="hljs-number">10</span>;<br>    <span class="hljs-built_in">print</span>(x);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="差异与好处"><a href="#差异与好处" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>C++ 保留了 C 的大部分语法，但提供了更高层的工具。你仍然可以写指针、数组、结构体，但多数时候可以用 <code>string</code>、<code>vector</code>、STL 容器替代手写底层逻辑。</p><h3 id="常见坑"><a href="#常见坑" class="headerlink" title="常见坑"></a>常见坑</h3><ul><li>C++ 不是“只有类”的语言。刷算法时，大部分代码仍然是函数 + STL。</li><li>C++ 兼容很多 C 写法，但不代表推荐继续用 C 风格。</li><li><code>using namespace std;</code> 在算法题里方便，工程代码里更推荐显式写 <code>std::</code>。</li></ul><h3 id="刷题常用写法"><a href="#刷题常用写法" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br>    cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br>    <span class="hljs-type">int</span> n;<br>    cin &gt;&gt; n;<br>    <span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(n)</span></span>;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span>&amp; x : nums) cin &gt;&gt; x;<br><br>    <span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>());<br><br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>    cout &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="2-程序结构、编译与头文件"><a href="#2-程序结构、编译与头文件" class="headerlink" title="2. 程序结构、编译与头文件"></a>2. 程序结构、编译与头文件</h2><h3 id="这是什么-1"><a href="#这是什么-1" class="headerlink" title="这是什么"></a>这是什么</h3><p>C++ 程序也从 <code>main</code> 函数开始。不同点主要在头文件、命名空间和标准库使用方式。</p><h3 id="C-里怎么写-1"><a href="#C-里怎么写-1" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;hello C\n&quot;</span>);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-1"><a href="#C-怎么写-1" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;vector&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    cout &lt;&lt; <span class="hljs-string">&quot;hello C++&quot;</span> &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>如果不写 <code>using namespace std;</code>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    std::cout &lt;&lt; <span class="hljs-string">&quot;hello C++&quot;</span> &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-1"><a href="#差异与好处-1" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>C++ 标准库的名字大多放在 <code>std</code> 命名空间里，比如：</p><ul><li><code>std::cout</code></li><li><code>std::cin</code></li><li><code>std::string</code></li><li><code>std::vector</code></li><li><code>std::sort</code></li></ul><p>常见 C 头文件和 C++ 头文件对照：</p><table><thead><tr><th>C 头文件</th><th>C++ 头文件</th><th>说明</th></tr></thead><tbody><tr><td><code>stdio.h</code></td><td><code>&lt;cstdio&gt;</code></td><td>C 风格输入输出</td></tr><tr><td><code>stdlib.h</code></td><td><code>&lt;cstdlib&gt;</code></td><td>常用工具函数</td></tr><tr><td><code>string.h</code></td><td><code>&lt;cstring&gt;</code></td><td>C 字符串函数</td></tr><tr><td><code>math.h</code></td><td><code>&lt;cmath&gt;</code></td><td>数学函数</td></tr><tr><td><code>ctype.h</code></td><td><code>&lt;cctype&gt;</code></td><td>字符判断</td></tr></tbody></table><p>C++ 还有自己的核心头文件：</p><table><thead><tr><th>头文件</th><th>常用内容</th></tr></thead><tbody><tr><td><code>&lt;iostream&gt;</code></td><td><code>cin</code>、<code>cout</code></td></tr><tr><td><code>&lt;string&gt;</code></td><td><code>string</code></td></tr><tr><td><code>&lt;vector&gt;</code></td><td><code>vector</code></td></tr><tr><td><code>&lt;queue&gt;</code></td><td><code>queue</code>、<code>priority_queue</code></td></tr><tr><td><code>&lt;stack&gt;</code></td><td><code>stack</code></td></tr><tr><td><code>&lt;unordered_map&gt;</code></td><td>哈希表</td></tr><tr><td><code>&lt;unordered_set&gt;</code></td><td>哈希集合</td></tr><tr><td><code>&lt;algorithm&gt;</code></td><td><code>sort</code>、<code>reverse</code>、<code>max</code>、<code>min</code>、二分函数</td></tr></tbody></table><h3 id="常见坑-1"><a href="#常见坑-1" class="headerlink" title="常见坑"></a>常见坑</h3><p><code>#include &lt;bits/stdc++.h&gt;</code> 是很多算法题常用头文件，它几乎包含了所有常见标准库：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br></code></pre></td></tr></table></figure><p>它的优点是方便，缺点是不属于正式 C++ 标准，部分编译器不支持。刷题平台一般可以用，工程项目不建议用。</p><h3 id="刷题常用写法-1"><a href="#刷题常用写法-1" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>工程代码更推荐：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;vector&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;algorithm&gt;</span></span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    std::vector&lt;<span class="hljs-type">int</span>&gt; nums&#123;<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>&#125;;<br>    std::<span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>());<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>        std::cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    &#125;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="3-输入输出：cin-cout-vs-scanf-printf"><a href="#3-输入输出：cin-cout-vs-scanf-printf" class="headerlink" title="3. 输入输出：cin/cout vs scanf/printf"></a>3. 输入输出：<code>cin/cout</code> vs <code>scanf/printf</code></h2><h3 id="这是什么-2"><a href="#这是什么-2" class="headerlink" title="这是什么"></a>这是什么</h3><p>C++ 使用流式输入输出：</p><ul><li><code>cin &gt;&gt; x</code>：读入。</li><li><code>cout &lt;&lt; x</code>：输出。</li><li><code>getline(cin, s)</code>：读取一整行字符串。</li></ul><h3 id="C-里怎么写-2"><a href="#C-里怎么写-2" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-type">int</span> age;<br>    <span class="hljs-type">double</span> score;<br>    <span class="hljs-type">char</span> name[<span class="hljs-number">100</span>];<br><br>    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d %lf %s&quot;</span>, &amp;age, &amp;score, name);<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;name=%s age=%d score=%.2f\n&quot;</span>, name, age, score);<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-2"><a href="#C-怎么写-2" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-type">int</span> age;<br>    <span class="hljs-type">double</span> score;<br>    string name;<br><br>    cin &gt;&gt; age &gt;&gt; score &gt;&gt; name;<br>    cout &lt;&lt; <span class="hljs-string">&quot;name=&quot;</span> &lt;&lt; name &lt;&lt; <span class="hljs-string">&quot; age=&quot;</span> &lt;&lt; age &lt;&lt; <span class="hljs-string">&quot; score=&quot;</span> &lt;&lt; score &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-2"><a href="#差异与好处-2" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>C 的 <code>scanf/printf</code> 要自己写格式占位符，比如 <code>%d</code>、<code>%lf</code>、<code>%s</code>。C++ 的 <code>cin/cout</code> 会根据变量类型自动处理。</p><p>读取一整行：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">string line;<br><span class="hljs-built_in">getline</span>(cin, line);<br></code></pre></td></tr></table></figure><p>循环读到 EOF：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-type">int</span> x;<br><span class="hljs-keyword">while</span> (cin &gt;&gt; x) &#123;<br>    cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="常见坑-2"><a href="#常见坑-2" class="headerlink" title="常见坑"></a>常见坑</h3><p><code>cin &gt;&gt;</code> 会留下换行符，后面直接 <code>getline</code> 可能读到空行：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp"><span class="hljs-type">int</span> n;<br>string line;<br><br>cin &gt;&gt; n;<br><span class="hljs-built_in">getline</span>(cin, line); <span class="hljs-comment">// 这里可能读到上一行剩下的换行</span><br></code></pre></td></tr></table></figure><p>正确写法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs cpp"><span class="hljs-type">int</span> n;<br>string line;<br><br>cin &gt;&gt; n;<br>cin.<span class="hljs-built_in">ignore</span>();<br><span class="hljs-built_in">getline</span>(cin, line);<br></code></pre></td></tr></table></figure><p>或者：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">getline</span>(cin &gt;&gt; ws, line);<br></code></pre></td></tr></table></figure><p>刷题时如果大量输入输出，建议加：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br>cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br></code></pre></td></tr></table></figure><p>注意：加了这两行后，不建议混用 <code>cin/cout</code> 和 <code>scanf/printf</code>。</p><h3 id="刷题常用写法-2"><a href="#刷题常用写法-2" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br>    cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br>    <span class="hljs-type">int</span> n;<br>    cin &gt;&gt; n;<br><br>    <span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(n)</span></span>;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; n; i++) &#123;<br>        cin &gt;&gt; nums[i];<br>    &#125;<br><br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>        cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>    &#125;<br>    cout &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="4-类型系统与变量声明"><a href="#4-类型系统与变量声明" class="headerlink" title="4. 类型系统与变量声明"></a>4. 类型系统与变量声明</h2><h3 id="这是什么-3"><a href="#这是什么-3" class="headerlink" title="这是什么"></a>这是什么</h3><p>C++ 有 C 的基本类型，也增加了更好用的类型和声明方式：</p><ul><li><code>bool</code>：布尔类型。</li><li><code>string</code>：字符串类型。</li><li><code>auto</code>：让编译器推导类型。</li><li><code>long long</code>：算法题常用大整数类型。</li><li><code>const</code>：常量和只读引用。</li></ul><h3 id="C-里怎么写-3"><a href="#C-里怎么写-3" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string.h&gt;</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-type">int</span> ok = <span class="hljs-number">1</span>;<br>    <span class="hljs-type">char</span> name[<span class="hljs-number">100</span>] = <span class="hljs-string">&quot;alice&quot;</span>;<br><br>    <span class="hljs-keyword">if</span> (ok) &#123;<br>        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%s\n&quot;</span>, name);<br>    &#125;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-3"><a href="#C-怎么写-3" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;string&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-type">bool</span> ok = <span class="hljs-literal">true</span>;<br>    string name = <span class="hljs-string">&quot;alice&quot;</span>;<br><br>    <span class="hljs-keyword">if</span> (ok) &#123;<br>        cout &lt;&lt; name &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    &#125;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-3"><a href="#差异与好处-3" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>C++ 的 <code>string</code> 比 C 字符数组更安全、更方便：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs cpp">string a = <span class="hljs-string">&quot;hello&quot;</span>;<br>string b = <span class="hljs-string">&quot;world&quot;</span>;<br>string c = a + <span class="hljs-string">&quot; &quot;</span> + b;<br><br>cout &lt;&lt; c.<span class="hljs-built_in">size</span>() &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>cout &lt;&lt; c[<span class="hljs-number">0</span>] &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br></code></pre></td></tr></table></figure><p><code>auto</code> 可以减少冗长类型：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>&#125;;<br><br><span class="hljs-keyword">auto</span> n = nums.<span class="hljs-built_in">size</span>();        <span class="hljs-comment">// n 的类型是 size_t</span><br><span class="hljs-keyword">auto</span> it = nums.<span class="hljs-built_in">begin</span>();      <span class="hljs-comment">// it 是 vector&lt;int&gt;::iterator</span><br></code></pre></td></tr></table></figure><p>初始化方式：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs cpp"><span class="hljs-type">int</span> a = <span class="hljs-number">10</span>;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">b</span><span class="hljs-params">(<span class="hljs-number">10</span>)</span></span>;<br><span class="hljs-type">int</span> c&#123;<span class="hljs-number">10</span>&#125;;<br><br>vector&lt;<span class="hljs-type">int</span>&gt; nums&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>&#125;;<br>string s&#123;<span class="hljs-string">&quot;hello&quot;</span>&#125;;<br></code></pre></td></tr></table></figure><h3 id="常见坑-3"><a href="#常见坑-3" class="headerlink" title="常见坑"></a>常见坑</h3><p>整数除法和 C 一样：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">cout &lt;&lt; <span class="hljs-number">5</span> / <span class="hljs-number">2</span> &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;      <span class="hljs-comment">// 2</span><br>cout &lt;&lt; <span class="hljs-number">5</span> / <span class="hljs-number">2.0</span> &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;    <span class="hljs-comment">// 2.5</span><br></code></pre></td></tr></table></figure><p><code>size_t</code> 和 <code>int</code> 比较容易出现警告：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>&#125;;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>    cout &lt;&lt; nums[i] &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>更稳妥：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; (<span class="hljs-type">int</span>)nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>    cout &lt;&lt; nums[i] &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>或者：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i &lt; nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>    cout &lt;&lt; nums[i] &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="刷题常用写法-3"><a href="#刷题常用写法-3" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs cpp"><span class="hljs-keyword">using</span> ll = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-type">const</span> <span class="hljs-type">int</span> INF = <span class="hljs-number">1e9</span>;<br><span class="hljs-type">const</span> ll LINF = <span class="hljs-number">4e18</span>;<br><br>vector&lt;<span class="hljs-type">int</span>&gt; nums&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>&#125;;<br>string s = <span class="hljs-string">&quot;abc&quot;</span>;<br><span class="hljs-type">bool</span> found = <span class="hljs-literal">false</span>;<br></code></pre></td></tr></table></figure><hr><h2 id="5-引用、指针与函数"><a href="#5-引用、指针与函数" class="headerlink" title="5. 引用、指针与函数"></a>5. 引用、指针与函数</h2><h3 id="这是什么-4"><a href="#这是什么-4" class="headerlink" title="这是什么"></a>这是什么</h3><p>引用是 C++ 非常核心的语法。可以把引用理解成一个变量的别名。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-type">int</span> x = <span class="hljs-number">10</span>;<br><span class="hljs-type">int</span>&amp; ref = x;<br>ref = <span class="hljs-number">20</span>;<br>cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>; <span class="hljs-comment">// 20</span><br></code></pre></td></tr></table></figure><h3 id="C-里怎么写-4"><a href="#C-里怎么写-4" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><br><span class="hljs-type">void</span> <span class="hljs-title function_">add_one</span><span class="hljs-params">(<span class="hljs-type">int</span>* p)</span> &#123;<br>    (*p)++;<br>&#125;<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-type">int</span> x = <span class="hljs-number">10</span>;<br>    add_one(&amp;x);<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d\n&quot;</span>, x);<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-4"><a href="#C-怎么写-4" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><p>C++ 可以用引用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">add_one</span><span class="hljs-params">(<span class="hljs-type">int</span>&amp; x)</span> </span>&#123;<br>    x++;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-type">int</span> x = <span class="hljs-number">10</span>;<br>    <span class="hljs-built_in">add_one</span>(x);<br>    cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-4"><a href="#差异与好处-4" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>函数传参有三种常见方式：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f1</span><span class="hljs-params">(vector&lt;<span class="hljs-type">int</span>&gt; nums)</span> </span>&#123;<br>    <span class="hljs-comment">// 值传递：复制一份，修改不影响原数组，成本高</span><br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f2</span><span class="hljs-params">(vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums)</span> </span>&#123;<br>    <span class="hljs-comment">// 引用传递：不复制，修改会影响原数组</span><br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f3</span><span class="hljs-params">(<span class="hljs-type">const</span> vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums)</span> </span>&#123;<br>    <span class="hljs-comment">// const 引用传递：不复制，也不允许修改，最常用于只读大对象</span><br>&#125;<br></code></pre></td></tr></table></figure><p>为什么算法题里经常写 <code>vector&lt;int&gt;&amp; nums</code>？</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">sum</span><span class="hljs-params">(<span class="hljs-type">const</span> vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums)</span> </span>&#123;<br>    <span class="hljs-type">int</span> ans = <span class="hljs-number">0</span>;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>        ans += x;<br>    &#125;<br>    <span class="hljs-keyword">return</span> ans;<br>&#125;<br></code></pre></td></tr></table></figure><p>如果写成 <code>vector&lt;int&gt; nums</code>，每次调用都会复制整个数组，大数据下很慢。</p><p>C++ 还支持函数重载：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b)</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> a + b;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">double</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">double</span> a, <span class="hljs-type">double</span> b)</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> a + b;<br>&#125;<br></code></pre></td></tr></table></figure><p>默认参数：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print</span><span class="hljs-params">(string s, <span class="hljs-type">char</span> end = <span class="hljs-string">&#x27;\n&#x27;</span>)</span> </span>&#123;<br>    cout &lt;&lt; s &lt;&lt; end;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="常见坑-4"><a href="#常见坑-4" class="headerlink" title="常见坑"></a>常见坑</h3><p>不要返回局部变量的引用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">int</span>&amp; <span class="hljs-title">bad</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-type">int</span> x = <span class="hljs-number">10</span>;<br>    <span class="hljs-keyword">return</span> x; <span class="hljs-comment">// 错误：x 离开函数就销毁了</span><br>&#125;<br></code></pre></td></tr></table></figure><p>如果不想修改传入对象，优先用 <code>const</code> 引用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print_vec</span><span class="hljs-params">(<span class="hljs-type">const</span> vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums)</span> </span>&#123;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>    cout &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="刷题常用写法-4"><a href="#刷题常用写法-4" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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><code class="hljs cpp"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> &#123;<br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">maxValue</span><span class="hljs-params">(<span class="hljs-type">const</span> vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums)</span> </span>&#123;<br>        <span class="hljs-type">int</span> ans = nums[<span class="hljs-number">0</span>];<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>            ans = <span class="hljs-built_in">max</span>(ans, x);<br>        &#125;<br>        <span class="hljs-keyword">return</span> ans;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><hr><h2 id="6-控制流与-C-新语法"><a href="#6-控制流与-C-新语法" class="headerlink" title="6. 控制流与 C++ 新语法"></a>6. 控制流与 C++ 新语法</h2><h3 id="这是什么-5"><a href="#这是什么-5" class="headerlink" title="这是什么"></a>这是什么</h3><p><code>if/else</code>、<code>for</code>、<code>while</code>、<code>switch</code> 和 C 基本一致。C++ 需要重点补充范围 for、<code>auto</code>、结构化绑定。</p><h3 id="C-里怎么写-5"><a href="#C-里怎么写-5" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">int</span> a[<span class="hljs-number">3</span>] = &#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>&#125;;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++) &#123;<br>    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d\n&quot;</span>, a[i]);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-5"><a href="#C-怎么写-5" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>&#125;;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>    cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-5"><a href="#差异与好处-5" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>范围 for 更适合遍历容器：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>&#125;;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> x : nums) &#123;<br>    x *= <span class="hljs-number">2</span>; <span class="hljs-comment">// 修改的是副本</span><br>&#125;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>&amp; x : nums) &#123;<br>    x *= <span class="hljs-number">2</span>; <span class="hljs-comment">// 修改原数组</span><br>&#125;<br></code></pre></td></tr></table></figure><p>遍历哈希表：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">unordered_map&lt;string, <span class="hljs-type">int</span>&gt; cnt&#123;&#123;<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-number">2</span>&#125;, &#123;<span class="hljs-string">&quot;banana&quot;</span>, <span class="hljs-number">3</span>&#125;&#125;;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-type">const</span> <span class="hljs-keyword">auto</span>&amp; [key, value] : cnt) &#123;<br>    cout &lt;&lt; key &lt;&lt; <span class="hljs-string">&quot; &quot;</span> &lt;&lt; value &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="常见坑-5"><a href="#常见坑-5" class="headerlink" title="常见坑"></a>常见坑</h3><p><code>for (auto x : nums)</code> 会复制元素。如果元素很大，或者你想修改原容器，要用引用：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>&amp; x : nums) &#123;<br>    x++;<br>&#125;<br></code></pre></td></tr></table></figure><p>如果只是读，推荐：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-type">const</span> <span class="hljs-keyword">auto</span>&amp; x : nums) &#123;<br>    cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="刷题常用写法-5"><a href="#刷题常用写法-5" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; (<span class="hljs-type">int</span>)nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>    <span class="hljs-comment">// 需要下标时用普通 for</span><br>&#125;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>    <span class="hljs-comment">// 只需要元素值时用范围 for</span><br>&#125;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>&amp; [key, value] : mp) &#123;<br>    <span class="hljs-comment">// 遍历 map/unordered_map</span><br>&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="7-数组、字符串与-STL-容器"><a href="#7-数组、字符串与-STL-容器" class="headerlink" title="7. 数组、字符串与 STL 容器"></a>7. 数组、字符串与 STL 容器</h2><h3 id="这是什么-6"><a href="#这是什么-6" class="headerlink" title="这是什么"></a>这是什么</h3><p>STL 是 C++ 刷算法的核心。你可以少写很多底层数据结构，把精力放在算法本身。</p><h3 id="7-1-数组、array、vector"><a href="#7-1-数组、array、vector" class="headerlink" title="7.1 数组、array、vector"></a>7.1 数组、<code>array</code>、<code>vector</code></h3><h4 id="C-里怎么写-6"><a href="#C-里怎么写-6" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h4><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">int</span> a[<span class="hljs-number">100</span>];<br><span class="hljs-type">int</span> n = <span class="hljs-number">0</span>;<br><br>a[n++] = <span class="hljs-number">10</span>;<br>a[n++] = <span class="hljs-number">20</span>;<br></code></pre></td></tr></table></figure><h4 id="C-怎么写-6"><a href="#C-怎么写-6" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h4><figure class="highlight cpp"><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums;<br>nums.<span class="hljs-built_in">push_back</span>(<span class="hljs-number">10</span>);<br>nums.<span class="hljs-built_in">push_back</span>(<span class="hljs-number">20</span>);<br></code></pre></td></tr></table></figure><h4 id="差异与好处-6"><a href="#差异与好处-6" class="headerlink" title="差异与好处"></a>差异与好处</h4><table><thead><tr><th>类型</th><th>大小是否固定</th><th>常用场景</th></tr></thead><tbody><tr><td>C 数组 <code>int a[100]</code></td><td>固定</td><td>已知最大长度，追求极致性能</td></tr><tr><td><code>array&lt;int, 100&gt;</code></td><td>固定</td><td>固定大小，但有 STL 接口</td></tr><tr><td><code>vector&lt;int&gt;</code></td><td>可变</td><td>算法题最常用动态数组</td></tr></tbody></table><p><code>vector</code> 常用 API：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; v;<br><br>v.<span class="hljs-built_in">push_back</span>(<span class="hljs-number">1</span>);<br>v.<span class="hljs-built_in">pop_back</span>();<br>v.<span class="hljs-built_in">size</span>();<br>v.<span class="hljs-built_in">empty</span>();<br>v.<span class="hljs-built_in">clear</span>();<br>v.<span class="hljs-built_in">front</span>();<br>v.<span class="hljs-built_in">back</span>();<br>v[<span class="hljs-number">0</span>];<br></code></pre></td></tr></table></figure><p>复杂度直觉：</p><ul><li>下标访问：O(1)。</li><li>尾部插入：均摊 O(1)。</li><li>中间插入&#x2F;删除：O(n)。</li></ul><h4 id="常见坑-6"><a href="#常见坑-6" class="headerlink" title="常见坑"></a>常见坑</h4><p><code>v[i]</code> 不检查越界，越界是未定义行为。调试时可以用 <code>v.at(i)</code>，它会抛异常。</p><p>初始化二维数组：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span> m = <span class="hljs-number">3</span>, n = <span class="hljs-number">4</span>;<br>vector&lt;vector&lt;<span class="hljs-type">int</span>&gt;&gt; <span class="hljs-built_in">grid</span>(m, <span class="hljs-built_in">vector</span>&lt;<span class="hljs-type">int</span>&gt;(n, <span class="hljs-number">0</span>));<br></code></pre></td></tr></table></figure><h4 id="刷题常用写法-6"><a href="#刷题常用写法-6" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(n)</span></span>;<br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span>&amp; x : nums) cin &gt;&gt; x;<br><br>vector&lt;vector&lt;<span class="hljs-type">int</span>&gt;&gt; <span class="hljs-built_in">dp</span>(n + <span class="hljs-number">1</span>, <span class="hljs-built_in">vector</span>&lt;<span class="hljs-type">int</span>&gt;(m + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>));<br></code></pre></td></tr></table></figure><h3 id="7-2-string"><a href="#7-2-string" class="headerlink" title="7.2 string"></a>7.2 <code>string</code></h3><h4 id="C-里怎么写-7"><a href="#C-里怎么写-7" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h4><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">char</span> s[<span class="hljs-number">100</span>] = <span class="hljs-string">&quot;hello&quot;</span>;<br><span class="hljs-type">char</span> t[<span class="hljs-number">100</span>] = <span class="hljs-string">&quot;world&quot;</span>;<br><span class="hljs-built_in">strcat</span>(s, t);<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%s\n&quot;</span>, s);<br></code></pre></td></tr></table></figure><h4 id="C-怎么写-7"><a href="#C-怎么写-7" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp">string s = <span class="hljs-string">&quot;hello&quot;</span>;<br>string t = <span class="hljs-string">&quot;world&quot;</span>;<br>string u = s + t;<br>cout &lt;&lt; u &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br></code></pre></td></tr></table></figure><h4 id="差异与好处-7"><a href="#差异与好处-7" class="headerlink" title="差异与好处"></a>差异与好处</h4><p><code>string</code> 常用 API：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs cpp">string s = <span class="hljs-string">&quot;abc&quot;</span>;<br><br>s.<span class="hljs-built_in">size</span>();<br>s.<span class="hljs-built_in">empty</span>();<br>s.<span class="hljs-built_in">push_back</span>(<span class="hljs-string">&#x27;d&#x27;</span>);<br>s.<span class="hljs-built_in">pop_back</span>();<br>s.<span class="hljs-built_in">substr</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);<br>s.<span class="hljs-built_in">find</span>(<span class="hljs-string">&quot;bc&quot;</span>);<br>s[<span class="hljs-number">0</span>];<br></code></pre></td></tr></table></figure><p>字符串可以直接比较：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">if</span> (a &lt; b) &#123;<br>    cout &lt;&lt; <span class="hljs-string">&quot;a 字典序更小\n&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="常见坑-7"><a href="#常见坑-7" class="headerlink" title="常见坑"></a>常见坑</h4><p><code>s.find(x)</code> 找不到时返回 <code>string::npos</code>：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">if</span> (s.<span class="hljs-built_in">find</span>(<span class="hljs-string">&quot;abc&quot;</span>) == string::npos) &#123;<br>    cout &lt;&lt; <span class="hljs-string">&quot;not found\n&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="刷题常用写法-7"><a href="#刷题常用写法-7" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp">string s;<br>cin &gt;&gt; s;<br><br>unordered_map&lt;<span class="hljs-type">char</span>, <span class="hljs-type">int</span>&gt; cnt;<br><span class="hljs-keyword">for</span> (<span class="hljs-type">char</span> c : s) &#123;<br>    cnt[c]++;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="7-3-list-和-deque"><a href="#7-3-list-和-deque" class="headerlink" title="7.3 list 和 deque"></a>7.3 <code>list</code> 和 <code>deque</code></h3><h4 id="C-里怎么写-8"><a href="#C-里怎么写-8" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h4><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Node</span> &#123;</span><br>    <span class="hljs-type">int</span> val;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Node</span>* <span class="hljs-title">next</span>;</span><br>&#125;;<br></code></pre></td></tr></table></figure><h4 id="C-怎么写-8"><a href="#C-怎么写-8" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">list&lt;<span class="hljs-type">int</span>&gt; lst;<br>lst.<span class="hljs-built_in">push_back</span>(<span class="hljs-number">1</span>);<br>lst.<span class="hljs-built_in">push_front</span>(<span class="hljs-number">2</span>);<br>lst.<span class="hljs-built_in">pop_back</span>();<br>lst.<span class="hljs-built_in">pop_front</span>();<br></code></pre></td></tr></table></figure><p><code>deque</code> 是双端队列：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">deque&lt;<span class="hljs-type">int</span>&gt; dq;<br>dq.<span class="hljs-built_in">push_back</span>(<span class="hljs-number">1</span>);<br>dq.<span class="hljs-built_in">push_front</span>(<span class="hljs-number">2</span>);<br>dq.<span class="hljs-built_in">pop_back</span>();<br>dq.<span class="hljs-built_in">pop_front</span>();<br></code></pre></td></tr></table></figure><h4 id="差异与好处-8"><a href="#差异与好处-8" class="headerlink" title="差异与好处"></a>差异与好处</h4><table><thead><tr><th>容器</th><th>特点</th><th>常用场景</th></tr></thead><tbody><tr><td><code>list</code></td><td>双向链表，任意位置插入删除快，但不能 O(1) 下标访问</td><td>较少用于算法题</td></tr><tr><td><code>deque</code></td><td>双端队列，两端插入删除快，支持下标访问</td><td>单调队列、滑动窗口</td></tr></tbody></table><h4 id="常见坑-8"><a href="#常见坑-8" class="headerlink" title="常见坑"></a>常见坑</h4><p><code>list</code> 不能写 <code>lst[i]</code>，因为链表不支持随机访问。</p><h4 id="刷题常用写法-8"><a href="#刷题常用写法-8" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h4><p>滑动窗口最大值常用 <code>deque&lt;int&gt;</code> 存下标：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs cpp">deque&lt;<span class="hljs-type">int</span>&gt; q;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; n; i++) &#123;<br>    <span class="hljs-keyword">while</span> (!q.<span class="hljs-built_in">empty</span>() &amp;&amp; nums[q.<span class="hljs-built_in">back</span>()] &lt;= nums[i]) q.<span class="hljs-built_in">pop_back</span>();<br>    q.<span class="hljs-built_in">push_back</span>(i);<br>    <span class="hljs-keyword">if</span> (q.<span class="hljs-built_in">front</span>() &lt;= i - k) q.<span class="hljs-built_in">pop_front</span>();<br>    <span class="hljs-keyword">if</span> (i &gt;= k - <span class="hljs-number">1</span>) ans.<span class="hljs-built_in">push_back</span>(nums[q.<span class="hljs-built_in">front</span>()]);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="7-4-map、unordered-map"><a href="#7-4-map、unordered-map" class="headerlink" title="7.4 map、unordered_map"></a>7.4 <code>map</code>、<code>unordered_map</code></h3><h4 id="C-里怎么写-9"><a href="#C-里怎么写-9" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h4><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">int</span> cnt[<span class="hljs-number">256</span>] = &#123;<span class="hljs-number">0</span>&#125;;<br>cnt[<span class="hljs-string">&#x27;a&#x27;</span>]++;<br></code></pre></td></tr></table></figure><h4 id="C-怎么写-9"><a href="#C-怎么写-9" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h4><figure class="highlight cpp"><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><code class="hljs cpp">unordered_map&lt;string, <span class="hljs-type">int</span>&gt; cnt;<br>cnt[<span class="hljs-string">&quot;apple&quot;</span>]++;<br>cnt[<span class="hljs-string">&quot;banana&quot;</span>] += <span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><h4 id="差异与好处-9"><a href="#差异与好处-9" class="headerlink" title="差异与好处"></a>差异与好处</h4><table><thead><tr><th>容器</th><th>底层直觉</th><th>查找复杂度</th><th>是否有序</th></tr></thead><tbody><tr><td><code>map</code></td><td>红黑树</td><td>O(log n)</td><td>按 key 有序</td></tr><tr><td><code>unordered_map</code></td><td>哈希表</td><td>平均 O(1)</td><td>无序</td></tr></tbody></table><p>常用 API：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs cpp">unordered_map&lt;string, <span class="hljs-type">int</span>&gt; mp;<br><br>mp[<span class="hljs-string">&quot;a&quot;</span>] = <span class="hljs-number">1</span>;<br>mp.<span class="hljs-built_in">count</span>(<span class="hljs-string">&quot;a&quot;</span>);<br>mp.<span class="hljs-built_in">find</span>(<span class="hljs-string">&quot;a&quot;</span>);<br>mp.<span class="hljs-built_in">erase</span>(<span class="hljs-string">&quot;a&quot;</span>);<br>mp.<span class="hljs-built_in">size</span>();<br>mp.<span class="hljs-built_in">empty</span>();<br></code></pre></td></tr></table></figure><h4 id="常见坑-9"><a href="#常见坑-9" class="headerlink" title="常见坑"></a>常见坑</h4><p><code>mp[key]</code> 如果 key 不存在，会自动插入默认值：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">unordered_map&lt;string, <span class="hljs-type">int</span>&gt; mp;<br>cout &lt;&lt; mp[<span class="hljs-string">&quot;x&quot;</span>] &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>; <span class="hljs-comment">// 插入 &quot;x&quot;，值为 0</span><br></code></pre></td></tr></table></figure><p>如果只想判断是否存在：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">if</span> (mp.<span class="hljs-built_in">count</span>(<span class="hljs-string">&quot;x&quot;</span>)) &#123;<br>    cout &lt;&lt; <span class="hljs-string">&quot;exists\n&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="刷题常用写法-9"><a href="#刷题常用写法-9" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h4><p>两数之和：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs cpp"><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">twoSum</span><span class="hljs-params">(vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums, <span class="hljs-type">int</span> target)</span> </span>&#123;<br>    unordered_map&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; pos;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; (<span class="hljs-type">int</span>)nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>        <span class="hljs-type">int</span> need = target - nums[i];<br>        <span class="hljs-keyword">if</span> (pos.<span class="hljs-built_in">count</span>(need)) &#123;<br>            <span class="hljs-keyword">return</span> &#123;pos[need], i&#125;;<br>        &#125;<br>        pos[nums[i]] = i;<br>    &#125;<br>    <span class="hljs-keyword">return</span> &#123;&#125;;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="7-5-set、unordered-set"><a href="#7-5-set、unordered-set" class="headerlink" title="7.5 set、unordered_set"></a>7.5 <code>set</code>、<code>unordered_set</code></h3><h4 id="C-里怎么写-10"><a href="#C-里怎么写-10" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h4><p>C 里通常用数组、排序、手写哈希来判断元素是否出现。</p><h4 id="C-怎么写-10"><a href="#C-怎么写-10" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs cpp">unordered_set&lt;<span class="hljs-type">int</span>&gt; seen;<br>seen.<span class="hljs-built_in">insert</span>(<span class="hljs-number">10</span>);<br><br><span class="hljs-keyword">if</span> (seen.<span class="hljs-built_in">count</span>(<span class="hljs-number">10</span>)) &#123;<br>    cout &lt;&lt; <span class="hljs-string">&quot;found\n&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="差异与好处-10"><a href="#差异与好处-10" class="headerlink" title="差异与好处"></a>差异与好处</h4><table><thead><tr><th>容器</th><th>查找复杂度</th><th>是否有序</th><th>是否去重</th></tr></thead><tbody><tr><td><code>set</code></td><td>O(log n)</td><td>有序</td><td>是</td></tr><tr><td><code>unordered_set</code></td><td>平均 O(1)</td><td>无序</td><td>是</td></tr></tbody></table><h4 id="常见坑-10"><a href="#常见坑-10" class="headerlink" title="常见坑"></a>常见坑</h4><p><code>set</code> 和 <code>unordered_set</code> 只存 key，不存 value。如果你要记录次数，用 <code>map/unordered_map</code>。</p><h4 id="刷题常用写法-10"><a href="#刷题常用写法-10" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">hasDuplicate</span><span class="hljs-params">(vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums)</span> </span>&#123;<br>    unordered_set&lt;<span class="hljs-type">int</span>&gt; seen;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>        <span class="hljs-keyword">if</span> (seen.<span class="hljs-built_in">count</span>(x)) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>        seen.<span class="hljs-built_in">insert</span>(x);<br>    &#125;<br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="7-6-queue、stack、priority-queue"><a href="#7-6-queue、stack、priority-queue" class="headerlink" title="7.6 queue、stack、priority_queue"></a>7.6 <code>queue</code>、<code>stack</code>、<code>priority_queue</code></h3><h4 id="C-里怎么写-11"><a href="#C-里怎么写-11" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h4><p>C 语言一般用数组模拟栈、队列、堆。</p><h4 id="C-怎么写-11"><a href="#C-怎么写-11" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs cpp">stack&lt;<span class="hljs-type">int</span>&gt; st;<br>st.<span class="hljs-built_in">push</span>(<span class="hljs-number">1</span>);<br>st.<span class="hljs-built_in">top</span>();<br>st.<span class="hljs-built_in">pop</span>();<br><br>queue&lt;<span class="hljs-type">int</span>&gt; q;<br>q.<span class="hljs-built_in">push</span>(<span class="hljs-number">1</span>);<br>q.<span class="hljs-built_in">front</span>();<br>q.<span class="hljs-built_in">pop</span>();<br></code></pre></td></tr></table></figure><p>堆：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp">priority_queue&lt;<span class="hljs-type">int</span>&gt; max_heap;<br>max_heap.<span class="hljs-built_in">push</span>(<span class="hljs-number">3</span>);<br>max_heap.<span class="hljs-built_in">push</span>(<span class="hljs-number">1</span>);<br>cout &lt;&lt; max_heap.<span class="hljs-built_in">top</span>() &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>; <span class="hljs-comment">// 3</span><br></code></pre></td></tr></table></figure><p>小根堆：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">priority_queue&lt;<span class="hljs-type">int</span>, vector&lt;<span class="hljs-type">int</span>&gt;, greater&lt;<span class="hljs-type">int</span>&gt;&gt; min_heap;<br></code></pre></td></tr></table></figure><h4 id="差异与好处-11"><a href="#差异与好处-11" class="headerlink" title="差异与好处"></a>差异与好处</h4><table><thead><tr><th>容器</th><th>语义</th><th>常用场景</th></tr></thead><tbody><tr><td><code>stack</code></td><td>后进先出</td><td>括号匹配、单调栈</td></tr><tr><td><code>queue</code></td><td>先进先出</td><td>BFS</td></tr><tr><td><code>priority_queue</code></td><td>堆</td><td>Top K、Dijkstra</td></tr></tbody></table><h4 id="常见坑-11"><a href="#常见坑-11" class="headerlink" title="常见坑"></a>常见坑</h4><p><code>pop()</code> 不返回元素，必须先 <code>top()</code> 或 <code>front()</code>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span> x = st.<span class="hljs-built_in">top</span>();<br>st.<span class="hljs-built_in">pop</span>();<br></code></pre></td></tr></table></figure><p><code>priority_queue</code> 默认是大根堆。</p><h4 id="刷题常用写法-11"><a href="#刷题常用写法-11" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h4><p>BFS：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp">queue&lt;<span class="hljs-type">int</span>&gt; q;<br><span class="hljs-function">vector&lt;<span class="hljs-type">bool</span>&gt; <span class="hljs-title">visited</span><span class="hljs-params">(n, <span class="hljs-literal">false</span>)</span></span>;<br><br>q.<span class="hljs-built_in">push</span>(start);<br>visited[start] = <span class="hljs-literal">true</span>;<br><br><span class="hljs-keyword">while</span> (!q.<span class="hljs-built_in">empty</span>()) &#123;<br>    <span class="hljs-type">int</span> cur = q.<span class="hljs-built_in">front</span>();<br>    q.<span class="hljs-built_in">pop</span>();<br><br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> next : graph[cur]) &#123;<br>        <span class="hljs-keyword">if</span> (!visited[next]) &#123;<br>            visited[next] = <span class="hljs-literal">true</span>;<br>            q.<span class="hljs-built_in">push</span>(next);<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="8-迭代器、范围与算法库"><a href="#8-迭代器、范围与算法库" class="headerlink" title="8. 迭代器、范围与算法库"></a>8. 迭代器、范围与算法库</h2><h3 id="这是什么-7"><a href="#这是什么-7" class="headerlink" title="这是什么"></a>这是什么</h3><p>迭代器可以理解成 STL 容器里的“泛化指针”。算法库通过迭代器操作不同容器。</p><h3 id="C-里怎么写-12"><a href="#C-里怎么写-12" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><p>C 语言排序常用 <code>qsort</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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">cmp</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">void</span>* a, <span class="hljs-type">const</span> <span class="hljs-type">void</span>* b)</span> &#123;<br>    <span class="hljs-keyword">return</span> (*(<span class="hljs-type">int</span>*)a) - (*(<span class="hljs-type">int</span>*)b);<br>&#125;<br><br>qsort(arr, n, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">int</span>), cmp);<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-12"><a href="#C-怎么写-12" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>());<br></code></pre></td></tr></table></figure><p>自定义排序：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b) &#123;<br>    <span class="hljs-keyword">return</span> a &gt; b;<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="差异与好处-12"><a href="#差异与好处-12" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>常见算法库函数：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs cpp"><span class="hljs-built_in">sort</span>(v.<span class="hljs-built_in">begin</span>(), v.<span class="hljs-built_in">end</span>());<br><span class="hljs-built_in">reverse</span>(v.<span class="hljs-built_in">begin</span>(), v.<span class="hljs-built_in">end</span>());<br><br><span class="hljs-type">int</span> a = <span class="hljs-built_in">max</span>(x, y);<br><span class="hljs-type">int</span> b = <span class="hljs-built_in">min</span>(x, y);<br><br><span class="hljs-keyword">auto</span> it = <span class="hljs-built_in">lower_bound</span>(v.<span class="hljs-built_in">begin</span>(), v.<span class="hljs-built_in">end</span>(), target);<br><span class="hljs-keyword">auto</span> it2 = <span class="hljs-built_in">upper_bound</span>(v.<span class="hljs-built_in">begin</span>(), v.<span class="hljs-built_in">end</span>(), target);<br></code></pre></td></tr></table></figure><p><code>lower_bound</code> 和 <code>upper_bound</code> 要求区间有序：</p><ul><li><code>lower_bound</code>：第一个 <code>&gt;= target</code> 的位置。</li><li><code>upper_bound</code>：第一个 <code>&gt; target</code> 的位置。</li></ul><p>例子：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; v&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>&#125;;<br><br><span class="hljs-keyword">auto</span> l = <span class="hljs-built_in">lower_bound</span>(v.<span class="hljs-built_in">begin</span>(), v.<span class="hljs-built_in">end</span>(), <span class="hljs-number">2</span>);<br><span class="hljs-keyword">auto</span> r = <span class="hljs-built_in">upper_bound</span>(v.<span class="hljs-built_in">begin</span>(), v.<span class="hljs-built_in">end</span>(), <span class="hljs-number">2</span>);<br><br>cout &lt;&lt; (l - v.<span class="hljs-built_in">begin</span>()) &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>; <span class="hljs-comment">// 1</span><br>cout &lt;&lt; (r - v.<span class="hljs-built_in">begin</span>()) &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>; <span class="hljs-comment">// 4</span><br></code></pre></td></tr></table></figure><h3 id="常见坑-12"><a href="#常见坑-12" class="headerlink" title="常见坑"></a>常见坑</h3><p>迭代器不是所有容器都能相减。<code>vector</code> 支持 <code>it - v.begin()</code>，<code>list</code> 不支持。</p><p><code>sort</code> 不能直接用于 <code>list</code>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">list&lt;<span class="hljs-type">int</span>&gt; lst&#123;<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>&#125;;<br>lst.<span class="hljs-built_in">sort</span>();<br></code></pre></td></tr></table></figure><p>自定义排序比较器必须满足严格弱序，不能乱写：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-built_in">sort</span>(v.<span class="hljs-built_in">begin</span>(), v.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b) &#123;<br>    <span class="hljs-keyword">return</span> a &lt; b; <span class="hljs-comment">// 正确</span><br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="刷题常用写法-12"><a href="#刷题常用写法-12" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><p>按照二维数组的第二项排序：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">vector&lt;vector&lt;<span class="hljs-type">int</span>&gt;&gt; intervals&#123;&#123;<span class="hljs-number">1</span>, <span class="hljs-number">3</span>&#125;, &#123;<span class="hljs-number">2</span>, <span class="hljs-number">4</span>&#125;, &#123;<span class="hljs-number">0</span>, <span class="hljs-number">2</span>&#125;&#125;;<br><br><span class="hljs-built_in">sort</span>(intervals.<span class="hljs-built_in">begin</span>(), intervals.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">const</span> <span class="hljs-keyword">auto</span>&amp; a, <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span>&amp; b) &#123;<br>    <span class="hljs-keyword">return</span> a[<span class="hljs-number">1</span>] &lt; b[<span class="hljs-number">1</span>];<br>&#125;);<br></code></pre></td></tr></table></figure><p>对结构体排序：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Node</span> &#123;<br>    <span class="hljs-type">int</span> x;<br>    <span class="hljs-type">int</span> y;<br>&#125;;<br><br>vector&lt;Node&gt; nodes&#123;&#123;<span class="hljs-number">1</span>, <span class="hljs-number">3</span>&#125;, &#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>&#125;, &#123;<span class="hljs-number">0</span>, <span class="hljs-number">5</span>&#125;&#125;;<br><br><span class="hljs-built_in">sort</span>(nodes.<span class="hljs-built_in">begin</span>(), nodes.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">const</span> Node&amp; a, <span class="hljs-type">const</span> Node&amp; b) &#123;<br>    <span class="hljs-keyword">if</span> (a.x != b.x) <span class="hljs-keyword">return</span> a.x &lt; b.x;<br>    <span class="hljs-keyword">return</span> a.y &lt; b.y;<br>&#125;);<br></code></pre></td></tr></table></figure><hr><h2 id="9-结构体、类与面向对象"><a href="#9-结构体、类与面向对象" class="headerlink" title="9. 结构体、类与面向对象"></a>9. 结构体、类与面向对象</h2><h3 id="这是什么-8"><a href="#这是什么-8" class="headerlink" title="这是什么"></a>这是什么</h3><p>C++ 的 <code>struct</code> 和 <code>class</code> 都可以包含成员变量和成员函数。区别主要是默认访问权限：</p><ul><li><code>struct</code> 默认 <code>public</code>。</li><li><code>class</code> 默认 <code>private</code>。</li></ul><h3 id="C-里怎么写-13"><a href="#C-里怎么写-13" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span> &#123;</span><br>    <span class="hljs-type">int</span> x;<br>    <span class="hljs-type">int</span> y;<br>&#125;;<br><br><span class="hljs-type">void</span> <span class="hljs-title function_">move</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> Point* p, <span class="hljs-type">int</span> dx, <span class="hljs-type">int</span> dy)</span> &#123;<br>    p-&gt;x += dx;<br>    p-&gt;y += dy;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-13"><a href="#C-怎么写-13" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Point</span> &#123;<br>    <span class="hljs-type">int</span> x;<br>    <span class="hljs-type">int</span> y;<br><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">move</span><span class="hljs-params">(<span class="hljs-type">int</span> dx, <span class="hljs-type">int</span> dy)</span> </span>&#123;<br>        x += dx;<br>        y += dy;<br>    &#125;<br>&#125;;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    Point p&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>&#125;;<br>    p.<span class="hljs-built_in">move</span>(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-13"><a href="#差异与好处-13" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>类可以封装数据和行为：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Counter</span> &#123;<br><span class="hljs-keyword">private</span>:<br>    <span class="hljs-type">int</span> value;<br><br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-built_in">Counter</span>() : <span class="hljs-built_in">value</span>(<span class="hljs-number">0</span>) &#123;&#125;<br><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">add</span><span class="hljs-params">()</span> </span>&#123;<br>        value++;<br>    &#125;<br><br>    <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">get</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> value;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>构造函数：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Node</span> &#123;<br>    <span class="hljs-type">int</span> val;<br>    Node* next;<br><br>    <span class="hljs-built_in">Node</span>(<span class="hljs-type">int</span> v) : <span class="hljs-built_in">val</span>(v), <span class="hljs-built_in">next</span>(<span class="hljs-literal">nullptr</span>) &#123;&#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>继承和虚函数：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Animal</span> &#123;<br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">speak</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>= <span class="hljs-number">0</span>;<br>    <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">Animal</span>() = <span class="hljs-keyword">default</span>;<br>&#125;;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Dog</span> : <span class="hljs-keyword">public</span> Animal &#123;<br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">speak</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">override</span> </span>&#123;<br>        cout &lt;&lt; <span class="hljs-string">&quot;wang\n&quot;</span>;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="常见坑-13"><a href="#常见坑-13" class="headerlink" title="常见坑"></a>常见坑</h3><p>如果一个类要被继承并通过基类指针删除对象，析构函数应该是 <code>virtual</code>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Base</span> &#123;<br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">Base</span>() = <span class="hljs-keyword">default</span>;<br>&#125;;<br></code></pre></td></tr></table></figure><p>成员函数后面的 <code>const</code> 表示这个函数不会修改对象：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">get</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> value;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="刷题常用写法-13"><a href="#刷题常用写法-13" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><p>LeetCode 风格：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> &#123;<br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">twoSum</span><span class="hljs-params">(vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums, <span class="hljs-type">int</span> target)</span> </span>&#123;<br>        unordered_map&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; pos;<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; (<span class="hljs-type">int</span>)nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>            <span class="hljs-type">int</span> need = target - nums[i];<br>            <span class="hljs-keyword">if</span> (pos.<span class="hljs-built_in">count</span>(need)) &#123;<br>                <span class="hljs-keyword">return</span> &#123;pos[need], i&#125;;<br>            &#125;<br>            pos[nums[i]] = i;<br>        &#125;<br>        <span class="hljs-keyword">return</span> &#123;&#125;;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>二叉树节点常见定义：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">TreeNode</span> &#123;<br>    <span class="hljs-type">int</span> val;<br>    TreeNode* left;<br>    TreeNode* right;<br><br>    <span class="hljs-built_in">TreeNode</span>(<span class="hljs-type">int</span> x) : <span class="hljs-built_in">val</span>(x), <span class="hljs-built_in">left</span>(<span class="hljs-literal">nullptr</span>), <span class="hljs-built_in">right</span>(<span class="hljs-literal">nullptr</span>) &#123;&#125;<br>&#125;;<br></code></pre></td></tr></table></figure><hr><h2 id="10-模板与泛型"><a href="#10-模板与泛型" class="headerlink" title="10. 模板与泛型"></a>10. 模板与泛型</h2><h3 id="这是什么-9"><a href="#这是什么-9" class="headerlink" title="这是什么"></a>这是什么</h3><p>模板让你写“和类型无关”的代码。STL 的 <code>vector&lt;int&gt;</code>、<code>vector&lt;string&gt;</code>、<code>map&lt;string, int&gt;</code> 都是模板的应用。</p><h3 id="C-里怎么写-14"><a href="#C-里怎么写-14" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><p>C 语言如果想写通用函数，经常要用 <code>void*</code>，类型不安全：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">void</span> <span class="hljs-title function_">swap_any</span><span class="hljs-params">(<span class="hljs-type">void</span>* a, <span class="hljs-type">void</span>* b, <span class="hljs-type">int</span> size)</span>;<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-14"><a href="#C-怎么写-14" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><p>函数模板：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;<br><span class="hljs-function">T <span class="hljs-title">my_max</span><span class="hljs-params">(T a, T b)</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> a &gt; b ? a : b;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    cout &lt;&lt; <span class="hljs-built_in">my_max</span>(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>) &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    cout &lt;&lt; <span class="hljs-built_in">my_max</span>(<span class="hljs-number">2.5</span>, <span class="hljs-number">1.2</span>) &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>类模板：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Box</span> &#123;<br><span class="hljs-keyword">private</span>:<br>    T value;<br><br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-built_in">Box</span>(T v) : <span class="hljs-built_in">value</span>(v) &#123;&#125;<br><br>    <span class="hljs-function">T <span class="hljs-title">get</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> value;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-14"><a href="#差异与好处-14" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>模板是编译期生成代码，类型安全：</p><figure class="highlight cpp"><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; a;<br>vector&lt;string&gt; b;<br>unordered_map&lt;string, <span class="hljs-type">int</span>&gt; cnt;<br></code></pre></td></tr></table></figure><p>你不需要现在就深入模板元编程，但至少要能看懂：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print_vec</span><span class="hljs-params">(<span class="hljs-type">const</span> vector&lt;T&gt;&amp; v)</span> </span>&#123;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">const</span> T&amp; x : v) &#123;<br>        cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>    &#125;<br>    cout &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="常见坑-14"><a href="#常见坑-14" class="headerlink" title="常见坑"></a>常见坑</h3><p>模板错误信息可能很长。初学阶段先记住：模板要求你使用的类型支持对应操作。</p><p>比如这个函数要求 <code>T</code> 支持 <code>&gt;</code>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">typename</span> T&gt;<br><span class="hljs-function">T <span class="hljs-title">my_max</span><span class="hljs-params">(T a, T b)</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> a &gt; b ? a : b;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="刷题常用写法-14"><a href="#刷题常用写法-14" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><p>刷题时很少自己写复杂模板，但经常使用模板容器：</p><figure class="highlight cpp"><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><code class="hljs cpp">vector&lt;vector&lt;<span class="hljs-type">int</span>&gt;&gt; grid;<br>unordered_map&lt;string, vector&lt;<span class="hljs-type">int</span>&gt;&gt; mp;<br>priority_queue&lt;pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt;, vector&lt;pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt;&gt;, greater&lt;pair&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt;&gt;&gt; pq;<br></code></pre></td></tr></table></figure><hr><h2 id="11-异常、内存与现代-C-补充"><a href="#11-异常、内存与现代-C-补充" class="headerlink" title="11. 异常、内存与现代 C++ 补充"></a>11. 异常、内存与现代 C++ 补充</h2><h3 id="这是什么-10"><a href="#这是什么-10" class="headerlink" title="这是什么"></a>这是什么</h3><p>C++ 提供异常机制、对象生命周期管理和智能指针。刷算法不常用，但它们是工程 C++ 的核心。</p><h3 id="C-里怎么写-15"><a href="#C-里怎么写-15" class="headerlink" title="C 里怎么写"></a>C 里怎么写</h3><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">int</span> <span class="hljs-title function_">divide</span><span class="hljs-params">(<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b, <span class="hljs-type">int</span>* result)</span> &#123;<br>    <span class="hljs-keyword">if</span> (b == <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br>    *result = a / b;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">int</span>* p = (<span class="hljs-type">int</span>*)<span class="hljs-built_in">malloc</span>(<span class="hljs-keyword">sizeof</span>(<span class="hljs-type">int</span>) * n);<br><span class="hljs-built_in">free</span>(p);<br></code></pre></td></tr></table></figure><h3 id="C-怎么写-15"><a href="#C-怎么写-15" class="headerlink" title="C++ 怎么写"></a>C++ 怎么写</h3><p>异常：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">divide</span><span class="hljs-params">(<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b)</span> </span>&#123;<br>    <span class="hljs-keyword">if</span> (b == <span class="hljs-number">0</span>) &#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-built_in">runtime_error</span>(<span class="hljs-string">&quot;divide by zero&quot;</span>);<br>    &#125;<br>    <span class="hljs-keyword">return</span> a / b;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        cout &lt;&lt; <span class="hljs-built_in">divide</span>(<span class="hljs-number">10</span>, <span class="hljs-number">0</span>) &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    &#125; <span class="hljs-built_in">catch</span> (<span class="hljs-type">const</span> exception&amp; e) &#123;<br>        cout &lt;&lt; e.<span class="hljs-built_in">what</span>() &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>动态内存：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span>* p = <span class="hljs-keyword">new</span> <span class="hljs-type">int</span>[<span class="hljs-number">10</span>];<br><span class="hljs-keyword">delete</span>[] p;<br></code></pre></td></tr></table></figure><p>更现代的写法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">auto</span> p = <span class="hljs-built_in">make_unique</span>&lt;<span class="hljs-type">int</span>&gt;(<span class="hljs-number">10</span>);<br>cout &lt;&lt; *p &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br></code></pre></td></tr></table></figure><h3 id="差异与好处-15"><a href="#差异与好处-15" class="headerlink" title="差异与好处"></a>差异与好处</h3><p>C++ 更强调 RAII：资源在对象构造时获取，在对象析构时释放。</p><p>比如 <code>vector</code> 会自动管理内存：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(<span class="hljs-number">1000000</span>)</span></span>;<br>&#125; <span class="hljs-comment">// 离开作用域后自动释放</span><br></code></pre></td></tr></table></figure><p>智能指针：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">unique_ptr&lt;<span class="hljs-type">int</span>&gt; p1 = <span class="hljs-built_in">make_unique</span>&lt;<span class="hljs-type">int</span>&gt;(<span class="hljs-number">10</span>);<br>shared_ptr&lt;<span class="hljs-type">int</span>&gt; p2 = <span class="hljs-built_in">make_shared</span>&lt;<span class="hljs-type">int</span>&gt;(<span class="hljs-number">20</span>);<br></code></pre></td></tr></table></figure><table><thead><tr><th>工具</th><th>含义</th></tr></thead><tbody><tr><td><code>unique_ptr</code></td><td>独占所有权，不能随便复制</td></tr><tr><td><code>shared_ptr</code></td><td>共享所有权，引用计数</td></tr><tr><td><code>weak_ptr</code></td><td>弱引用，避免循环引用</td></tr></tbody></table><h3 id="常见坑-15"><a href="#常见坑-15" class="headerlink" title="常见坑"></a>常见坑</h3><p>不要混用申请和释放方式：</p><table><thead><tr><th>申请</th><th>释放</th></tr></thead><tbody><tr><td><code>malloc</code></td><td><code>free</code></td></tr><tr><td><code>new</code></td><td><code>delete</code></td></tr><tr><td><code>new[]</code></td><td><code>delete[]</code></td></tr></tbody></table><p>刷题里通常不需要手写 <code>new/delete</code>，优先用 <code>vector</code>、<code>string</code> 和局部对象。</p><h3 id="刷题常用写法-15"><a href="#刷题常用写法-15" class="headerlink" title="刷题常用写法"></a>刷题常用写法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(n)</span></span>;<br>string s;<br>queue&lt;<span class="hljs-type">int</span>&gt; q;<br>unordered_map&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; mp;<br></code></pre></td></tr></table></figure><p>算法题中的链表、树节点通常由平台创建，你只需要操作指针：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-function">ListNode* <span class="hljs-title">reverseList</span><span class="hljs-params">(ListNode* head)</span> </span>&#123;<br>    ListNode* pre = <span class="hljs-literal">nullptr</span>;<br>    ListNode* cur = head;<br><br>    <span class="hljs-keyword">while</span> (cur != <span class="hljs-literal">nullptr</span>) &#123;<br>        ListNode* nxt = cur-&gt;next;<br>        cur-&gt;next = pre;<br>        pre = cur;<br>        cur = nxt;<br>    &#125;<br><br>    <span class="hljs-keyword">return</span> pre;<br>&#125;<br></code></pre></td></tr></table></figure><hr><h2 id="12-C-到-C-迁移速查表"><a href="#12-C-到-C-迁移速查表" class="headerlink" title="12. C 到 C++ 迁移速查表"></a>12. C 到 C++ 迁移速查表</h2><h3 id="12-1-基础写法对照"><a href="#12-1-基础写法对照" class="headerlink" title="12.1 基础写法对照"></a>12.1 基础写法对照</h3><table><thead><tr><th>目标</th><th>C 写法</th><th>C++ 写法</th></tr></thead><tbody><tr><td>输出整数</td><td><code>printf(&quot;%d\n&quot;, x);</code></td><td><code>cout &lt;&lt; x &lt;&lt; &#39;\n&#39;;</code></td></tr><tr><td>输入整数</td><td><code>scanf(&quot;%d&quot;, &amp;x);</code></td><td><code>cin &gt;&gt; x;</code></td></tr><tr><td>字符串</td><td><code>char s[100];</code></td><td><code>string s;</code></td></tr><tr><td>字符串长度</td><td><code>strlen(s)</code></td><td><code>s.size()</code></td></tr><tr><td>字符串拼接</td><td><code>strcat(a, b)</code></td><td><code>a + b</code></td></tr><tr><td>动态数组</td><td><code>malloc</code> + 手动扩容</td><td><code>vector&lt;int&gt;</code></td></tr><tr><td>哈希计数</td><td>手写哈希表</td><td><code>unordered_map&lt;int, int&gt;</code></td></tr><tr><td>集合判重</td><td>排序&#x2F;手写哈希</td><td><code>unordered_set&lt;int&gt;</code></td></tr><tr><td>排序</td><td><code>qsort</code></td><td><code>sort(v.begin(), v.end())</code></td></tr><tr><td>函数修改实参</td><td>指针 <code>int* p</code></td><td>引用 <code>int&amp; x</code></td></tr></tbody></table><h3 id="12-2-常用-STL-速查"><a href="#12-2-常用-STL-速查" class="headerlink" title="12.2 常用 STL 速查"></a>12.2 常用 STL 速查</h3><table><thead><tr><th>容器</th><th>头文件</th><th>核心操作</th><th>复杂度直觉</th></tr></thead><tbody><tr><td><code>vector</code></td><td><code>&lt;vector&gt;</code></td><td><code>push_back</code>、<code>pop_back</code>、<code>[]</code></td><td>尾插均摊 O(1)，访问 O(1)</td></tr><tr><td><code>string</code></td><td><code>&lt;string&gt;</code></td><td><code>+</code>、<code>substr</code>、<code>find</code></td><td>类似动态字符数组</td></tr><tr><td><code>deque</code></td><td><code>&lt;deque&gt;</code></td><td><code>push_front</code>、<code>push_back</code></td><td>两端操作 O(1)</td></tr><tr><td><code>stack</code></td><td><code>&lt;stack&gt;</code></td><td><code>push</code>、<code>top</code>、<code>pop</code></td><td>O(1)</td></tr><tr><td><code>queue</code></td><td><code>&lt;queue&gt;</code></td><td><code>push</code>、<code>front</code>、<code>pop</code></td><td>O(1)</td></tr><tr><td><code>priority_queue</code></td><td><code>&lt;queue&gt;</code></td><td><code>push</code>、<code>top</code>、<code>pop</code></td><td>插入删除 O(log n)</td></tr><tr><td><code>map</code></td><td><code>&lt;map&gt;</code></td><td><code>[]</code>、<code>find</code>、<code>count</code></td><td>O(log n)，key 有序</td></tr><tr><td><code>unordered_map</code></td><td><code>&lt;unordered_map&gt;</code></td><td><code>[]</code>、<code>find</code>、<code>count</code></td><td>平均 O(1)，key 无序</td></tr><tr><td><code>set</code></td><td><code>&lt;set&gt;</code></td><td><code>insert</code>、<code>erase</code>、<code>count</code></td><td>O(log n)，有序去重</td></tr><tr><td><code>unordered_set</code></td><td><code>&lt;unordered_set&gt;</code></td><td><code>insert</code>、<code>erase</code>、<code>count</code></td><td>平均 O(1)，无序去重</td></tr></tbody></table><h3 id="12-3-算法题-C-模板代码"><a href="#12-3-算法题-C-模板代码" class="headerlink" title="12.3 算法题 C++ 模板代码"></a>12.3 算法题 C++ 模板代码</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-keyword">using</span> ll = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br>    cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br>    <span class="hljs-type">int</span> n;<br>    cin &gt;&gt; n;<br><br>    <span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(n)</span></span>;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span>&amp; x : nums) &#123;<br>        cin &gt;&gt; x;<br>    &#125;<br><br>    unordered_map&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; cnt;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>        cnt[x]++;<br>    &#125;<br><br>    <span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>());<br><br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>        cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>    &#125;<br>    cout &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="12-4-LeetCode-风格模板"><a href="#12-4-LeetCode-风格模板" class="headerlink" title="12.4 LeetCode 风格模板"></a>12.4 LeetCode 风格模板</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> &#123;<br><span class="hljs-keyword">public</span>:<br>    <span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">twoSum</span><span class="hljs-params">(vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums, <span class="hljs-type">int</span> target)</span> </span>&#123;<br>        unordered_map&lt;<span class="hljs-type">int</span>, <span class="hljs-type">int</span>&gt; pos;<br><br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; (<span class="hljs-type">int</span>)nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>            <span class="hljs-type">int</span> need = target - nums[i];<br>            <span class="hljs-keyword">if</span> (pos.<span class="hljs-built_in">count</span>(need)) &#123;<br>                <span class="hljs-keyword">return</span> &#123;pos[need], i&#125;;<br>            &#125;<br>            pos[nums[i]] = i;<br>        &#125;<br><br>        <span class="hljs-keyword">return</span> &#123;&#125;;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><hr><h2 id="13-建议学习路线"><a href="#13-建议学习路线" class="headerlink" title="13. 建议学习路线"></a>13. 建议学习路线</h2><h3 id="第一轮：能写出-C-算法题"><a href="#第一轮：能写出-C-算法题" class="headerlink" title="第一轮：能写出 C++ 算法题"></a>第一轮：能写出 C++ 算法题</h3><p>优先学：</p><ul><li><code>cin/cout</code></li><li><code>string</code></li><li><code>vector</code></li><li>引用 <code>&amp;</code></li><li><code>auto</code></li><li>range-based for</li><li><code>unordered_map</code>、<code>unordered_set</code></li><li><code>queue</code>、<code>stack</code>、<code>priority_queue</code></li><li><code>sort</code> 和 lambda</li></ul><p>目标：把以前用 C 写数组、字符串、哈希表、排序的代码迁移到 C++ STL。</p><h3 id="第二轮：能看懂常见-C-代码"><a href="#第二轮：能看懂常见-C-代码" class="headerlink" title="第二轮：能看懂常见 C++ 代码"></a>第二轮：能看懂常见 C++ 代码</h3><p>继续学：</p><ul><li><code>class</code>、<code>struct</code></li><li>构造函数</li><li><code>public/private</code></li><li><code>const</code> 成员函数</li><li>函数重载</li><li>模板基础</li></ul><p>目标：能看懂 LeetCode、labuladong 算法笔记、普通工程代码里的类和模板容器。</p><h3 id="第三轮：进入现代-C-工程"><a href="#第三轮：进入现代-C-工程" class="headerlink" title="第三轮：进入现代 C++ 工程"></a>第三轮：进入现代 C++ 工程</h3><p>再学：</p><ul><li>RAII</li><li>智能指针</li><li>移动语义</li><li>异常安全</li><li>标准库更深层用法</li><li>CMake 和工程组织</li></ul><p>目标：写更可靠、更工程化的 C++。</p><hr><h2 id="14-最后给-C-使用者的迁移建议"><a href="#14-最后给-C-使用者的迁移建议" class="headerlink" title="14. 最后给 C 使用者的迁移建议"></a>14. 最后给 C 使用者的迁移建议</h2><p>不要把 C++ 当成“必须全部用类重写的 C”。对算法学习来说，最重要的是：</p><ol><li>用 <code>vector</code> 替代大部分手写动态数组。</li><li>用 <code>string</code> 替代大部分 <code>char[]</code>。</li><li>用 <code>unordered_map/unordered_set</code> 替代手写哈希。</li><li>用引用传参避免复制大对象。</li><li>用 STL 算法替代 <code>qsort</code> 和大量手写循环。</li><li>能看懂 <code>class Solution</code>、模板容器、lambda，就已经能读大多数算法题 C++ 代码。</li></ol><p>C++ 的完整体系很大，但刷算法和语法迁移不需要一口气啃完。先把 C++ 当作“C + 更强大的标准库 + 更清晰的抽象工具”，你会迁移得快很多。</p><hr><h2 id="15-标准语法格式补充"><a href="#15-标准语法格式补充" class="headerlink" title="15. 标准语法格式补充"></a>15. 标准语法格式补充</h2><p>这一节用来补足“查语法”的需求。前面的章节偏迁移理解，这里把常用写法集中列出来，方便以后快速回看。</p><h3 id="15-1-程序结构"><a href="#15-1-程序结构" class="headerlink" title="15.1 程序结构"></a>15.1 程序结构</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-comment">// 代码写在这里</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>刷题常用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;bits/stdc++.h&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br>    cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>工程更推荐：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;vector&gt;</span></span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;<br>    std::vector&lt;<span class="hljs-type">int</span>&gt; nums;<br>    std::cout &lt;&lt; nums.<span class="hljs-built_in">size</span>() &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="15-2-输入输出"><a href="#15-2-输入输出" class="headerlink" title="15.2 输入输出"></a>15.2 输入输出</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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><code class="hljs cpp"><span class="hljs-type">int</span> x;<br>cin &gt;&gt; x;<br><br>cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br></code></pre></td></tr></table></figure><p>多个变量：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-type">int</span> a, b;<br>cin &gt;&gt; a &gt;&gt; b;<br>cout &lt;&lt; a + b &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br></code></pre></td></tr></table></figure><p>读取一整行：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">string line;<br><span class="hljs-built_in">getline</span>(cin, line);<br></code></pre></td></tr></table></figure><h3 id="15-3-函数与引用传参"><a href="#15-3-函数与引用传参" class="headerlink" title="15.3 函数与引用传参"></a>15.3 函数与引用传参</h3><p>普通函数：</p><figure class="highlight cpp"><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><code class="hljs cpp">返回值类型 函数名(参数类型 参数名) &#123;<br>    <span class="hljs-keyword">return</span> 返回值;<br>&#125;<br></code></pre></td></tr></table></figure><p>引用传参：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">update</span><span class="hljs-params">(<span class="hljs-type">int</span>&amp; x)</span> </span>&#123;<br>    x++;<br>&#125;<br></code></pre></td></tr></table></figure><p>避免复制大对象：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print_vector</span><span class="hljs-params">(<span class="hljs-type">const</span> vector&lt;<span class="hljs-type">int</span>&gt;&amp; nums)</span> </span>&#123;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>        cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="15-4-vector"><a href="#15-4-vector" class="headerlink" title="15.4 vector"></a>15.4 vector</h3><figure class="highlight cpp"><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><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums;<br><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(n)</span></span>;<br><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(n, 初始值)</span></span>;<br></code></pre></td></tr></table></figure><p>遍历：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp"><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x : nums) &#123;<br>    cout &lt;&lt; x &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>&#125;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; (<span class="hljs-type">int</span>)nums.<span class="hljs-built_in">size</span>(); i++) &#123;<br>    cout &lt;&lt; nums[i] &lt;&lt; <span class="hljs-string">&#x27; &#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="15-5-string"><a href="#15-5-string" class="headerlink" title="15.5 string"></a>15.5 string</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">string s;<br>cin &gt;&gt; s;<br><br>string line;<br><span class="hljs-built_in">getline</span>(cin, line);<br></code></pre></td></tr></table></figure><p>拼接与截取：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">string a = <span class="hljs-string">&quot;hello&quot;</span>;<br>string b = <span class="hljs-string">&quot;world&quot;</span>;<br>string c = a + <span class="hljs-string">&quot; &quot;</span> + b;<br><br>string part = c.<span class="hljs-built_in">substr</span>(<span class="hljs-number">0</span>, <span class="hljs-number">5</span>);<br></code></pre></td></tr></table></figure><h3 id="15-6-unordered-map-unordered-set"><a href="#15-6-unordered-map-unordered-set" class="headerlink" title="15.6 unordered_map &#x2F; unordered_set"></a>15.6 unordered_map &#x2F; unordered_set</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cpp">unordered_map&lt;string, <span class="hljs-type">int</span>&gt; count;<br>count[<span class="hljs-string">&quot;apple&quot;</span>]++;<br><br>unordered_set&lt;<span class="hljs-type">int</span>&gt; seen;<br>seen.<span class="hljs-built_in">insert</span>(<span class="hljs-number">10</span>);<br></code></pre></td></tr></table></figure><p>查找：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cpp"><span class="hljs-keyword">if</span> (count.<span class="hljs-built_in">count</span>(<span class="hljs-string">&quot;apple&quot;</span>)) &#123;<br>    cout &lt;&lt; count[<span class="hljs-string">&quot;apple&quot;</span>] &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br><br><span class="hljs-keyword">if</span> (seen.<span class="hljs-built_in">find</span>(<span class="hljs-number">10</span>) != seen.<span class="hljs-built_in">end</span>()) &#123;<br>    cout &lt;&lt; <span class="hljs-string">&quot;found\n&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="15-7-sort-和-lambda"><a href="#15-7-sort-和-lambda" class="headerlink" title="15.7 sort 和 lambda"></a>15.7 sort 和 lambda</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>());<br><span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>(), <span class="hljs-built_in">greater</span>&lt;<span class="hljs-type">int</span>&gt;());<br></code></pre></td></tr></table></figure><p>自定义排序：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-built_in">sort</span>(items.<span class="hljs-built_in">begin</span>(), items.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">const</span> Item&amp; a, <span class="hljs-type">const</span> Item&amp; b) &#123;<br>    <span class="hljs-keyword">return</span> a.score &gt; b.score;<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="15-8-class-struct"><a href="#15-8-class-struct" class="headerlink" title="15.8 class &#x2F; struct"></a>15.8 class &#x2F; struct</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Point</span> &#123;<br>    <span class="hljs-type">int</span> x;<br>    <span class="hljs-type">int</span> y;<br>&#125;;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> &#123;<br><span class="hljs-keyword">public</span>:<br>    string name;<br><br>    <span class="hljs-built_in">User</span>(string name) : <span class="hljs-built_in">name</span>(name) &#123;&#125;<br><br>    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>&#123;<br>        cout &lt;&lt; <span class="hljs-string">&quot;hello &quot;</span> &lt;&lt; name &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><hr><h2 id="16-STL-常用方法速查表"><a href="#16-STL-常用方法速查表" class="headerlink" title="16. STL 常用方法速查表"></a>16. STL 常用方法速查表</h2><h3 id="16-1-vector"><a href="#16-1-vector" class="headerlink" title="16.1 vector"></a>16.1 vector</h3><table><thead><tr><th>方法</th><th>作用</th><th>常见用法</th></tr></thead><tbody><tr><td><code>push_back(x)</code></td><td>末尾添加元素</td><td><code>nums.push_back(10);</code></td></tr><tr><td><code>pop_back()</code></td><td>删除最后一个元素</td><td><code>nums.pop_back();</code></td></tr><tr><td><code>size()</code></td><td>元素个数</td><td><code>nums.size()</code></td></tr><tr><td><code>empty()</code></td><td>是否为空</td><td><code>nums.empty()</code></td></tr><tr><td><code>clear()</code></td><td>清空数组</td><td><code>nums.clear()</code></td></tr><tr><td><code>front()</code></td><td>第一个元素</td><td><code>nums.front()</code></td></tr><tr><td><code>back()</code></td><td>最后一个元素</td><td><code>nums.back()</code></td></tr><tr><td><code>begin()</code> &#x2F; <code>end()</code></td><td>迭代器范围</td><td><code>sort(nums.begin(), nums.end());</code></td></tr></tbody></table><p>常见坑：</p><ul><li><code>nums[i]</code> 不会检查越界。</li><li><code>nums.size()</code> 是无符号整数，和 <code>int</code> 比较时容易出现警告，刷题可写 <code>(int)nums.size()</code>。</li><li>删除中间元素会移动后面的元素，频繁删除中间元素不适合用 <code>vector</code>。</li></ul><h3 id="16-2-string"><a href="#16-2-string" class="headerlink" title="16.2 string"></a>16.2 string</h3><table><thead><tr><th>方法</th><th>作用</th><th>常见用法</th></tr></thead><tbody><tr><td><code>size()</code> &#x2F; <code>length()</code></td><td>字符串长度</td><td><code>s.size()</code></td></tr><tr><td><code>substr(pos, len)</code></td><td>截取子串</td><td><code>s.substr(0, 3)</code></td></tr><tr><td><code>find(x)</code></td><td>查找位置</td><td><code>s.find(&quot;abc&quot;)</code></td></tr><tr><td><code>replace(pos, len, str)</code></td><td>替换内容</td><td><code>s.replace(0, 3, &quot;hi&quot;)</code></td></tr><tr><td><code>push_back(ch)</code></td><td>追加字符</td><td><code>s.push_back(&#39;a&#39;)</code></td></tr><tr><td><code>empty()</code></td><td>是否为空</td><td><code>s.empty()</code></td></tr></tbody></table><p>常见坑：</p><ul><li><code>find</code> 找不到会返回 <code>string::npos</code>，不是 <code>-1</code>。</li><li><code>substr(pos, len)</code> 第二个参数是长度，不是结束下标。</li><li><code>char</code> 用单引号，<code>string</code> 用双引号。</li></ul><h3 id="16-3-unordered-map"><a href="#16-3-unordered-map" class="headerlink" title="16.3 unordered_map"></a>16.3 unordered_map</h3><table><thead><tr><th>方法</th><th>作用</th><th>常见用法</th></tr></thead><tbody><tr><td><code>mp[key]</code></td><td>访问或创建键</td><td><code>mp[x]++</code></td></tr><tr><td><code>count(key)</code></td><td>判断键是否存在</td><td><code>if (mp.count(x))</code></td></tr><tr><td><code>find(key)</code></td><td>查找迭代器</td><td><code>mp.find(x)</code></td></tr><tr><td><code>erase(key)</code></td><td>删除键</td><td><code>mp.erase(x)</code></td></tr><tr><td><code>size()</code></td><td>键值对数量</td><td><code>mp.size()</code></td></tr><tr><td><code>clear()</code></td><td>清空</td><td><code>mp.clear()</code></td></tr></tbody></table><p>常见坑：</p><ul><li><code>mp[key]</code> 如果 key 不存在，会自动创建默认值。</li><li>只想判断是否存在时，优先用 <code>count</code> 或 <code>find</code>。</li><li>哈希表平均 O(1)，但不是严格永远 O(1)。</li></ul><h3 id="16-4-algorithm"><a href="#16-4-algorithm" class="headerlink" title="16.4 algorithm"></a>16.4 algorithm</h3><table><thead><tr><th>函数</th><th>作用</th><th>常见用法</th></tr></thead><tbody><tr><td><code>sort</code></td><td>排序</td><td><code>sort(a.begin(), a.end())</code></td></tr><tr><td><code>reverse</code></td><td>反转</td><td><code>reverse(a.begin(), a.end())</code></td></tr><tr><td><code>lower_bound</code></td><td>找第一个大于等于目标的位置</td><td><code>lower_bound(a.begin(), a.end(), x)</code></td></tr><tr><td><code>upper_bound</code></td><td>找第一个大于目标的位置</td><td><code>upper_bound(a.begin(), a.end(), x)</code></td></tr><tr><td><code>unique</code></td><td>去除相邻重复，返回新末尾</td><td><code>a.erase(unique(a.begin(), a.end()), a.end())</code></td></tr><tr><td><code>max</code> &#x2F; <code>min</code></td><td>求较大&#x2F;较小值</td><td><code>max(a, b)</code></td></tr></tbody></table><p>常见坑：</p><ul><li><code>lower_bound</code> 要求区间已经有序。</li><li><code>unique</code> 只移动元素，不会真正缩短容器，通常要配合 <code>erase</code>。</li><li>自定义比较函数必须满足严格弱序，不能写成 <code>&lt;=</code>。</li></ul><hr><h2 id="17-推荐写法-vs-不推荐写法"><a href="#17-推荐写法-vs-不推荐写法" class="headerlink" title="17. 推荐写法 vs 不推荐写法"></a>17. 推荐写法 vs 不推荐写法</h2><table><thead><tr><th>场景</th><th>推荐写法</th><th>不推荐写法</th><th>原因</th></tr></thead><tbody><tr><td>刷题头文件</td><td><code>#include &lt;bits/stdc++.h&gt;</code></td><td>手动漏写头文件</td><td>刷题时更省事</td></tr><tr><td>工程头文件</td><td>按需 <code>#include &lt;vector&gt;</code></td><td><code>#include &lt;bits/stdc++.h&gt;</code></td><td><code>bits/stdc++.h</code> 非标准</td></tr><tr><td>动态数组</td><td><code>vector&lt;int&gt;</code></td><td>手写 <code>malloc</code> 动态数组</td><td>更安全，自动管理内存</td></tr><tr><td>字符串</td><td><code>string</code></td><td><code>char s[100]</code></td><td>更容易拼接、比较、截取</td></tr><tr><td>大对象传参</td><td><code>const vector&lt;int&gt;&amp;</code></td><td><code>vector&lt;int&gt;</code> 值传递</td><td>避免不必要复制</td></tr><tr><td>修改参数</td><td><code>int&amp; x</code></td><td><code>int* x</code> 加解引用</td><td>引用语义更清楚</td></tr><tr><td>工程命名空间</td><td><code>std::vector</code></td><td>全局 <code>using namespace std;</code></td><td>减少命名冲突</td></tr><tr><td>排序</td><td><code>sort + lambda</code></td><td><code>qsort + 函数指针</code></td><td>类型安全，可读性更好</td></tr></tbody></table><p>补充建议：</p><ul><li>刷算法时，优先掌握 STL 容器和算法库，比先深挖类和模板更划算。</li><li>工程代码里，越是公共头文件，越要避免 <code>using namespace std;</code>。</li><li>能用标准库解决的问题，不要急着手写底层结构，除非题目要求你实现它。</li></ul><hr><h2 id="18-常见编译错误速查"><a href="#18-常见编译错误速查" class="headerlink" title="18. 常见编译错误速查"></a>18. 常见编译错误速查</h2><h3 id="18-1-忘记-include"><a href="#18-1-忘记-include" class="headerlink" title="18.1 忘记 include"></a>18.1 忘记 include</h3><p>现象：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">error: &#x27;vector&#x27; was not declared in this scope<br></code></pre></td></tr></table></figure><p>原因：没有包含对应头文件，或者没有写 <code>std::</code>。</p><p>解决：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;vector&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br></code></pre></td></tr></table></figure><p>或：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">std::vector&lt;<span class="hljs-type">int</span>&gt; nums;<br></code></pre></td></tr></table></figure><h3 id="18-2-命名空间错误"><a href="#18-2-命名空间错误" class="headerlink" title="18.2 命名空间错误"></a>18.2 命名空间错误</h3><p>现象：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">error: &#x27;cout&#x27; was not declared in this scope<br></code></pre></td></tr></table></figure><p>解决：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp">std::cout &lt;&lt; <span class="hljs-string">&quot;hello\n&quot;</span>;<br></code></pre></td></tr></table></figure><p>或者在刷题代码里写：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br></code></pre></td></tr></table></figure><h3 id="18-3-vector-下标越界"><a href="#18-3-vector-下标越界" class="headerlink" title="18.3 vector 下标越界"></a>18.3 vector 下标越界</h3><p>错误写法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums;<br>nums[<span class="hljs-number">0</span>] = <span class="hljs-number">10</span>;<br></code></pre></td></tr></table></figure><p>原因：<code>nums</code> 还是空的，不能直接访问第 0 个元素。</p><p>正确写法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">vector&lt;<span class="hljs-type">int</span>&gt; nums;<br>nums.<span class="hljs-built_in">push_back</span>(<span class="hljs-number">10</span>);<br></code></pre></td></tr></table></figure><p>或：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">nums</span><span class="hljs-params">(<span class="hljs-number">1</span>)</span></span>;<br>nums[<span class="hljs-number">0</span>] = <span class="hljs-number">10</span>;<br></code></pre></td></tr></table></figure><h3 id="18-4-迭代器越界"><a href="#18-4-迭代器越界" class="headerlink" title="18.4 迭代器越界"></a>18.4 迭代器越界</h3><p>错误写法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">auto</span> it = nums.<span class="hljs-built_in">end</span>();<br>cout &lt;&lt; *it &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br></code></pre></td></tr></table></figure><p>原因：<code>end()</code> 指向最后一个元素的后一个位置，不能解引用。</p><p>正确理解：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-keyword">if</span> (!nums.<span class="hljs-built_in">empty</span>()) &#123;<br>    cout &lt;&lt; nums.<span class="hljs-built_in">back</span>() &lt;&lt; <span class="hljs-string">&#x27;\n&#x27;</span>;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="18-5-比较函数写错"><a href="#18-5-比较函数写错" class="headerlink" title="18.5 比较函数写错"></a>18.5 比较函数写错</h3><p>错误写法：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b) &#123;<br>    <span class="hljs-keyword">return</span> a &lt;= b;<br>&#125;);<br></code></pre></td></tr></table></figure><p>原因：比较函数不能用 <code>&lt;=</code>，应该表达“a 是否应该排在 b 前面”。</p><p>正确写法：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-built_in">sort</span>(nums.<span class="hljs-built_in">begin</span>(), nums.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b) &#123;<br>    <span class="hljs-keyword">return</span> a &lt; b;<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="18-6-引用和指针混淆"><a href="#18-6-引用和指针混淆" class="headerlink" title="18.6 引用和指针混淆"></a>18.6 引用和指针混淆</h3><p>指针写法：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">add_one</span><span class="hljs-params">(<span class="hljs-type">int</span>* x)</span> </span>&#123;<br>    (*x)++;<br>&#125;<br></code></pre></td></tr></table></figure><p>调用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">add_one</span>(&amp;num);<br></code></pre></td></tr></table></figure><p>引用写法：</p><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">add_one</span><span class="hljs-params">(<span class="hljs-type">int</span>&amp; x)</span> </span>&#123;<br>    x++;<br>&#125;<br></code></pre></td></tr></table></figure><p>调用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">add_one</span>(num);<br></code></pre></td></tr></table></figure><p>迁移建议：如果只是想让函数修改传入变量，C++ 里优先用引用；如果需要表达“可能为空”或动态内存地址，再考虑指针。</p><hr><h2 id="19-每章练习题补充"><a href="#19-每章练习题补充" class="headerlink" title="19. 每章练习题补充"></a>19. 每章练习题补充</h2><h3 id="19-1-基础输入输出"><a href="#19-1-基础输入输出" class="headerlink" title="19.1 基础输入输出"></a>19.1 基础输入输出</h3><ol><li>输入两个整数，输出它们的和、差、积。</li><li>输入一个字符串，输出它的长度和第一个字符。</li><li>输入一行带空格的文本，统计其中字符总数。</li></ol><h3 id="19-2-vector-和数组"><a href="#19-2-vector-和数组" class="headerlink" title="19.2 vector 和数组"></a>19.2 vector 和数组</h3><ol><li>输入 <code>n</code> 个整数，存入 <code>vector&lt;int&gt;</code>，输出最大值和最小值。</li><li>输入 <code>n</code> 个整数，删除所有偶数，只保留奇数。</li><li>输入 <code>n</code> 个分数，计算平均分。</li></ol><h3 id="19-3-string"><a href="#19-3-string" class="headerlink" title="19.3 string"></a>19.3 string</h3><ol><li>输入一个字符串，判断是否包含 <code>&quot;cpp&quot;</code>。</li><li>输入一个字符串，反转后输出。</li><li>输入一个英文句子，把所有空格替换成 <code>-</code>。</li></ol><h3 id="19-4-unordered-map-unordered-set"><a href="#19-4-unordered-map-unordered-set" class="headerlink" title="19.4 unordered_map &#x2F; unordered_set"></a>19.4 unordered_map &#x2F; unordered_set</h3><ol><li>输入一组数字，统计每个数字出现次数。</li><li>判断数组中是否存在重复元素。</li><li>写一个 Two Sum：返回两个数的下标，使它们的和等于目标值。</li></ol><h3 id="19-5-algorithm"><a href="#19-5-algorithm" class="headerlink" title="19.5 algorithm"></a>19.5 algorithm</h3><ol><li>输入 <code>n</code> 个数，升序排序后输出。</li><li>输入 <code>n</code> 个数，去重后输出。</li><li>在有序数组中查找第一个大于等于目标值的位置。</li></ol><h3 id="19-6-struct-class"><a href="#19-6-struct-class" class="headerlink" title="19.6 struct &#x2F; class"></a>19.6 struct &#x2F; class</h3><ol><li>定义 <code>Student</code>，包含 <code>name</code> 和 <code>score</code>，按分数从高到低排序。</li><li>定义 <code>Point</code>，写一个函数计算它到原点的距离。</li><li>定义一个简单 <code>Counter</code> 类，支持 <code>add()</code> 和 <code>value()</code>。</li></ol><h3 id="19-7-模板"><a href="#19-7-模板" class="headerlink" title="19.7 模板"></a>19.7 模板</h3><ol><li>写一个模板函数 <code>my_max(a, b)</code>，返回较大值。</li><li>写一个模板函数 <code>print_vector</code>，能输出 <code>vector&lt;int&gt;</code> 和 <code>vector&lt;string&gt;</code>。</li><li>写一个模板结构 <code>Pair&lt;T&gt;</code>，保存两个同类型值。</li></ol><h3 id="19-8-内存与现代-C"><a href="#19-8-内存与现代-C" class="headerlink" title="19.8 内存与现代 C++"></a>19.8 内存与现代 C++</h3><ol><li>对比 <code>new/delete</code> 和局部对象自动释放的区别。</li><li>用 <code>unique_ptr&lt;int&gt;</code> 保存一个整数并输出。</li><li>写一个函数，说明为什么返回局部对象通常是安全的。</li></ol><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1778042546856" data-twikoo-path="article_1778042546856"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/05/05/c-su-tong-bi-ji/</id>
    <link href="https://aoiblog.top/2026/05/05/c-su-tong-bi-ji/"/>
    <published>2026-05-05T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>C++ 速通笔记</h2>
# C++ 速通学习笔记：从 C 到 C++ 语法迁移

<blockquote>
<p>适合读者：已经学完 C 语言，想快速掌握 C++ 常用语法，并能看懂、写出算法题里的 C++ 代码。</p>
<p>默认标准：C++17。</p>
<p>]]>
    </summary>
    <title>C++ 速通笔记</title>
    <updated>2026-05-05T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>视频文件结构与编码学习笔记</h2>> 本笔记基于 ChatGPT 分享对话《视频文件结构与编码》的内容整理，并补充了视频容器、编码、播放链路、色彩、码率控制和常见排查思路。<h2 id="1-先建立整体认识"><a href="#1-先建立整体认识" class="headerlink" title="1. 先建立整体认识"></a>1. 先建立整体认识</h2><p>一个常见的视频文件并不是“只有画面”的文件，而是一个被封装起来的多媒体包。它通常包含：</p><ul><li>视频流：压缩后的连续画面数据。</li><li>音频流：压缩后的声音数据。</li><li>字幕流：文字字幕或图像字幕。</li><li>元数据：标题、作者、创建时间、旋转角度、编码器信息、章节信息等。</li><li>索引和时间戳：帮助播放器定位、拖动进度、保持音画同步。</li></ul><p>可以把视频文件理解成：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">视频文件 = 容器格式 + 一个或多个编码后的媒体流 + 元数据 + 索引/时间信息<br></code></pre></td></tr></table></figure><p>例如一个 <code>movie.mp4</code> 可能是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs text">容器：MP4<br>视频编码：H.264<br>音频编码：AAC<br>字幕：mov_text 或外挂 .srt<br>元数据：时长、分辨率、旋转信息、编码器信息<br></code></pre></td></tr></table></figure><p>所以 <code>.mp4</code>、<code>.mkv</code>、<code>.avi</code> 这类后缀通常说明的是“容器格式”，不等于视频编码格式。</p><h2 id="2-容器格式：视频文件的包装盒"><a href="#2-容器格式：视频文件的包装盒" class="headerlink" title="2. 容器格式：视频文件的包装盒"></a>2. 容器格式：视频文件的包装盒</h2><p>容器负责把视频、音频、字幕、元数据等组织在一起。它解决的是“怎么装”的问题，而不是“怎么压缩”的问题。</p><h3 id="2-1-MP4"><a href="#2-1-MP4" class="headerlink" title="2.1 MP4"></a>2.1 MP4</h3><p>MP4 是最常见的容器之一，兼容性非常好。</p><p>常见搭配：</p><ul><li>视频：H.264、H.265、AV1</li><li>音频：AAC、ALAC、MP3</li><li>字幕：mov_text、外挂 SRT</li></ul><p>适合场景：</p><ul><li>手机播放</li><li>网页播放</li><li>上传视频平台</li><li>日常分享</li></ul><p>优点是兼容性好，缺点是对复杂字幕、多音轨、多附件的灵活性不如 MKV。</p><h3 id="2-2-MKV"><a href="#2-2-MKV" class="headerlink" title="2.2 MKV"></a>2.2 MKV</h3><p>MKV 是 Matroska 容器，功能很强，常见于高清电影、番剧、收藏资源。</p><p>特点：</p><ul><li>支持多视频流、多音频流、多字幕流。</li><li>支持章节、附件、字体文件。</li><li>对 ASS&#x2F;SSA 等复杂字幕支持较好。</li><li>可以封装很多种编码格式。</li></ul><p>适合场景：</p><ul><li>本地收藏</li><li>多语言音轨</li><li>多字幕版本</li><li>高规格影视资源</li></ul><h3 id="2-3-MOV"><a href="#2-3-MOV" class="headerlink" title="2.3 MOV"></a>2.3 MOV</h3><p>MOV 是 Apple QuickTime 体系下常见的容器，和 MP4 关系很近。</p><p>常见场景：</p><ul><li>iPhone、相机素材</li><li>剪辑软件工作流</li><li>ProRes 等中间格式素材</li></ul><h3 id="2-4-AVI"><a href="#2-4-AVI" class="headerlink" title="2.4 AVI"></a>2.4 AVI</h3><p>AVI 是较老的容器格式。它历史悠久，但对现代编码、字幕、可变帧率、多轨道等支持不如新容器。</p><p>现在如果没有特殊兼容需求，一般不优先选择 AVI。</p><h3 id="2-5-WebM"><a href="#2-5-WebM" class="headerlink" title="2.5 WebM"></a>2.5 WebM</h3><p>WebM 面向网页和开放生态，常见搭配是 VP8、VP9、AV1 视频编码，以及 Opus、Vorbis 音频编码。</p><p>适合场景：</p><ul><li>浏览器播放</li><li>开放 Web 视频</li><li>动图替代类短视频</li></ul><h2 id="3-编码格式：视频压缩的方法"><a href="#3-编码格式：视频压缩的方法" class="headerlink" title="3. 编码格式：视频压缩的方法"></a>3. 编码格式：视频压缩的方法</h2><p>编码格式解决的是“怎么压缩”的问题。</p><p>原始视频数据非常大。以 1920x1080、8bit、RGB、30fps 为例，未压缩数据量大致是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">1920 x 1080 x 3 字节 x 30 帧 ≈ 178 MB/s<br></code></pre></td></tr></table></figure><p>一分钟就可能超过 10GB。因此日常视频几乎都需要压缩编码。</p><h3 id="3-1-H-264-AVC"><a href="#3-1-H-264-AVC" class="headerlink" title="3.1 H.264 &#x2F; AVC"></a>3.1 H.264 &#x2F; AVC</h3><p>H.264 是目前兼容性最强的视频编码之一。</p><p>特点：</p><ul><li>兼容性极高。</li><li>硬件解码支持普遍。</li><li>编码速度、压缩率、画质之间平衡很好。</li><li>适合直播、录屏、上传、移动端播放。</li></ul><p>如果不知道怎么选，通常可以优先选择：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">MP4 + H.264 + AAC<br></code></pre></td></tr></table></figure><p>这是最稳的通用组合。</p><h3 id="3-2-H-265-HEVC"><a href="#3-2-H-265-HEVC" class="headerlink" title="3.2 H.265 &#x2F; HEVC"></a>3.2 H.265 &#x2F; HEVC</h3><p>H.265 比 H.264 有更高压缩效率，常见于 4K、HDR、手机高效视频格式。</p><p>特点：</p><ul><li>同等画质下通常比 H.264 更省空间。</li><li>适合 4K、高码率、HDR。</li><li>编码和解码成本更高。</li><li>老设备、部分浏览器兼容性不如 H.264。</li></ul><p>需要注意：HEVC 的生态中还可能涉及授权和平台支持问题。</p><h3 id="3-3-AV1"><a href="#3-3-AV1" class="headerlink" title="3.3 AV1"></a>3.3 AV1</h3><p>AV1 是较新的开放视频编码，压缩效率优秀，越来越多流媒体平台开始使用。</p><p>特点：</p><ul><li>压缩效率很高。</li><li>适合在线视频、流媒体分发。</li><li>开放免版税方向更友好。</li><li>软件编码通常较慢。</li><li>老设备硬解支持不足。</li></ul><p>AV1 很适合平台级分发，但如果目标用户设备较旧，要谨慎选择。</p><h3 id="3-4-VP9"><a href="#3-4-VP9" class="headerlink" title="3.4 VP9"></a>3.4 VP9</h3><p>VP9 由 Google 推广，在 YouTube 和 Web 视频中比较常见。</p><p>特点：</p><ul><li>压缩效率优于 H.264。</li><li>浏览器支持较好。</li><li>常和 WebM 容器搭配。</li><li>设备兼容性总体不如 H.264。</li></ul><h3 id="3-5-ProRes-DNxHD-DNxHR"><a href="#3-5-ProRes-DNxHD-DNxHR" class="headerlink" title="3.5 ProRes &#x2F; DNxHD &#x2F; DNxHR"></a>3.5 ProRes &#x2F; DNxHD &#x2F; DNxHR</h3><p>这些不是面向最终发布的小体积编码，而是常见的剪辑中间格式。</p><p>特点：</p><ul><li>体积大。</li><li>解码压力相对低。</li><li>保留更多画面信息。</li><li>更适合剪辑、调色、后期制作。</li></ul><p>如果素材要反复剪辑，不一定应该用 H.264&#x2F;H.265 这种高压缩交付格式。</p><h2 id="4-音频编码"><a href="#4-音频编码" class="headerlink" title="4. 音频编码"></a>4. 音频编码</h2><p>视频文件里通常也包含音频流。常见音频编码包括：</p><ul><li>AAC：MP4 中最常见，兼容性好，压缩效率不错。</li><li>MP3：老牌有损音频格式，兼容性强。</li><li>Opus：低码率表现优秀，常见于 WebRTC、网络语音、WebM。</li><li>AC-3 &#x2F; Dolby Digital：家庭影院、多声道常见。</li><li>DTS：影视资源中常见的多声道音频格式。</li><li>FLAC：无损音频，体积较大。</li><li>ALAC：Apple 生态中的无损音频格式。</li></ul><p>日常分享视频时，<code>AAC</code> 通常是最稳的音频选择。</p><h2 id="5-视频压缩到底压缩了什么"><a href="#5-视频压缩到底压缩了什么" class="headerlink" title="5. 视频压缩到底压缩了什么"></a>5. 视频压缩到底压缩了什么</h2><p>视频压缩主要利用两类冗余：</p><h3 id="5-1-空间冗余"><a href="#5-1-空间冗余" class="headerlink" title="5.1 空间冗余"></a>5.1 空间冗余</h3><p>一张图像中，相邻像素通常很相似。例如蓝天、墙面、桌面等区域，很多像素颜色接近，不需要逐像素完整保存。</p><p>编码器会把画面分块，利用变换、量化、预测等方法减少数据量。</p><h3 id="5-2-时间冗余"><a href="#5-2-时间冗余" class="headerlink" title="5.2 时间冗余"></a>5.2 时间冗余</h3><p>连续视频帧之间通常变化不大。例如一个人站着说话，背景基本不动，只有嘴和身体有局部变化。</p><p>编码器会利用帧间预测，只保存“变化”而不是每一帧完整画面。</p><h2 id="6-I-帧、P-帧、B-帧与-GOP"><a href="#6-I-帧、P-帧、B-帧与-GOP" class="headerlink" title="6. I 帧、P 帧、B 帧与 GOP"></a>6. I 帧、P 帧、B 帧与 GOP</h2><p>视频编码不是每一帧都独立保存。常见帧类型包括：</p><ul><li>I 帧：关键帧，基本可以独立解码。</li><li>P 帧：参考之前的帧，只保存预测差异。</li><li>B 帧：可以参考前后帧，压缩效率更高，但解码和排序更复杂。</li></ul><p>GOP 是 Group of Pictures，即一组图像。一个典型结构可能是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">I B B P B B P B B P ...<br></code></pre></td></tr></table></figure><p>GOP 会影响：</p><ul><li>文件体积</li><li>压缩效率</li><li>拖动响应</li><li>剪辑方便程度</li><li>直播延迟</li></ul><p>一般来说：</p><ul><li>GOP 长：压缩效率高，体积更小，但拖动和剪辑不方便。</li><li>GOP 短：更容易定位和编辑，但体积更大。</li></ul><p>如果要做低延迟直播，通常不希望 GOP 太长，也会减少或禁用 B 帧。</p><h2 id="7-分辨率、帧率、码率"><a href="#7-分辨率、帧率、码率" class="headerlink" title="7. 分辨率、帧率、码率"></a>7. 分辨率、帧率、码率</h2><p>这三个概念经常被混在一起，但它们影响的是不同方面。</p><h3 id="7-1-分辨率"><a href="#7-1-分辨率" class="headerlink" title="7.1 分辨率"></a>7.1 分辨率</h3><p>分辨率表示画面有多少像素。</p><p>常见例子：</p><ul><li>1280x720：720p</li><li>1920x1080：1080p</li><li>2560x1440：2K &#x2F; QHD</li><li>3840x2160：4K UHD</li></ul><p>分辨率影响画面细节上限，但不直接等于画质。低码率的 4K 可能比高质量 1080p 更糊。</p><h3 id="7-2-帧率"><a href="#7-2-帧率" class="headerlink" title="7.2 帧率"></a>7.2 帧率</h3><p>帧率表示每秒显示多少帧。</p><p>常见例子：</p><ul><li>24fps：电影常见。</li><li>25fps：PAL 电视制式相关地区常见。</li><li>30fps：普通网络视频常见。</li><li>50&#x2F;60fps：运动、游戏、直播常见。</li><li>120fps 或更高：慢动作、高刷新素材。</li></ul><p>帧率主要影响运动流畅度。帧率越高，通常需要更高码率来维持画质。</p><h3 id="7-3-码率"><a href="#7-3-码率" class="headerlink" title="7.3 码率"></a>7.3 码率</h3><p>码率表示单位时间使用多少数据，常见单位是 Mbps。</p><p>例如：</p><ul><li>2 Mbps：普通 720p 或低码率 1080p。</li><li>8 Mbps：较常见的 1080p 网络视频。</li><li>25 Mbps：较高质量 4K 或高规格素材。</li></ul><p>码率直接影响压缩强度和体积。同样编码下，码率越高，通常画质越好、文件越大。</p><p>大致关系：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">文件大小 ≈ 平均总码率 x 时长<br></code></pre></td></tr></table></figure><p>例如一个 10 分钟、平均总码率 8 Mbps 的视频：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">8 Mbps x 600 秒 / 8 ≈ 600 MB<br></code></pre></td></tr></table></figure><p>这里除以 8 是因为 8 bit &#x3D; 1 byte。</p><h2 id="8-CBR、VBR、CRF"><a href="#8-CBR、VBR、CRF" class="headerlink" title="8. CBR、VBR、CRF"></a>8. CBR、VBR、CRF</h2><p>码率控制方式会影响画质和体积。</p><h3 id="8-1-CBR"><a href="#8-1-CBR" class="headerlink" title="8.1 CBR"></a>8.1 CBR</h3><p>CBR 是 Constant Bitrate，恒定码率。</p><p>特点：</p><ul><li>码率稳定。</li><li>适合直播、带宽固定的传输场景。</li><li>复杂画面可能不够用，简单画面可能浪费码率。</li></ul><h3 id="8-2-VBR"><a href="#8-2-VBR" class="headerlink" title="8.2 VBR"></a>8.2 VBR</h3><p>VBR 是 Variable Bitrate，可变码率。</p><p>特点：</p><ul><li>简单画面少分配码率，复杂画面多分配码率。</li><li>同等体积下通常比 CBR 更合理。</li><li>适合离线压制和点播。</li></ul><h3 id="8-3-CRF"><a href="#8-3-CRF" class="headerlink" title="8.3 CRF"></a>8.3 CRF</h3><p>CRF 是常见于 x264&#x2F;x265 的恒定质量模式。</p><p>特点：</p><ul><li>目标是保持主观质量相对稳定。</li><li>文件大小不固定。</li><li>CRF 数值越低，质量越高，体积越大。</li></ul><p>常见经验：</p><ul><li>x264 的 CRF 18 到 23 常用于较高质量压制。</li><li>x265 的 CRF 数值不能和 x264 简单等价。</li><li>具体取值要结合内容、分辨率、编码器预设测试。</li></ul><h2 id="9-像素格式、色彩采样与位深"><a href="#9-像素格式、色彩采样与位深" class="headerlink" title="9. 像素格式、色彩采样与位深"></a>9. 像素格式、色彩采样与位深</h2><h3 id="9-1-RGB-与-YUV"><a href="#9-1-RGB-与-YUV" class="headerlink" title="9.1 RGB 与 YUV"></a>9.1 RGB 与 YUV</h3><p>显示设备常用 RGB 表示颜色，但视频编码中更常见的是 YUV 或 YCbCr。</p><p>其中：</p><ul><li>Y 表示亮度。</li><li>U&#x2F;V 或 Cb&#x2F;Cr 表示色度。</li></ul><p>人眼对亮度细节更敏感，对颜色细节相对不那么敏感，所以视频压缩常减少色度信息。</p><h3 id="9-2-色彩采样"><a href="#9-2-色彩采样" class="headerlink" title="9.2 色彩采样"></a>9.2 色彩采样</h3><p>常见色彩采样格式：</p><ul><li>4:4:4：亮度和色度信息都较完整，适合高质量制作。</li><li>4:2:2：专业视频、广播、采集卡素材中常见。</li><li>4:2:0：消费级视频、网络视频最常见。</li></ul><p>大多数普通 MP4 视频都是 4:2:0。</p><h3 id="9-3-位深"><a href="#9-3-位深" class="headerlink" title="9.3 位深"></a>9.3 位深</h3><p>位深表示每个颜色通道能表示多少层级。</p><p>常见位深：</p><ul><li>8bit：每通道 256 级，普通 SDR 视频常见。</li><li>10bit：每通道 1024 级，HDR 和高质量压制常见。</li><li>12bit：更专业的制作和母版场景。</li></ul><p>位深越高，渐变越平滑，越不容易出现色带，但体积和解码要求也更高。</p><h3 id="9-4-SDR-与-HDR"><a href="#9-4-SDR-与-HDR" class="headerlink" title="9.4 SDR 与 HDR"></a>9.4 SDR 与 HDR</h3><p>SDR 是普通动态范围，HDR 是高动态范围。</p><p>HDR 通常涉及：</p><ul><li>更高亮度范围。</li><li>更广色域，如 BT.2020。</li><li>10bit 或更高位深。</li><li>PQ 或 HLG 转换曲线。</li><li>HDR10、HDR10+、Dolby Vision 等元数据体系。</li></ul><p>如果 HDR 元数据、色域标记或播放设备支持有问题，可能出现画面发灰、过曝、颜色不对等现象。</p><h2 id="10-时间戳、索引与音画同步"><a href="#10-时间戳、索引与音画同步" class="headerlink" title="10. 时间戳、索引与音画同步"></a>10. 时间戳、索引与音画同步</h2><p>播放器播放视频时，不只是顺序读文件，还需要理解时间信息。</p><p>重要概念：</p><ul><li>DTS：Decoding Time Stamp，解码时间戳。</li><li>PTS：Presentation Time Stamp，显示时间戳。</li><li>time base：时间单位基准。</li><li>index：索引，帮助快速定位。</li></ul><p>因为 B 帧可能参考未来帧，所以“解码顺序”和“显示顺序”可能不同。播放器需要根据 PTS&#x2F;DTS 正确安排解码与显示。</p><p>如果时间戳或索引异常，可能出现：</p><ul><li>音画不同步。</li><li>拖动进度条不准确。</li><li>某些播放器能播，某些播放器不能播。</li><li>视频开头黑屏或卡顿。</li><li>合并、剪辑后时长显示异常。</li></ul><h2 id="11-MP4-文件结构简要理解"><a href="#11-MP4-文件结构简要理解" class="headerlink" title="11. MP4 文件结构简要理解"></a>11. MP4 文件结构简要理解</h2><p>MP4 内部由一系列 box，也叫 atom，组成。每个 box 通常有大小、类型和内容。</p><p>常见 box：</p><ul><li><code>ftyp</code>：文件类型和兼容品牌信息。</li><li><code>moov</code>：媒体元数据，包含轨道、时间、索引等关键信息。</li><li><code>mdat</code>：实际媒体数据。</li><li><code>free</code>：预留或填充空间。</li></ul><p>一个普通 MP4 的抽象结构可能是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><code class="hljs text">[ftyp]<br>[moov]<br>  [mvhd]<br>  [trak]<br>    [tkhd]<br>    [mdia]<br>      [mdhd]<br>      [hdlr]<br>      [minf]<br>        [stbl]<br>[mdat]<br></code></pre></td></tr></table></figure><p>其中：</p><ul><li><code>moov</code> 像目录和说明书。</li><li><code>mdat</code> 像真正的音视频数据仓库。</li><li><code>stbl</code> 里保存 sample table，用于记录样本位置、时长、关键帧等。</li></ul><h3 id="11-1-为什么有些-MP4-要“fast-start”"><a href="#11-1-为什么有些-MP4-要“fast-start”" class="headerlink" title="11.1 为什么有些 MP4 要“fast start”"></a>11.1 为什么有些 MP4 要“fast start”</h3><p>如果 <code>moov</code> 在文件末尾，播放器可能要先下载完整文件或跳到末尾读取元数据，网页播放启动会变慢。</p><p>所谓 fast start 通常是把 <code>moov</code> 移到文件开头，让播放器更快获得索引和时长信息。</p><p>用 FFmpeg 常见命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4<br></code></pre></td></tr></table></figure><p>这里 <code>-c copy</code> 表示不重新编码，只重新封装。</p><h2 id="12-播放一个视频时发生了什么"><a href="#12-播放一个视频时发生了什么" class="headerlink" title="12. 播放一个视频时发生了什么"></a>12. 播放一个视频时发生了什么</h2><p>播放链路大致如下：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs text">读取文件<br>  -&gt; 解析容器<br>  -&gt; 解复用 demux<br>  -&gt; 得到视频流、音频流、字幕流<br>  -&gt; 解码 decode<br>  -&gt; 视频帧送到渲染器<br>  -&gt; 音频帧送到音频设备<br>  -&gt; 根据时间戳做同步<br></code></pre></td></tr></table></figure><p>关键步骤：</p><ul><li>demux：从容器里拆出各条流。</li><li>decode：把压缩数据还原成可显示&#x2F;可播放的数据。</li><li>render：把画面显示到屏幕，把声音送到扬声器。</li><li>sync：根据时间戳维持音画同步。</li></ul><h2 id="13-软解与硬解"><a href="#13-软解与硬解" class="headerlink" title="13. 软解与硬解"></a>13. 软解与硬解</h2><h3 id="13-1-软解"><a href="#13-1-软解" class="headerlink" title="13.1 软解"></a>13.1 软解</h3><p>软解是用 CPU 解码。</p><p>优点：</p><ul><li>灵活。</li><li>支持格式更多。</li><li>便于调试和兼容特殊编码。</li></ul><p>缺点：</p><ul><li>占用 CPU。</li><li>高分辨率、高码率、新编码可能卡顿。</li></ul><h3 id="13-2-硬解"><a href="#13-2-硬解" class="headerlink" title="13.2 硬解"></a>13.2 硬解</h3><p>硬解是用 GPU、SoC 或专门的视频解码模块。</p><p>优点：</p><ul><li>省电。</li><li>性能好。</li><li>对移动设备很重要。</li></ul><p>缺点：</p><ul><li>取决于硬件是否支持对应编码、profile、level、位深、色彩采样。</li></ul><p>例如某设备可能支持 H.264 8bit 4:2:0 硬解，但不支持 H.265 10bit 或 AV1 硬解。</p><h2 id="14-为什么同样-1080p，文件大小和画质差很多"><a href="#14-为什么同样-1080p，文件大小和画质差很多" class="headerlink" title="14. 为什么同样 1080p，文件大小和画质差很多"></a>14. 为什么同样 1080p，文件大小和画质差很多</h2><p>影响因素很多，不只有分辨率：</p><ul><li>编码格式不同：H.264、H.265、AV1 压缩效率不同。</li><li>码率不同：码率越低越容易糊、块状、涂抹。</li><li>帧率不同：60fps 通常比 30fps 需要更多码率。</li><li>画面复杂度不同：运动、噪点、草地、水面、烟雾都很吃码率。</li><li>编码器不同：x264、x265、SVT-AV1、硬件编码器效果不同。</li><li>编码预设不同：慢速预设通常压缩效率更好。</li><li>音频轨道不同：多音轨、无损音频会显著增大体积。</li><li>字幕和附件不同：字体附件、图像字幕也会增加体积。</li></ul><p>所以两个 1080p 视频，一个 2GB，一个 12GB，都可能是合理的。</p><h2 id="15-常见封装、转码、转封装"><a href="#15-常见封装、转码、转封装" class="headerlink" title="15. 常见封装、转码、转封装"></a>15. 常见封装、转码、转封装</h2><h3 id="15-1-转封装"><a href="#15-1-转封装" class="headerlink" title="15.1 转封装"></a>15.1 转封装</h3><p>转封装是只改变容器，不改变音视频编码。</p><p>例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">MKV(H.264 + AAC) -&gt; MP4(H.264 + AAC)<br></code></pre></td></tr></table></figure><p>FFmpeg 示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffmpeg -i input.mkv -c copy output.mp4<br></code></pre></td></tr></table></figure><p>优点：</p><ul><li>速度快。</li><li>几乎不损失画质。</li></ul><p>前提：</p><ul><li>目标容器支持原来的编码格式。</li></ul><h3 id="15-2-转码"><a href="#15-2-转码" class="headerlink" title="15.2 转码"></a>15.2 转码</h3><p>转码会重新编码视频或音频。</p><p>例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">H.265 -&gt; H.264<br>FLAC -&gt; AAC<br></code></pre></td></tr></table></figure><p>FFmpeg 示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffmpeg -i input.mkv -c:v libx264 -crf 20 -c:a aac -b:a 192k output.mp4<br></code></pre></td></tr></table></figure><p>特点：</p><ul><li>耗时更长。</li><li>有损转有损通常会产生二次损失。</li><li>可以提升兼容性或减小体积。</li></ul><h3 id="15-3-不要把改后缀当成转换"><a href="#15-3-不要把改后缀当成转换" class="headerlink" title="15.3 不要把改后缀当成转换"></a>15.3 不要把改后缀当成转换</h3><p>把 <code>video.mkv</code> 直接重命名成 <code>video.mp4</code> 不会改变内部结构，也不会提升兼容性。真正转换需要重新封装或转码。</p><h2 id="16-常见场景选择建议"><a href="#16-常见场景选择建议" class="headerlink" title="16. 常见场景选择建议"></a>16. 常见场景选择建议</h2><h3 id="16-1-日常分享和最大兼容"><a href="#16-1-日常分享和最大兼容" class="headerlink" title="16.1 日常分享和最大兼容"></a>16.1 日常分享和最大兼容</h3><p>推荐：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">MP4 + H.264 + AAC<br></code></pre></td></tr></table></figure><p>理由：</p><ul><li>手机、电脑、浏览器、电视兼容性都很好。</li><li>上传平台也普遍接受。</li></ul><h3 id="16-2-本地高清收藏"><a href="#16-2-本地高清收藏" class="headerlink" title="16.2 本地高清收藏"></a>16.2 本地高清收藏</h3><p>推荐：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">MKV + H.265/H.264 + AAC/AC-3/DTS + 多字幕<br></code></pre></td></tr></table></figure><p>理由：</p><ul><li>MKV 对多音轨、多字幕、章节、字体附件支持更灵活。</li></ul><h3 id="16-3-网页播放"><a href="#16-3-网页播放" class="headerlink" title="16.3 网页播放"></a>16.3 网页播放</h3><p>常见选择：</p><ul><li>MP4 + H.264 + AAC：兼容性优先。</li><li>WebM + VP9&#x2F;AV1 + Opus：开放 Web 和节省带宽。</li><li>HLS&#x2F;DASH：长视频、直播、码率自适应。</li></ul><h3 id="16-4-剪辑和后期"><a href="#16-4-剪辑和后期" class="headerlink" title="16.4 剪辑和后期"></a>16.4 剪辑和后期</h3><p>推荐考虑：</p><ul><li>ProRes</li><li>DNxHD &#x2F; DNxHR</li><li>高码率 intra-frame 编码</li></ul><p>理由：</p><ul><li>更适合反复剪辑。</li><li>解码压力小。</li><li>时间线操作更流畅。</li></ul><h3 id="16-5-直播和低延迟"><a href="#16-5-直播和低延迟" class="headerlink" title="16.5 直播和低延迟"></a>16.5 直播和低延迟</h3><p>常见考虑：</p><ul><li>H.264 仍然非常常见。</li><li>GOP 不宜过长。</li><li>B 帧可能增加延迟。</li><li>CBR 或受控 VBR 更方便带宽管理。</li></ul><h2 id="17-用-FFmpeg-ffprobe-查看视频信息"><a href="#17-用-FFmpeg-ffprobe-查看视频信息" class="headerlink" title="17. 用 FFmpeg &#x2F; ffprobe 查看视频信息"></a>17. 用 FFmpeg &#x2F; ffprobe 查看视频信息</h2><p>查看基本信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffprobe input.mp4<br></code></pre></td></tr></table></figure><p>查看流信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffprobe -hide_banner -show_streams input.mp4<br></code></pre></td></tr></table></figure><p>查看容器格式信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffprobe -hide_banner -show_format input.mp4<br></code></pre></td></tr></table></figure><p>常关注字段：</p><ul><li><code>codec_name</code>：编码格式。</li><li><code>codec_type</code>：流类型，如 video、audio、subtitle。</li><li><code>width</code> &#x2F; <code>height</code>：分辨率。</li><li><code>pix_fmt</code>：像素格式，如 yuv420p、yuv420p10le。</li><li><code>r_frame_rate</code>：帧率相关信息。</li><li><code>bit_rate</code>：码率。</li><li><code>duration</code>：时长。</li><li><code>profile</code> &#x2F; <code>level</code>：编码规格。</li></ul><h2 id="18-常见问题排查"><a href="#18-常见问题排查" class="headerlink" title="18. 常见问题排查"></a>18. 常见问题排查</h2><h3 id="18-1-视频能播放但不能拖动"><a href="#18-1-视频能播放但不能拖动" class="headerlink" title="18.1 视频能播放但不能拖动"></a>18.1 视频能播放但不能拖动</h3><p>可能原因：</p><ul><li>索引损坏或缺失。</li><li><code>moov</code> 在文件末尾，网络播放体验差。</li><li>时间戳异常。</li></ul><p>可尝试：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4<br></code></pre></td></tr></table></figure><h3 id="18-2-有画面没声音"><a href="#18-2-有画面没声音" class="headerlink" title="18.2 有画面没声音"></a>18.2 有画面没声音</h3><p>可能原因：</p><ul><li>播放器不支持音频编码。</li><li>音轨被静音或选错。</li><li>音频流损坏。</li></ul><p>排查：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">ffprobe -hide_banner -show_streams input.mp4<br></code></pre></td></tr></table></figure><p>看是否存在 <code>codec_type=audio</code>。</p><h3 id="18-3-有声音没画面"><a href="#18-3-有声音没画面" class="headerlink" title="18.3 有声音没画面"></a>18.3 有声音没画面</h3><p>可能原因：</p><ul><li>视频编码不被设备支持。</li><li>硬解失败。</li><li>视频流损坏。</li><li>profile、level、位深或色彩采样超出设备能力。</li></ul><p>解决方向：</p><ul><li>换播放器。</li><li>关闭硬解尝试软解。</li><li>转码为 H.264 8bit 4:2:0。</li></ul><h3 id="18-4-音画不同步"><a href="#18-4-音画不同步" class="headerlink" title="18.4 音画不同步"></a>18.4 音画不同步</h3><p>可能原因：</p><ul><li>时间戳异常。</li><li>可变帧率处理不当。</li><li>剪辑或合并时没有正确处理音频偏移。</li><li>播放器兼容问题。</li></ul><p>解决方向：</p><ul><li>重新封装。</li><li>转为恒定帧率。</li><li>使用音频延迟参数修正。</li></ul><h3 id="18-5-颜色发灰或过曝"><a href="#18-5-颜色发灰或过曝" class="headerlink" title="18.5 颜色发灰或过曝"></a>18.5 颜色发灰或过曝</h3><p>可能原因：</p><ul><li>HDR 被当 SDR 播放。</li><li>色域标记错误。</li><li>full range &#x2F; limited range 处理不一致。</li><li>播放器或系统色彩管理问题。</li></ul><p>需要检查：</p><ul><li>色域：BT.709、BT.2020。</li><li>转换曲线：SDR gamma、PQ、HLG。</li><li>位深：8bit、10bit。</li><li>range：limited &#x2F; full。</li></ul><h2 id="19-最重要的记忆卡片"><a href="#19-最重要的记忆卡片" class="headerlink" title="19. 最重要的记忆卡片"></a>19. 最重要的记忆卡片</h2><h3 id="19-1-容器和编码不要混淆"><a href="#19-1-容器和编码不要混淆" class="headerlink" title="19.1 容器和编码不要混淆"></a>19.1 容器和编码不要混淆</h3><figure class="highlight text"><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><code class="hljs text">MP4、MKV、MOV、AVI、WebM 是容器。<br>H.264、H.265、AV1、VP9 是视频编码。<br>AAC、Opus、MP3、FLAC 是音频编码。<br></code></pre></td></tr></table></figure><h3 id="19-2-分辨率不等于画质"><a href="#19-2-分辨率不等于画质" class="headerlink" title="19.2 分辨率不等于画质"></a>19.2 分辨率不等于画质</h3><p>画质受分辨率、码率、编码效率、源素材质量、色彩、帧率、编码器参数共同影响。</p><h3 id="19-3-兼容性优先的默认选择"><a href="#19-3-兼容性优先的默认选择" class="headerlink" title="19.3 兼容性优先的默认选择"></a>19.3 兼容性优先的默认选择</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">MP4 + H.264 + AAC + yuv420p<br></code></pre></td></tr></table></figure><h3 id="19-4-收藏和多轨优先的默认选择"><a href="#19-4-收藏和多轨优先的默认选择" class="headerlink" title="19.4 收藏和多轨优先的默认选择"></a>19.4 收藏和多轨优先的默认选择</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">MKV + 高效视频编码 + 多音轨 + 多字幕<br></code></pre></td></tr></table></figure><h3 id="19-5-交付格式不一定适合剪辑"><a href="#19-5-交付格式不一定适合剪辑" class="headerlink" title="19.5 交付格式不一定适合剪辑"></a>19.5 交付格式不一定适合剪辑</h3><p>H.264&#x2F;H.265 很适合发布，但不一定适合后期剪辑。剪辑中间格式通常体积更大，但编辑体验更好。</p><h2 id="20-一句话总结"><a href="#20-一句话总结" class="headerlink" title="20. 一句话总结"></a>20. 一句话总结</h2><p>视频文件的核心不是“一个后缀名”，而是容器、编码、码率、时间戳、色彩信息和播放设备共同作用的结果。学习视频文件时，先分清“容器负责装什么”“编码负责怎么压缩”“播放器负责怎么拆、解码和同步”，很多问题就能定位到正确层面。</p><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1777641068079" data-twikoo-path="article_1777641068079"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/30/shi-pin-wen-jian-jie-gou-yu-bian-ma-xue-xi-bi-ji/</id>
    <link href="https://aoiblog.top/2026/04/30/shi-pin-wen-jian-jie-gou-yu-bian-ma-xue-xi-bi-ji/"/>
    <published>2026-04-30T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>视频文件结构与编码学习笔记</h2>
> 本笔记基于 ChatGPT 分享对话《视频文件结构与编码》的内容整理，并补充了视频容器、编码、播放链路、色彩、码率控制和常见排查思路。

<h2 id="1-先建立整体认识"><a href="#1-先建立整体认识" class=]]>
    </summary>
    <title>视频文件结构与编码学习笔记</title>
    <updated>2026-04-30T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="随笔" scheme="https://aoiblog.top/categories/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<h2>一点思考与总结</h2>4月24日<p>本来已经累到不想再花费额外的时间和精力自己手搓文章，毕竟周日还要面对尚未复习的三门期中考试，但还是想逼着自己写点什么东西，以此来记录这段宝贵的实践经历。</p><p>为什么我会觉得这段经历很宝贵呢，也许是因为我一开始也没想到自己能找到这段实习吧。我正式决定转码也还是在今年2月底，我找到实习是在今年3月底，能找到这段实习，我觉得自己运气还是挺好的。</p><p>我想出来实习，其实主要是因为当时我自己觉得窝在学校里实在太无聊了，学校里的课程又教不了什么有用的东西，对找工作可以说是一点用都没有，再加上当时我身边的一些很厉害的人当时也在实习，所以我就也想出来找份实习，就当是小小地gap一下了。</p><p>事实证明，要想同时平衡实习和学业真挺累的，因为我后面还是打算保研，我学技术的同时还得保证我绩点不能丢，但我一周又要至少出勤四天，所以我周末也没办法休息太长时间，还得补这一周的课程。</p><p>我当时投简历的时候遇到了一个很有意思的leader，他的公司是一家做B2B SaaS的创业小公司，但他们的产品已经落地到茶百道这些国内头部的茶饮品牌里了，而且他们还和胖东来以及飞书等都有合作，我觉得真挺吊的。我把简历发给他，和他聊了几句，然后他线上拉我稍微面试了一下，简单聊了一下我之前用ai跑出来的我的个人博客，然后他就答应让我来实习了。</p><p>说实话我当时也挺懵逼的，我一个基础这么差的人，前端当时连js都没开始学，他怎么就答应让我来实习呢？</p><p>他说第一个是因为我有自己的github账号，还有这个博客，至少说明我对全栈以及git有一定的了解，也有一定的信息检索能力，并且也已经有能借助ai把项目落地上线的这个能力，第二个是因为他觉得技术能力也没有那么重要，他跟我说他当时面试的时候，他有个技术栈突击了两天，然后面试的时候过了，他说他一般会给我们这些实习生1-2周的时间去学需要的技术栈，前两周就让我们零薪实习体验一下，等到我们真正能开始开发的时候再给我们发工资。更重要的是看我们抗不抗压，因为他们那边实习生和正式员工是一样的，早上9点半上班，晚上9点下班，而且经常会出现需求没办法按时交付的情况。</p><p>他跟我说他们之前也招过一个大一的，但是那个大一的干了一段时间之后不知道是因为工作压力太大还是什么原因，情绪崩溃了还是怎么样，他当时也没和我细说，我也不好猜测。</p><p>其实这么看来我觉得自己也确实挺牛逼的，我这人可能没什么特别突出的优点，真要说的可能也就只有心理素质比较好了。</p><p>其实也都是吃亏吃出来的，我这人可能确实不太聪明，什么事情都必须先尝试一下，吃几次亏才能复盘复出来到底该怎么做。</p><p>我跟他说我反正无所谓，他就说可以，让我下周一直接来上班。</p><p>这其实也说明了当一个公司是真的缺人或者你的条件正好符合招聘要求的时候，面试压根就不会特别复杂，不会问你这项目怎么做的问你那细节怎么实现的，而就是会很简单，你符合要求是吧，行，那你直接来上班。</p><p>刚开始那两周，其实也没做什么实质上的事情，前几天试着写了下一个SOP的一小节，然后设计了个流程，剩下的时间基本上也就是在熟悉他们家的产品，然后自学一下js和mysql之类的技术栈。</p><p>不过我觉得他们家那个产品是真的太复杂了，我现在离职了都还没完全搞清楚他们家那个产品，可能这就是他们家产品的牛逼之处，也是那些头部茶饮品牌和那些大公司愿意为他们家产品付费的原因吧。</p><p>等到第三周我mentor真正开始给我派活之后，我就都是用ai给我做了，从写技术方案，到开发和自测，我都在使唤ai，因为我就算花了两周时间狂补技术栈，也还是没能补完，而且正好第二周的时候，我们leader不知道怎么搞的，从闲鱼上发现了一个gpt的代充店铺，价格每个月只要十几二十块，然后当晚他就专门开了个会教我们所有人怎么充gpt，有点难绷，不过每个月二十块的gpt plus账户资格真给我爽到了。</p><p>说实话，要想用好ai其实也是一门学问，现在我自己的代码能力不是很强，也没掌握多少编程语言，我辞掉这份实习的最大的原因也其实是我全程都在用ai给我干活，我自己做的事情大概也只有对齐和理解客户的需求，然后审查和规范ai的每一步行动，以及简单的测试和提MR。我觉得我在这段实习里技术上的收获就是，培养了每天有意识地获取行业前沿消息的习惯，如何深度地使用ai，怎么使唤ai给我做事情，以及怎么把ai落地到大型项目的维护和优化上。</p><p>现在很多人都有所谓的ai焦虑，我觉得这是因为很多人都没有亲自去体验和深度地使用ai，让ai成为能帮你做事的agent，而不只是单纯的把ai当做一个可以聊天的助手，或者只是不停地去拿所谓的洗车笑话去评价一个llm到底聪不聪明，只有当你让它给你写代码，给你修bug，帮你发邮件，给你抓取信息，帮你发消息，给你做ppt，编辑word文档，填excel表格，帮你做笔记，给你生成图和视频，让它使用CLI接入各种软件，比如说obsidian和飞书等，你才能知道ai在实际生活中的能力边界在哪里，知道有哪些方面是ai无法取代你的，知道哪些事情是只能由人来完成的，从而知道自己究竟该做些什么。</p><p>5月1日</p><p>结束实习之后的这几天时间里，都在补之前自己落下的课程，然后这几天也一直在打go，也算是给自己放几天假吧，因此也没什么时间继续写这篇随笔，直到今天才有一些时间继续往下写。</p><p>至于ai以后到底会不会取代人类，只能说谁也不知道吧，现在云服务方式已经更多地在向MaaS方式演变，可能今天你自己搞的一个产品或者是服务能够满足客户的需求，明天那些大模型厂商就推出了新的模型或者是给现有的模型增加了新的能力，直接把你这块业务给覆盖掉了，就像现在的claude mythos和gpt5.5，它们都已经在向网络安全和生物科学等方面进军了。</p><p>然而就如同我上面所说的，这些模型可以帮你写代码，帮你做事情，却还是会在“洗车问题”和“分橘子”这样的常识题上翻车，karpathy把这个现象描述为“锯齿形智能”，为什么这些模型在写代码和解决一些复杂的问题上能力如此之强，是因为这些领域是真正可验证和赚大钱的地方，御三家厂商才会在这些方面进行强化学习，而这些模型再怎么正确地回答用户平常问的一些问题，他们也赚不到钱，毕竟网页版都是随便用的，所以才会出现这种现象。</p><p>Karpathy近日在一次炉边谈话的活动中，回答了主持人的一个问题：当智能体能写代码，能调度，能自己装软件的时候，人类还剩什么？答案的话，我自己觉得karpathy说的已经够清楚了。</p><img src="/images/img_1777639323087.png" alt="图片" style="max-width: 100%;"><img src="/images/img_1777639339893.png" alt="图片" style="max-width: 100%;"><img src="/images/img_1777639346837.png" alt="图片" style="max-width: 100%;"><p>这些观点和openai之前内部的一次谈话里的一个观点也是一致的。也就是，真正在实际的生产和开发环境中，至少在目前，是肯定不能百分之百信任ai的，包括skill，CLAUDE.md和AGENTS.md，实际上也都是对agent的约束，你可以把事情和任务甩给ai做，但你必须告诉他什么事情不能做，有哪些细节和规范要注意，否则就会有安全隐患。</p><img src="/images/img_1777639354475.png" alt="图片" style="max-width: 100%;"><p>至于AI未来会发展成什么样，没人知道，就算是一个月之后，也没人敢轻易下定论，模型的能力之后肯定会越来越强，但正因如此，我觉得软件基础和程序员的基本功在之后也会越来越重要。能力越强的人，使用ai就会提高更多的效率，而一个毫无计算机知识和基础的人使用ai，他也许根本就不会使用ai，他只会不停地在一个session里对话和塞文件，遇到bug了也不知道该怎么查，当模型能力不足的时候只会不停地说“给我修复”来烧token，他也许会把密钥和隐私数据硬编码在前端模块里……</p><p>总之，我不认为ai会踏平所有技术的门槛，技术一定是会有护城河的，vibe coding只是抹去了从0到1的门槛，而不是从1到100的规模化和规范化，那些新闻里报道的使用ai创业成功的人，都是有计算机基础和必备知识的人，都是懂得开发规范和如何使用ai的人，而不是像新闻里所说的什么纯文科生和零计算机基础，我只能说这真的是新闻学魅力时刻了。ai说到底也只是一个能够提效的工具，它绝对无法代替你思考和做决策，它所能做的只是一些你本没必要做的dirty work，真正筛选、整合信息，并最终作出决策的步骤必须要由你自己来完成，它应该是帮你实现想法，而不是给你提出想法供你选择。</p><p>今天我去了WCA立夏魔方赛当裁判，回来的路上听见两个学生说，人生有四大痛苦：上学、上班、没学上和没班上，我想了想之后觉得其实挺对的，就是我之前动态里发过的那句话：假如一个人做的不是他所热爱的工作的话，那么他开心的时候大概也只有入职和辞职的那一天。</p><p>高考完之后，我做事其实要比以前感性多了，现在很多事情其实我都是我一拍大腿或者心血来潮想做，我就立刻开始评估这件事是否可行，只要我找到了那么一条可行的路径，那么我就会马上付出行动，而我在做这些事情的过程中，通常都会获得一些来自我自己的反馈，因此我也能更好地决定我之后的生活里该做些什么，这次实习也是如此，我觉得，我在这段实习里最大的收获其实是我在这个过程中认清了我目前的能力边界。</p><p>本来我想的是一边实习一边抓绩点的，但经过期中考试的检验之后，我发现这样并不可行。经过权衡之后，我还是决定先搞个研究生的学历，假如我打算本科就业，我肯定是没办法和那些初高中就接触计算机和打OI的比，毕竟我上大学之前也没有学过任何关于计算机的技术，而且我真正决定转码也是今年2月底，我打算进互联网行业，纯粹是因为我对这个行业非常感兴趣，但我也不是非要想做和代码强相关的工作，我觉得互联网PM其实也挺好的。就算进不了互联网行业也不要紧，我还可以尝试搞搞自媒体，实在不行的话，我就去学门剪头发的手艺，开个理发店，没事的时候写写文章或者小说。</p><p>说实话，假如不考虑工资等现实因素的话，我最想做的，其实是一名会写代码和文章的理发师。我也不奢求以后能进大厂或者创业什么的，我只要能有一份工作，能养活自己，能有空余时间做点自己喜欢的事情，这就足够了。</p><p>前几天不是也爆出个新闻吗，官方下场说什么躺平和摆烂之风是境外势力在给我们洗脑，我听了之后觉得其实也挺难绷的，有人说其实我们从高中开始就是在默认加班，因为像晚自习和周六周日的上课什么的基本上也都是强迫我们参加嘛。我之前，包括现在其实也还是总是喜欢压力自己，逼自己去做一些事情。但我现在觉得对一个人来说最重要的其实还是内心的平静，这四年以来，我也一直在寻找我内心的平静，总是试图在内心的骚乱和暂时的平静中寻求一种动态平衡，也许我之后的人生就会是这样的一个过程吧。</p><p>我觉得现在很多人焦虑的原因就还是总是喜欢把自己盲目地和别人比较，他们都忽略了一件事情，就是所有人的起点和成长的环境都是不一样的，有的人出生就在谷底，有的人出生在半山腰，有的人出生就在山顶，有的人一生是从谷底爬到半山腰的过程，有的人一生是从半山腰爬到山顶的过程，有的人一生都待在山顶，但你能说那些最终也没爬到山顶的人就是白白活这一遭吗？我觉得不能。</p><img src="/images/img_1777639369539.jpeg" alt="图片" style="max-width: 100%;"><p>走得慢也好，步子小也好，只要在向前走就好。</p><p>以上。</p><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1777639404279" data-twikoo-path="article_1777639404279"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/30/yi-dian-si-kao-yu-zong-jie/</id>
    <link href="https://aoiblog.top/2026/04/30/yi-dian-si-kao-yu-zong-jie/"/>
    <published>2026-04-30T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>一点思考与总结</h2>
4月24日

<p>本来已经累到不想再花费额外的时间和精力自己手搓文章，毕竟周日还要面对尚未复习的三门期中考试，但还是想逼着自己写点什么东西，以此来记录这段宝贵的实践经历。</p>
<p>为什么我会觉得这段经历很宝贵呢，也许是因为我一开始也没想到]]>
    </summary>
    <title>一点思考与总结</title>
    <updated>2026-04-30T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>Git 团队开发规范与冲突处理 SOP</h2>2026年4月17日20:37:25<br>好不容易提前把今天的工作弄完了，遂拷打ai根据这几天拷打ai的经验生成了两篇如何使用git的笔记<br>上班好累……<br><p>这份文档偏向真实项目协作，重点说明团队中的开发规范、提交规范、分支管理、开发流程、上线流程、合并冲突处理和常见问题。</p><p>如果要学习 Git 基础概念和常用命令，请先看：<code>Git常用命令总结笔记.md</code>。</p><h2 id="1-核心原则"><a href="#1-核心原则" class="headerlink" title="1. 核心原则"></a>1. 核心原则</h2><p>团队协作时，Git 的目标不是“把代码提交上去就行”，而是保证每次提交、每个分支、每次合并都清楚、可追踪、可回滚。</p><p>日常开发遵循以下原则：</p><ul><li>操作分支前，始终保证本地仓库已经同步远端最新代码。</li><li>本地不要长期留有大量未提交代码。</li><li>一般情况下禁止使用 <code>git add .</code> 提交全量文件，优先按文件或功能分批提交。</li><li>提交要小步、清晰、可回看，不要把多个无关功能混在一个 commit 中。</li><li>每天下班前至少提交一次代码，避免本地改动丢失。</li><li>如果代码还不稳定，可以先提交到本地，开发完成后再推送远端。</li><li>大块功能改动应在独立功能分支或 worktree 中开发，开发完再合并回目标分支。</li><li>公共分支上谨慎使用 <code>rebase</code>，禁止随意强推。</li><li>危险命令执行前必须确认影响范围，例如 <code>git reset --hard</code>、<code>git stash clear</code>、<code>git push --force</code>。</li></ul><h2 id="2-开发前检查"><a href="#2-开发前检查" class="headerlink" title="2. 开发前检查"></a>2. 开发前检查</h2><p>开始任何开发前，先确认仓库、分支和工作区状态。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status --short --branch<br>git branch --show-current<br>git fetch origin<br></code></pre></td></tr></table></figure><p>如果准备从 <code>master</code> 新建功能分支，应先同步本地 <code>master</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><code class="hljs bash">git switch master<br>git pull origin master<br></code></pre></td></tr></table></figure><p>如果项目使用 worktree，先确认当前功能分支实际所在目录：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree list<br></code></pre></td></tr></table></figure><p>注意：启动哪个目录，运行的就是哪个目录对应分支的代码。不要在主工作区启动项目，却以为跑的是 <code>.worktrees</code> 里的功能分支代码。</p><h2 id="3-Commit-提交规范"><a href="#3-Commit-提交规范" class="headerlink" title="3. Commit 提交规范"></a>3. Commit 提交规范</h2><h3 id="3-1-标准格式"><a href="#3-1-标准格式" class="headerlink" title="3.1 标准格式"></a>3.1 标准格式</h3><p>推荐提交信息格式：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs text">type(scope): subject<br><br>body<br><br>footer<br></code></pre></td></tr></table></figure><p>由三部分组成：</p><ul><li>标题行：必填，描述主要修改类型和内容。</li><li>主题内容：说明为什么修改、做了什么修改、开发思路是什么。</li><li>页脚注释：可选，可写备注、BUG 号、Closed Issues 或 BREAKING CHANGE。</li></ul><h3 id="3-2-标题行"><a href="#3-2-标题行" class="headerlink" title="3.2 标题行"></a>3.2 标题行</h3><p>标题行格式：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">type(scope): subject<br></code></pre></td></tr></table></figure><p>说明：</p><ul><li><code>type</code>：必填，表示 commit 类型。</li><li><code>scope</code>：可选，表示影响范围，例如 <code>global</code>、<code>common</code>、<code>route</code>、<code>component</code>、<code>utils</code>、<code>build</code>。</li><li><code>subject</code>：必填，简短描述本次提交内容，建议不超过 50 个字符。</li></ul><p>示例：</p><figure class="highlight text"><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><code class="hljs text">feat(component): add export button<br>fix(route): correct login redirect<br>docs: update git workflow notes<br></code></pre></td></tr></table></figure><h3 id="3-3-type-类型"><a href="#3-3-type-类型" class="headerlink" title="3.3 type 类型"></a>3.3 type 类型</h3><table><thead><tr><th>type</th><th>说明</th></tr></thead><tbody><tr><td><code>feat</code></td><td>新功能、新特性</td></tr><tr><td><code>fix</code></td><td>修复 bug</td></tr><tr><td><code>perf</code></td><td>性能优化</td></tr><tr><td><code>refactor</code></td><td>代码重构，不改变外部行为</td></tr><tr><td><code>docs</code></td><td>文档修改</td></tr><tr><td><code>style</code></td><td>代码格式修改，不是 CSS 样式修改</td></tr><tr><td><code>test</code></td><td>测试用例新增或修改</td></tr><tr><td><code>build</code></td><td>项目构建或依赖项修改</td></tr><tr><td><code>revert</code></td><td>回滚上一次提交</td></tr><tr><td><code>ci</code></td><td>持续集成相关修改</td></tr><tr><td><code>chore</code></td><td>其他不属于以上类型的修改</td></tr><tr><td><code>release</code></td><td>发布新版本</td></tr><tr><td><code>workflow</code></td><td>工作流相关修改</td></tr></tbody></table><h3 id="3-4-body-正文"><a href="#3-4-body-正文" class="headerlink" title="3.4 body 正文"></a>3.4 body 正文</h3><p>当提交内容不止一两行时，建议写 body。</p><p>body 应说明：</p><ul><li>为什么要改。</li><li>改了哪些关键点。</li><li>有哪些实现思路或取舍。</li><li>是否影响已有功能。</li></ul><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs text">fix(component): preserve line breaks in approval comments<br><br>Approval comments lost line breaks after rendering as plain text.<br>This change keeps the existing image rendering component and adds<br>white-space: pre-wrap for text comments.<br></code></pre></td></tr></table></figure><h3 id="3-5-footer-页脚"><a href="#3-5-footer-页脚" class="headerlink" title="3.5 footer 页脚"></a>3.5 footer 页脚</h3><p>footer 通常用于：</p><ul><li>关联 BUG 编号。</li><li>关闭 issue。</li><li>标记破坏性变更。</li></ul><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">Closed #123<br></code></pre></td></tr></table></figure><p>或：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">BREAKING CHANGE: remove legacy export API<br></code></pre></td></tr></table></figure><h3 id="3-6-常见提交示例"><a href="#3-6-常见提交示例" class="headerlink" title="3.6 常见提交示例"></a>3.6 常见提交示例</h3><p>新功能：</p><figure class="highlight text"><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><code class="hljs text">feat(export): add select all export support<br><br>Add export support for selected project records.<br></code></pre></td></tr></table></figure><p>修复 bug：</p><figure class="highlight text"><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><code class="hljs text">fix(login): correct redirect after session timeout<br><br>Keep the original redirect URL before sending users to login.<br></code></pre></td></tr></table></figure><p>文档：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">docs: add git team workflow sop<br></code></pre></td></tr></table></figure><p>重构：</p><figure class="highlight text"><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><code class="hljs text">refactor(utils): simplify date formatter<br><br>Keep the original output format while removing duplicated branches.<br></code></pre></td></tr></table></figure><p>合并最新 master 并解决冲突：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">chore: merge latest master and resolve conflicts<br></code></pre></td></tr></table></figure><h2 id="4-分支管理规范"><a href="#4-分支管理规范" class="headerlink" title="4. 分支管理规范"></a>4. 分支管理规范</h2><h3 id="4-1-master-稳定分支"><a href="#4-1-master-稳定分支" class="headerlink" title="4.1 master 稳定分支"></a>4.1 master 稳定分支</h3><p><code>master</code> 是稳定分支。</p><p>规则：</p><ul><li>代码至少上过一次客户生产环境后，才可以合并入此分支。</li><li>只允许通过 MR 合入代码。</li><li>不允许直接推送。</li></ul><p>用途：</p><ul><li>表示稳定、可追踪的生产基础代码。</li><li>作为新功能分支的常用起点。</li><li>上线后由 release 分支合回。</li></ul><h3 id="4-2-development-开发测试分支"><a href="#4-2-development-开发测试分支" class="headerlink" title="4.2 development 开发测试分支"></a>4.2 development 开发测试分支</h3><p><code>development</code> 是开发测试分支。</p><p>规则：</p><ul><li>功能开发完成后合并入此分支提测到测试环境。</li><li>所有客户测试环境都会部署此分支。</li><li>只允许通过 MR 合入代码。</li><li>不允许直接推送。</li></ul><p>用途：</p><ul><li>承接功能分支的提测代码。</li><li>测试同学验证开发阶段功能。</li></ul><h3 id="4-3-release-预发分支"><a href="#4-3-release-预发分支" class="headerlink" title="4.3 release 预发分支"></a>4.3 release 预发分支</h3><p>命名规则：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">release/&lt;客户标识&gt;_&lt;月_日&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">release/cbd_12_11<br></code></pre></td></tr></table></figure><p>含义：茶百道 12 月 11 日上线功能的预发分支。</p><p>用途：</p><ul><li>功能经过测试同学验证完成后，合入预发分支。</li><li>用于部署预发环境。</li><li>上线时通常部署对应 release 分支代码。</li></ul><h3 id="4-4-feat-功能分支"><a href="#4-4-feat-功能分支" class="headerlink" title="4.4 feat 功能分支"></a>4.4 feat 功能分支</h3><p>命名规则：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">feat/&lt;英文功能名称&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">feat/export-select-all<br>feat/cbd-detail-multiline-display<br></code></pre></td></tr></table></figure><p>规则：</p><ul><li>新功能开发分支。</li><li>一般从最新 <code>master</code> 检出。</li><li>一个功能尽量一个分支。</li><li>可以多次 commit、多次 push。</li><li>Code review 后继续在同一分支修复，原 MR 会自动更新。</li></ul><h3 id="4-5-hotfix-热修复分支"><a href="#4-5-hotfix-热修复分支" class="headerlink" title="4.5 hotfix 热修复分支"></a>4.5 hotfix 热修复分支</h3><p>命名规则：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">hotfix/&lt;问题名称&gt;<br></code></pre></td></tr></table></figure><p>规则：</p><ul><li>上线后出现紧急 bug 时使用。</li><li>基于最近一次对应客户上线 tag 检出热修复分支。</li><li>修复完成后按新功能提测流程走。</li></ul><h3 id="4-6-Tag-规范"><a href="#4-6-Tag-规范" class="headerlink" title="4.6 Tag 规范"></a>4.6 Tag 规范</h3><p>每次客户上线完成后，需要基于 release 分支创建对应 tag，用于标识客户上线时各仓库代码状态，方便出问题时回滚。</p><p>命名规则：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">&lt;客户标识&gt;_&lt;年_月_日&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">cbd_2026_04_17<br></code></pre></td></tr></table></figure><p>说明：</p><ul><li>除 <code>bpmax-plugins</code> 外，其他项目通常会在包哥部署生产环境时自动生成 tag。</li><li><code>bpmax-plugins</code> 需要在 release 分支上手动创建 tag。</li></ul><h2 id="5-标准开发流程"><a href="#5-标准开发流程" class="headerlink" title="5. 标准开发流程"></a>5. 标准开发流程</h2><h3 id="5-1-开发新功能"><a href="#5-1-开发新功能" class="headerlink" title="5.1 开发新功能"></a>5.1 开发新功能</h3><p>从对应项目最新 <code>master</code> 分支检出新功能分支：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch master<br>git pull origin master<br>git switch -c feat/&lt;英文功能名称&gt;<br></code></pre></td></tr></table></figure><p>开发过程中：</p><ul><li>尽量按功能点拆分提交。</li><li>不要把多个无关功能混在同一个分支或同一个 commit 中。</li><li>提交前检查暂存区内容。</li></ul><p>推荐提交流程：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status<br>git add &lt;文件1&gt;<br>git add &lt;文件2&gt;<br>git --no-pager diff --cached<br>git commit<br></code></pre></td></tr></table></figure><h3 id="5-2-新功能提测"><a href="#5-2-新功能提测" class="headerlink" title="5.2 新功能提测"></a>5.2 新功能提测</h3><p>本地开发完成后，达到提测标准再提测。</p><p>提测标准通常包括：</p><ul><li>功能主体开发完成。</li><li>自测通过。</li><li>冒烟测试通过。</li><li>没有明显控制台错误或阻塞问题。</li><li>提交信息符合规范。</li></ul><p>提测流程：</p><ol><li>推送功能分支到远端。</li><li>创建 MR 合并到 <code>development</code>。</li><li>找相关负责人合并。</li><li>部署测试环境时，可找相关负责人协助部署。</li></ol><p>首次推送：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin feat/&lt;英文功能名称&gt;<br></code></pre></td></tr></table></figure><p>后续同分支推送：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push<br></code></pre></td></tr></table></figure><h3 id="5-3-测试阶段出现-bug"><a href="#5-3-测试阶段出现-bug" class="headerlink" title="5.3 测试阶段出现 bug"></a>5.3 测试阶段出现 bug</h3><p>如果测试阶段发现 bug：</p><ol><li>回到原功能分支修复。</li><li>提交修复 commit。</li><li>推送同一个功能分支。</li><li>再次合并入 <code>development</code>。</li><li>重复提测流程。</li></ol><p>不要为了同一个功能的测试 bug 随意新建多个功能分支，除非确实是独立功能或独立问题。</p><h3 id="5-4-线上紧急-bug-修复"><a href="#5-4-线上紧急-bug-修复" class="headerlink" title="5.4 线上紧急 bug 修复"></a>5.4 线上紧急 bug 修复</h3><p>线上出现紧急 bug 时：</p><ol><li>找到最近一次对应客户上线 tag。</li><li>从该 tag 检出 <code>hotfix</code> 分支。</li><li>在 <code>hotfix</code> 分支修复问题。</li><li>修复完成后按新功能提测流程走。</li></ol><p>示例：</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><code class="hljs bash">git fetch origin --tags<br>git switch -c hotfix/&lt;问题名称&gt; &lt;tag名称&gt;<br></code></pre></td></tr></table></figure><h3 id="5-5-预上线"><a href="#5-5-预上线" class="headerlink" title="5.5 预上线"></a>5.5 预上线</h3><p>预上线前，将各个功能分支合并到对应预发分支。</p><p>流程：</p><ol><li>从合适基线创建或更新 <code>release/&lt;客户标识&gt;_&lt;月_日&gt;</code>。</li><li>将本次要上线的功能分支合入 release。</li><li>部署预发环境。</li><li>测试同学在预发环境验证。</li></ol><p>预发环境发现问题时：</p><ul><li>回到对应功能分支修复。</li><li>修复后再合入预发分支。</li><li>不建议直接在 release 分支上堆临时修复，避免功能来源不清晰。</li></ul><h3 id="5-6-上线"><a href="#5-6-上线" class="headerlink" title="5.6 上线"></a>5.6 上线</h3><p>上线时：</p><ul><li>有迭代功能的项目，上对应预发分支代码。</li><li>本次没有迭代功能的项目，上对应 <code>master</code> 分支代码。</li></ul><p>上线前确认：</p><ul><li>release 分支内容符合本次上线范围。</li><li>预发测试通过。</li><li>关键功能冒烟通过。</li><li>需要上线的仓库和分支明确。</li></ul><h3 id="5-7-上线后操作"><a href="#5-7-上线后操作" class="headerlink" title="5.7 上线后操作"></a>5.7 上线后操作</h3><p>上线后必须做收尾：</p><ol><li>确保预发分支已经加上 tag。</li><li>合并预发分支到 <code>master</code>。</li><li>合并 <code>master</code> 分支到 <code>development</code>。</li><li>删除预发分支对应的各个功能分支。</li><li>删除预发分支。</li></ol><p>这样可以保证：</p><ul><li><code>master</code> 记录已上线稳定代码。</li><li><code>development</code> 不会落后于生产稳定代码。</li><li>临时分支及时清理，避免后续误用。</li></ul><h2 id="6-MR-与推送规范"><a href="#6-MR-与推送规范" class="headerlink" title="6. MR 与推送规范"></a>6. MR 与推送规范</h2><h3 id="6-1-首次推送分支"><a href="#6-1-首次推送分支" class="headerlink" title="6.1 首次推送分支"></a>6.1 首次推送分支</h3><p>本地新建分支后，GitLab 页面看不到该分支。必须先推送到远端：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin feat/export-select-all<br></code></pre></td></tr></table></figure><p><code>-u</code> 会建立本地分支和远端分支的上游关系。</p><h3 id="6-2-后续推送"><a href="#6-2-后续推送" class="headerlink" title="6.2 后续推送"></a>6.2 后续推送</h3><p>建立上游关系后，后续同分支直接：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push<br></code></pre></td></tr></table></figure><h3 id="6-3-已有-open-MR"><a href="#6-3-已有-open-MR" class="headerlink" title="6.3 已有 open MR"></a>6.3 已有 open MR</h3><p>如果 GitLab 提示：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">Another open merge request already exists for this source branch<br></code></pre></td></tr></table></figure><p>说明这个源分支已经有一个未关闭 MR。</p><p>处理方式：</p><ul><li>不要重复新建 MR。</li><li>继续 push 到同一个分支。</li><li>原 MR 会自动更新。</li></ul><h3 id="6-4-MR-目标分支"><a href="#6-4-MR-目标分支" class="headerlink" title="6.4 MR 目标分支"></a>6.4 MR 目标分支</h3><p>常见目标分支：</p><table><thead><tr><th>场景</th><th>源分支</th><th>目标分支</th></tr></thead><tbody><tr><td>新功能提测</td><td><code>feat/&lt;功能名&gt;</code></td><td><code>development</code></td></tr><tr><td>预上线</td><td><code>feat/&lt;功能名&gt;</code></td><td><code>release/&lt;客户标识&gt;_&lt;月_日&gt;</code></td></tr><tr><td>上线后合回</td><td><code>release/&lt;客户标识&gt;_&lt;月_日&gt;</code></td><td><code>master</code></td></tr><tr><td>同步开发分支</td><td><code>master</code></td><td><code>development</code></td></tr></tbody></table><p>创建 MR 前，必须确认 source branch 和 target branch 没选反。</p><h2 id="7-合并冲突处理-SOP"><a href="#7-合并冲突处理-SOP" class="headerlink" title="7. 合并冲突处理 SOP"></a>7. 合并冲突处理 SOP</h2><p>本 SOP 用于处理 GitLab MR 提示 merge conflict 的常见场景。</p><p>核心原则：</p><ol><li>先让本地 <code>master</code> 同步到线上最新 <code>origin/master</code>。</li><li>再把最新 <code>origin/master</code> 合并到发生冲突的功能分支。</li><li>在本地解决冲突。<strong>一定要在线下解决冲突，不要线上解决</strong></li><li>验证后推送功能分支，让 MR 自动更新。</li></ol><h3 id="7-1-确认当前仓库和分支"><a href="#7-1-确认当前仓库和分支" class="headerlink" title="7.1 确认当前仓库和分支"></a>7.1 确认当前仓库和分支</h3><p>进入目标仓库或目标 worktree：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-built_in">cd</span> C:\Users\lenovo\Desktop\BPMAX\hpt<span class="hljs-literal">-frontend</span><br></code></pre></td></tr></table></figure><p>查看当前分支和工作区：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status --short --branch<br>git branch --show-current<br></code></pre></td></tr></table></figure><p>如果项目使用 worktree：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree list<br></code></pre></td></tr></table></figure><p>如果看到类似：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">C:/Users/lenovo/Desktop/BPMAX/hpt-frontend/.worktrees/cbd-detail-multiline-display  [feat/cbd-detail-multiline-display]<br></code></pre></td></tr></table></figure><p>后续操作应进入该 worktree：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-built_in">cd</span> C:\Users\lenovo\Desktop\BPMAX\hpt<span class="hljs-literal">-frontend</span>\.worktrees\cbd<span class="hljs-literal">-detail-multiline-display</span><br></code></pre></td></tr></table></figure><h3 id="7-2-同步本地-master-到线上最新-master"><a href="#7-2-同步本地-master-到线上最新-master" class="headerlink" title="7.2 同步本地 master 到线上最新 master"></a>7.2 同步本地 master 到线上最新 master</h3><p>先更新远端引用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch origin<br></code></pre></td></tr></table></figure><p>确认本地 <code>master</code> 和线上 <code>origin/master</code> 的差异：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git rev-list --left-right --count master...origin/master<br></code></pre></td></tr></table></figure><p>输出格式：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">本地领先数  本地落后数<br></code></pre></td></tr></table></figure><p>例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">0  65<br></code></pre></td></tr></table></figure><p>表示本地 <code>master</code> 落后线上 <code>origin/master</code> 65 个提交。</p><p>切到 <code>master</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch master<br></code></pre></td></tr></table></figure><p>如果 <code>master</code> 上有未提交改动，先临时保存：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;before-sync-master&quot;</span><br></code></pre></td></tr></table></figure><p>快进同步到线上最新：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git merge --ff-only origin/master<br></code></pre></td></tr></table></figure><p>如果前面 stash 过，再恢复：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash pop<br></code></pre></td></tr></table></figure><p>最后确认本地 <code>master</code> 已经和线上一致：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git rev-list --left-right --count master...origin/master<br></code></pre></td></tr></table></figure><p>期望输出：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">0  0<br></code></pre></td></tr></table></figure><p>注意：把 <code>origin/master</code> 同步到本地 <code>master</code> 不会影响线上。只有执行 <code>git push origin master</code> 才会改变线上 <code>master</code>。</p><h3 id="7-3-切回发生冲突的功能分支"><a href="#7-3-切回发生冲突的功能分支" class="headerlink" title="7.3 切回发生冲突的功能分支"></a>7.3 切回发生冲突的功能分支</h3><p>如果功能分支在当前仓库：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch feat/&lt;功能分支&gt;<br></code></pre></td></tr></table></figure><p>如果功能分支在 worktree：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-built_in">cd</span> C:\Users\lenovo\Desktop\BPMAX\hpt<span class="hljs-literal">-frontend</span>\.worktrees\&lt;功能worktree目录&gt;<br></code></pre></td></tr></table></figure><p>再次确认：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status --short --branch<br>git branch --show-current<br></code></pre></td></tr></table></figure><h3 id="7-4-合并最新-origin-master-到功能分支"><a href="#7-4-合并最新-origin-master-到功能分支" class="headerlink" title="7.4 合并最新 origin&#x2F;master 到功能分支"></a>7.4 合并最新 origin&#x2F;master 到功能分支</h3><p>如果功能分支上有未提交改动，先 stash：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;before-merge-master-into-feature&quot;</span><br></code></pre></td></tr></table></figure><p>确保远端引用最新：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch origin<br></code></pre></td></tr></table></figure><p>在功能分支上合并线上最新 <code>master</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git merge origin/master<br></code></pre></td></tr></table></figure><p>如果没有冲突，Git 会直接完成合并。</p><p>如果有冲突，Git 会提示类似：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">CONFLICT (content): Merge conflict in components/ProjectStepSingle/ProjectStepSingle.vue<br>Automatic merge failed; fix conflicts and then commit the result.<br></code></pre></td></tr></table></figure><h3 id="7-5-查看冲突文件和冲突块"><a href="#7-5-查看冲突文件和冲突块" class="headerlink" title="7.5 查看冲突文件和冲突块"></a>7.5 查看冲突文件和冲突块</h3><p>查看所有未解决冲突文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git diff --name-only --diff-filter=U<br></code></pre></td></tr></table></figure><p>查看工作区状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status --short --branch<br></code></pre></td></tr></table></figure><p>状态中 <code>UU</code> 表示该文件有未解决冲突：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">UU components/ProjectStepSingle/ProjectStepSingle.vue<br></code></pre></td></tr></table></figure><p>在 PowerShell 中搜索冲突标记：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-built_in">Select-String</span> <span class="hljs-literal">-Path</span> <span class="hljs-string">&quot;components\ProjectStepSingle\ProjectStepSingle.vue&quot;</span> <span class="hljs-literal">-Pattern</span> <span class="hljs-string">&quot;&lt;&lt;&lt;&lt;&lt;&lt;&lt;|=======|&gt;&gt;&gt;&gt;&gt;&gt;&gt;&quot;</span> <span class="hljs-literal">-Context</span> <span class="hljs-number">5</span>,<span class="hljs-number">5</span><br></code></pre></td></tr></table></figure><p>冲突块通常长这样：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs text">&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD<br>当前功能分支的内容<br>=======<br>合并进来的 origin/master 内容<br>&gt;&gt;&gt;&gt;&gt;&gt;&gt; origin/master<br></code></pre></td></tr></table></figure><p>可视化理解:</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs text">文件内容<br>    │<br>    ▼<br>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD      ← 冲突开始，下面是“你的版本”<br>你的代码 A<br>你的代码 B<br>=======           ← 分隔线，下面是“别人的版本”<br>别人的代码 A<br>别人的代码 B<br>&gt;&gt;&gt;&gt;&gt;&gt;&gt; main      ← 冲突结束，并标明别人的分支名<br>    │<br>    ▼<br>（删除所有标记，保留最终代码）<br></code></pre></td></tr></table></figure><p>在“功能分支合并 <code>origin/master</code>”这个场景中：</p><ul><li><code>HEAD</code> &#x2F; <code>ours</code>：当前功能分支。</li><li><code>origin/master</code> &#x2F; <code>theirs</code>：线上最新 master。</li></ul><h3 id="7-6-判断解决策略"><a href="#7-6-判断解决策略" class="headerlink" title="7.6 判断解决策略"></a>7.6 判断解决策略</h3><p>不要机械地只选一边。先判断两边改动目的。</p><p>常见选择：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 完全保留当前功能分支版本</span><br>git checkout --ours -- path/to/file<br><br><span class="hljs-comment"># 完全采用 origin/master 版本</span><br>git checkout --theirs -- path/to/file<br></code></pre></td></tr></table></figure><p>但如果两边都有价值，应手动合并。</p><p>示例：</p><ul><li>功能分支改动：保留多行文本输入展示效果，使用 <code>v-text</code> 和 <code>white-space: pre-wrap</code>。</li><li><code>origin/master</code> 改动：新增 <code>StepApprovalCommentContent</code>，支持审批意见中展示图片。</li></ul><p>正确策略不是只保留一边，而是合并能力：</p><ul><li>普通多行文本字段保留功能分支实现。</li><li>审批意见内容保留线上组件，以支持图片。</li><li>修改线上组件文本样式为 <code>white-space: pre-wrap</code>，保证纯文本审批意见也按输入换行显示。</li></ul><h3 id="7-7-手动解决冲突"><a href="#7-7-手动解决冲突" class="headerlink" title="7.7 手动解决冲突"></a>7.7 手动解决冲突</h3><p>打开冲突文件，删除冲突标记：</p><figure class="highlight text"><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><code class="hljs text">&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD<br>=======<br>&gt;&gt;&gt;&gt;&gt;&gt;&gt; origin/master<br></code></pre></td></tr></table></figure><p>保留或组合需要的代码。</p><p>解决后再次检查是否还有冲突标记：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-built_in">Select-String</span> <span class="hljs-literal">-Path</span> <span class="hljs-string">&quot;components\ProjectStepSingle\ProjectStepSingle.vue&quot;</span> <span class="hljs-literal">-Pattern</span> <span class="hljs-string">&quot;&lt;&lt;&lt;&lt;&lt;&lt;&lt;|=======|&gt;&gt;&gt;&gt;&gt;&gt;&gt;&quot;</span><br></code></pre></td></tr></table></figure><p>如果没有输出，说明该文件中没有残留冲突标记。</p><p>再检查 Git 是否还有未解决冲突文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git diff --name-only --diff-filter=U<br></code></pre></td></tr></table></figure><p>如果没有输出，说明冲突都已解决。</p><h3 id="7-8-标记冲突已解决"><a href="#7-8-标记冲突已解决" class="headerlink" title="7.8 标记冲突已解决"></a>7.8 标记冲突已解决</h3><p>把解决后的文件加入暂存区：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add -- components/ProjectStepSingle/ProjectStepSingle.vue<br></code></pre></td></tr></table></figure><p>如果还改了相关组件，也一起加入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add -- components/ProjectStepSingle/ProjectStepSingle.vue components/StepApprovalCommentContent.vue<br></code></pre></td></tr></table></figure><p>查看状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status --short --branch<br></code></pre></td></tr></table></figure><p>如果不再有 <code>UU</code>，说明 Git 已接受冲突解决结果。</p><h3 id="7-9-运行验证"><a href="#7-9-运行验证" class="headerlink" title="7.9 运行验证"></a>7.9 运行验证</h3><p>先做 Git 基础检查：</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><code class="hljs bash">git diff --check<br>git diff --name-only --diff-filter=U<br></code></pre></td></tr></table></figure><p>含义：</p><ul><li><code>git diff --check</code>：检查空白错误。</li><li><code>git diff --name-only --diff-filter=U</code>：确认没有未解决冲突文件。</li></ul><p>针对冲突相关文件运行 lint：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">.\node_modules\.bin\eslint.cmd components/ProjectStepSingle/ProjectStepSingle.vue components/StepApprovalCommentContent.vue<br></code></pre></td></tr></table></figure><p>如果 worktree 里没有可用的 <code>node_modules</code>，可以使用主仓库里的 eslint：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">C:\Users\lenovo\Desktop\BPMAX\hpt<span class="hljs-literal">-frontend</span>\node_modules\.bin\eslint.cmd components/ProjectStepSingle/ProjectStepSingle.vue components/StepApprovalCommentContent.vue<br></code></pre></td></tr></table></figure><p>如果只是 Prettier 或行尾格式问题，可只对相关文件自动修复：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">C:\Users\lenovo\Desktop\BPMAX\hpt<span class="hljs-literal">-frontend</span>\node_modules\.bin\eslint.cmd <span class="hljs-literal">--fix</span> components/ProjectStepSingle/ProjectStepSingle.vue components/StepApprovalCommentContent.vue<br></code></pre></td></tr></table></figure><p>修复后重新暂存：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add -- components/ProjectStepSingle/ProjectStepSingle.vue components/StepApprovalCommentContent.vue<br></code></pre></td></tr></table></figure><h3 id="7-10-提交-merge-commit"><a href="#7-10-提交-merge-commit" class="headerlink" title="7.10 提交 merge commit"></a>7.10 提交 merge commit</h3><p>正常提交：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit -m <span class="hljs-string">&quot;chore: merge latest master and resolve conflicts&quot;</span><br></code></pre></td></tr></table></figure><p>如果团队要求中文提交，也可以：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit -m <span class="hljs-string">&quot;chore: 合并最新master并解决冲突&quot;</span><br></code></pre></td></tr></table></figure><p>如果 pre-commit hook 因为无关历史问题阻塞，例如 <code>origin/master</code> 自带旧 lint 问题，而冲突相关文件已经单独验证通过，可以谨慎使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit --no-verify -m <span class="hljs-string">&quot;chore: 合并最新master并解决冲突&quot;</span><br></code></pre></td></tr></table></figure><p>使用 <code>--no-verify</code> 前必须确认：</p><ul><li>冲突文件已解决。</li><li><code>git diff --check</code> 通过。</li><li>冲突相关文件 lint 通过。</li><li>阻塞 hook 的问题不是本次改动引入。</li></ul><h3 id="7-11-恢复合并前-stash-的本地改动"><a href="#7-11-恢复合并前-stash-的本地改动" class="headerlink" title="7.11 恢复合并前 stash 的本地改动"></a>7.11 恢复合并前 stash 的本地改动</h3><p>如果合并前做过 stash，提交 merge commit 后恢复：</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><code class="hljs bash">git stash list<br>git stash pop<br></code></pre></td></tr></table></figure><p>如果 <code>stash pop</code> 又产生冲突，按同样流程解决。</p><p>恢复完成后确认工作区：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status --short --branch<br></code></pre></td></tr></table></figure><p>如果输出类似：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">## feat/your-feature-branch...origin/feat/your-feature-branch [ahead N]<br></code></pre></td></tr></table></figure><p>并且没有文件列表，说明工作区干净，只是本地分支领先远端。</p><h3 id="7-12-推送功能分支"><a href="#7-12-推送功能分支" class="headerlink" title="7.12 推送功能分支"></a>7.12 推送功能分支</h3><p>推送当前功能分支：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push origin feat/&lt;功能分支&gt;<br></code></pre></td></tr></table></figure><p>推送后 GitLab MR 会自动更新。</p><h2 id="8-常见问题与解决方法"><a href="#8-常见问题与解决方法" class="headerlink" title="8. 常见问题与解决方法"></a>8. 常见问题与解决方法</h2><h3 id="8-1-GitLab-看不到本地分支"><a href="#8-1-GitLab-看不到本地分支" class="headerlink" title="8.1 GitLab 看不到本地分支"></a>8.1 GitLab 看不到本地分支</h3><p>原因：分支还只在本地，没有推送到远端。</p><p>解决：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>推送后，GitLab 的 source branch 下拉框才能看到该分支。</p><h3 id="8-2-已有-open-MR，不能再次创建"><a href="#8-2-已有-open-MR，不能再次创建" class="headerlink" title="8.2 已有 open MR，不能再次创建"></a>8.2 已有 open MR，不能再次创建</h3><p>原因：同一个 source branch 已经有未关闭 MR。</p><p>处理：</p><ul><li>不要重复创建 MR。</li><li>继续 push 到原分支。</li><li>原 MR 会自动更新。</li></ul><h3 id="8-3-MR-出现-merge-conflict"><a href="#8-3-MR-出现-merge-conflict" class="headerlink" title="8.3 MR 出现 merge conflict"></a>8.3 MR 出现 merge conflict</h3><p>原因：</p><ul><li>源分支和目标分支都改了同一个文件或同一区域。</li><li>GitLab 无法自动判断该保留哪边内容。</li></ul><p>处理：</p><ol><li>在本地把目标分支最新代码合入功能分支。</li><li>手动解决冲突。</li><li>验证。</li><li>commit。</li><li>push 功能分支。</li></ol><h3 id="8-4-merge-后-GitLab-看起来多了很多提交"><a href="#8-4-merge-后-GitLab-看起来多了很多提交" class="headerlink" title="8.4 merge 后 GitLab 看起来多了很多提交"></a>8.4 merge 后 GitLab 看起来多了很多提交</h3><p>把 <code>origin/master</code> merge 到功能分支后，功能分支历史里会出现一个 merge commit。</p><p>这个 merge commit 有两个父节点：</p><ul><li>功能分支原来的提交。</li><li>最新 <code>origin/master</code> 的提交链。</li></ul><p>因此 GitLab 的分支提交列表有时会把 <code>master</code> 上合进来的提交也显示出来，看起来像“多了很多提交”。</p><p>判断 MR 实际多了多少提交，应以目标分支比较为准：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git rev-list --left-right --count origin/master...feat/&lt;功能分支&gt;<br></code></pre></td></tr></table></figure><p>例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">0  4<br></code></pre></td></tr></table></figure><p>表示相对 <code>origin/master</code>，功能分支实际只有 4 个提交。</p><p>如果原来有 3 个业务提交，合并 master 后通常会变成：</p><ul><li>原来的 3 个业务提交。</li><li>1 个 merge commit。</li></ul><p>这是正常现象。</p><h3 id="8-5-worktree-跑错目录"><a href="#8-5-worktree-跑错目录" class="headerlink" title="8.5 worktree 跑错目录"></a>8.5 worktree 跑错目录</h3><p>现象：明明改了功能分支，但页面没变化。</p><p>原因：启动项目的目录不是功能分支所在 worktree。</p><p>检查：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree list<br>git branch --show-current<br></code></pre></td></tr></table></figure><p>处理：</p><ul><li>进入正确 worktree 目录。</li><li>在该目录启动项目。</li></ul><h3 id="8-6-本地-master-混入多个功能"><a href="#8-6-本地-master-混入多个功能" class="headerlink" title="8.6 本地 master 混入多个功能"></a>8.6 本地 master 混入多个功能</h3><p>不推荐在 <code>master</code> 上长期开发多个功能。如果已经混入：</p><ol><li>先 stash 当前改动。</li><li>恢复干净 <code>master</code>。</li><li>从最新 <code>master</code> 新建不同功能分支或 worktree。</li><li>从 stash 中按文件恢复到对应功能分支。</li></ol><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;temp-save-before-splitting-features&quot;</span><br>git switch master<br>git pull origin master<br>git switch -c feat/feature-a<br>git checkout stash@&#123;0&#125; -- path/to/feature-a-file<br></code></pre></td></tr></table></figure><h3 id="8-7-stash-pop-后又产生冲突"><a href="#8-7-stash-pop-后又产生冲突" class="headerlink" title="8.7 stash pop 后又产生冲突"></a>8.7 stash pop 后又产生冲突</h3><p>原因：stash 中的改动和当前分支已有改动冲突。</p><p>处理：</p><ol><li>查看冲突文件。</li><li>手动解决冲突。</li><li>删除冲突标记。</li><li><code>git add -- &lt;冲突文件&gt;</code>。</li><li>继续提交或保留工作区改动。</li></ol><p>常用命令：</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><code class="hljs bash">git diff --name-only --diff-filter=U<br>git status --short --branch<br></code></pre></td></tr></table></figure><h3 id="8-8-commit-被-hook-阻塞"><a href="#8-8-commit-被-hook-阻塞" class="headerlink" title="8.8 commit 被 hook 阻塞"></a>8.8 commit 被 hook 阻塞</h3><p>原因可能包括：</p><ul><li>lint 不通过。</li><li>格式检查不通过。</li><li>测试不通过。</li><li>历史代码中已有问题被 hook 扫到。</li></ul><p>处理原则：</p><ol><li>优先修复本次改动引入的问题。</li><li>如果是冲突相关文件，先单独验证冲突相关文件。</li><li>如果确认阻塞来自无关历史问题，且本次改动已验证通过，才考虑 <code>--no-verify</code>。</li></ol><p>谨慎使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit --no-verify -m <span class="hljs-string">&quot;chore: 合并最新master并解决冲突&quot;</span><br></code></pre></td></tr></table></figure><h3 id="8-9-git-diff-进入分页器"><a href="#8-9-git-diff-进入分页器" class="headerlink" title="8.9 git diff 进入分页器"></a>8.9 git diff 进入分页器</h3><p>现象：执行 <code>git diff</code> 后终端像卡住。</p><p>原因：进入了分页器。</p><p>退出：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">q<br></code></pre></td></tr></table></figure><p>避免分页：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git --no-pager diff<br>git --no-pager diff --cached<br></code></pre></td></tr></table></figure><h3 id="8-10-误用了-git-add"><a href="#8-10-误用了-git-add" class="headerlink" title="8.10 误用了 git add ."></a>8.10 误用了 git add .</h3><p>如果不小心全量暂存：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore --staged .<br></code></pre></td></tr></table></figure><p>然后重新按文件暂存：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add &lt;文件1&gt;<br>git add &lt;文件2&gt;<br>git --no-pager diff --cached<br></code></pre></td></tr></table></figure><h3 id="8-11-需要撤销工作区修改"><a href="#8-11-需要撤销工作区修改" class="headerlink" title="8.11 需要撤销工作区修改"></a>8.11 需要撤销工作区修改</h3><p>只撤销指定文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore &lt;文件&gt;<br></code></pre></td></tr></table></figure><p>注意：这会丢弃该文件未暂存的修改。</p><p>如果不确定是否还需要，先 stash：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;backup-before-restore&quot;</span><br></code></pre></td></tr></table></figure><h3 id="8-12-误提交后想重新整理"><a href="#8-12-误提交后想重新整理" class="headerlink" title="8.12 误提交后想重新整理"></a>8.12 误提交后想重新整理</h3><p>如果只是在本地提交，还没有 push，可以根据情况选择：</p><p>保留暂存区：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --soft HEAD^<br></code></pre></td></tr></table></figure><p>保留工作区但取消暂存：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --mixed HEAD^<br></code></pre></td></tr></table></figure><p>不要轻易使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --hard HEAD^<br></code></pre></td></tr></table></figure><p><code>--hard</code> 会丢弃工作区和暂存区内容，执行前必须确认不需要这些改动。</p><h2 id="9-团队检查清单"><a href="#9-团队检查清单" class="headerlink" title="9. 团队检查清单"></a>9. 团队检查清单</h2><h3 id="9-1-开发前检查"><a href="#9-1-开发前检查" class="headerlink" title="9.1 开发前检查"></a>9.1 开发前检查</h3><ul><li><input disabled="" type="checkbox"> 已确认当前仓库正确。</li><li><input disabled="" type="checkbox"> 已确认当前分支正确。</li><li><input disabled="" type="checkbox"> 已执行 <code>git fetch origin</code> 或 <code>git pull</code> 同步最新代码。</li><li><input disabled="" type="checkbox"> 本地工作区没有无关未提交改动。</li><li><input disabled="" type="checkbox"> 如果使用 worktree，已确认当前目录就是目标分支目录。</li></ul><h3 id="9-2-提交前检查"><a href="#9-2-提交前检查" class="headerlink" title="9.2 提交前检查"></a>9.2 提交前检查</h3><ul><li><input disabled="" type="checkbox"> 已执行 <code>git status</code>。</li><li><input disabled="" type="checkbox"> 没有默认使用 <code>git add .</code> 全量提交。</li><li><input disabled="" type="checkbox"> 已按文件或功能点暂存改动。</li><li><input disabled="" type="checkbox"> 已执行 <code>git --no-pager diff --cached</code> 检查暂存区。</li><li><input disabled="" type="checkbox"> commit message 符合规范。</li><li><input disabled="" type="checkbox"> 本次提交只包含相关改动。</li></ul><h3 id="9-3-MR-前检查"><a href="#9-3-MR-前检查" class="headerlink" title="9.3 MR 前检查"></a>9.3 MR 前检查</h3><ul><li><input disabled="" type="checkbox"> 功能分支已推送到远端。</li><li><input disabled="" type="checkbox"> GitLab 能看到正确 source branch。</li><li><input disabled="" type="checkbox"> target branch 选择正确。</li><li><input disabled="" type="checkbox"> 本地自测或冒烟测试通过。</li><li><input disabled="" type="checkbox"> 如果已有 MR，没有重复新建。</li></ul><h3 id="9-4-冲突后检查"><a href="#9-4-冲突后检查" class="headerlink" title="9.4 冲突后检查"></a>9.4 冲突后检查</h3><ul><li><input disabled="" type="checkbox"> 已确认功能分支合入最新 <code>origin/master</code>。</li><li><input disabled="" type="checkbox"> 已删除所有冲突标记。</li><li><input disabled="" type="checkbox"> <code>git diff --name-only --diff-filter=U</code> 没有输出。</li><li><input disabled="" type="checkbox"> <code>git diff --check</code> 通过。</li><li><input disabled="" type="checkbox"> 冲突相关文件已完成必要 lint 或自测。</li><li><input disabled="" type="checkbox"> 已提交 merge commit。</li><li><input disabled="" type="checkbox"> 已 push 功能分支，让 MR 自动更新。</li></ul><h3 id="9-5-上线后检查"><a href="#9-5-上线后检查" class="headerlink" title="9.5 上线后检查"></a>9.5 上线后检查</h3><ul><li><input disabled="" type="checkbox"> 已确认 release 分支加上 tag。</li><li><input disabled="" type="checkbox"> 已将 release 分支合并回 <code>master</code>。</li><li><input disabled="" type="checkbox"> 已将 <code>master</code> 合并回 <code>development</code>。</li><li><input disabled="" type="checkbox"> 已删除本次 release 对应功能分支。</li><li><input disabled="" type="checkbox"> 已删除 release 分支。</li><li><input disabled="" type="checkbox"> 如 <code>bpmax-plugins</code> 需要手动 tag，已确认 tag 创建完成。</li></ul><h2 id="10-常用-SOP-命令汇总"><a href="#10-常用-SOP-命令汇总" class="headerlink" title="10. 常用 SOP 命令汇总"></a>10. 常用 SOP 命令汇总</h2><h3 id="10-1-开发新功能"><a href="#10-1-开发新功能" class="headerlink" title="10.1 开发新功能"></a>10.1 开发新功能</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch master<br>git pull origin master<br>git switch -c feat/&lt;英文功能名称&gt;<br></code></pre></td></tr></table></figure><h3 id="10-2-提交代码"><a href="#10-2-提交代码" class="headerlink" title="10.2 提交代码"></a>10.2 提交代码</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status<br>git add &lt;文件1&gt;<br>git add &lt;文件2&gt;<br>git --no-pager diff --cached<br>git commit<br></code></pre></td></tr></table></figure><h3 id="10-3-首次推送功能分支"><a href="#10-3-首次推送功能分支" class="headerlink" title="10.3 首次推送功能分支"></a>10.3 首次推送功能分支</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin feat/&lt;英文功能名称&gt;<br></code></pre></td></tr></table></figure><h3 id="10-4-后续推送"><a href="#10-4-后续推送" class="headerlink" title="10.4 后续推送"></a>10.4 后续推送</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push<br></code></pre></td></tr></table></figure><h3 id="10-5-同步本地-master"><a href="#10-5-同步本地-master" class="headerlink" title="10.5 同步本地 master"></a>10.5 同步本地 master</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch origin<br>git switch master<br>git merge --ff-only origin/master<br></code></pre></td></tr></table></figure><h3 id="10-6-功能分支合并最新-master"><a href="#10-6-功能分支合并最新-master" class="headerlink" title="10.6 功能分支合并最新 master"></a>10.6 功能分支合并最新 master</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch feat/&lt;英文功能名称&gt;<br>git fetch origin<br>git merge origin/master<br></code></pre></td></tr></table></figure><h3 id="10-7-查看并处理冲突"><a href="#10-7-查看并处理冲突" class="headerlink" title="10.7 查看并处理冲突"></a>10.7 查看并处理冲突</h3><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><code class="hljs bash">git diff --name-only --diff-filter=U<br>git status --short --branch<br></code></pre></td></tr></table></figure><p>解决文件后：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add -- &lt;冲突文件&gt;<br>git diff --check<br>git diff --name-only --diff-filter=U<br>git commit -m <span class="hljs-string">&quot;chore: 合并最新master并解决冲突&quot;</span><br>git push<br></code></pre></td></tr></table></figure><h3 id="10-8-临时保存未完成改动"><a href="#10-8-临时保存未完成改动" class="headerlink" title="10.8 临时保存未完成改动"></a>10.8 临时保存未完成改动</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;临时说明&quot;</span><br>git stash list<br>git stash pop<br></code></pre></td></tr></table></figure><h2 id="11-高风险命令提醒"><a href="#11-高风险命令提醒" class="headerlink" title="11. 高风险命令提醒"></a>11. 高风险命令提醒</h2><table><thead><tr><th>命令</th><th>风险</th><th>建议</th></tr></thead><tbody><tr><td><code>git reset --hard</code></td><td>丢弃工作区和暂存区内容</td><td>执行前先 <code>git status</code>，必要时先 stash</td></tr><tr><td><code>git stash clear</code></td><td>清空所有 stash，难以恢复</td><td>优先用 <code>git stash drop stash@{n}</code> 删除指定记录</td></tr><tr><td><code>git push --force</code></td><td>覆盖远端历史，可能影响他人</td><td>公共分支禁止随意使用，必须先沟通</td></tr><tr><td><code>git branch -D</code></td><td>强制删除本地分支</td><td>确认分支已合并或已不需要</td></tr><tr><td><code>git commit --no-verify</code></td><td>跳过 hook，可能绕过质量检查</td><td>只在确认阻塞问题非本次引入时谨慎使用</td></tr></tbody></table><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1776429194802" data-twikoo-path="article_1776429194802"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/16/git-tuan-dui-kai-fa-gui-fan-yu-chong-tu-chu-li-sop/</id>
    <link href="https://aoiblog.top/2026/04/16/git-tuan-dui-kai-fa-gui-fan-yu-chong-tu-chu-li-sop/"/>
    <published>2026-04-16T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>Git 团队开发规范与冲突处理 SOP</h2>
2026年4月17日20:37:25<br>
好不容易提前把今天的工作弄完了，遂拷打ai根据这几天拷打ai的经验生成了两篇如何使用git的笔记<br>
上班好累……<br>

<p>这份文档偏向真实项目协作，重点说明团队中]]>
    </summary>
    <title>Git 团队开发规范与冲突处理 SOP</title>
    <updated>2026-04-16T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="随笔" scheme="https://aoiblog.top/categories/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<h2>你妈的，为什么上班这么累</h2><strong>今天一定要早睡😭</strong><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1776430310445" data-twikoo-path="article_1776430310445"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/16/ni-ma-de-wei-shen-me-shang-ban-zhe-me-lei/</id>
    <link href="https://aoiblog.top/2026/04/16/ni-ma-de-wei-shen-me-shang-ban-zhe-me-lei/"/>
    <published>2026-04-16T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>你妈的，为什么上班这么累</h2>
<strong>今天一定要早睡😭</strong>

<section class="legacy-comments">
  <h2>评论区</h2>
  <div id="twikoo-article_1776430310445"]]>
    </summary>
    <title>你妈的，为什么上班这么累</title>
    <updated>2026-04-16T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>Git常用命令总结笔记</h2># Git 命令教学笔记<p>这份笔记偏向 Git 命令的教学和日常查阅，重点解决三个问题：</p><ul><li>Git 的基本概念是什么。</li><li>常用命令怎么写、什么时候用。</li><li>日常开发中遇到常见命令行现象时怎么处理。</li></ul><p>如果要查看团队开发规范、提交规范、分支管理、上线流程和冲突处理 SOP，请看另一份文档：<code>Git团队开发规范与冲突处理SOP.md</code>。</p><h2 id="1-Git-基础概念"><a href="#1-Git-基础概念" class="headerlink" title="1. Git 基础概念"></a>1. Git 基础概念</h2><h3 id="1-1-Git-是什么"><a href="#1-1-Git-是什么" class="headerlink" title="1.1 Git 是什么"></a>1.1 Git 是什么</h3><p>Git 是一个分布式版本控制工具，用来记录代码或文档的修改历史。它可以帮助你：</p><ul><li>查看文件改了什么。</li><li>回到某一次历史版本。</li><li>在不同分支上并行开发功能。</li><li>和远端仓库协作，例如 GitLab、GitHub、Gitee。</li></ul><h3 id="1-2-工作区、暂存区、本地仓库、远端仓库"><a href="#1-2-工作区、暂存区、本地仓库、远端仓库" class="headerlink" title="1.2 工作区、暂存区、本地仓库、远端仓库"></a>1.2 工作区、暂存区、本地仓库、远端仓库</h3><table><thead><tr><th>概念</th><th>含义</th><th>常见操作</th></tr></thead><tbody><tr><td>工作区</td><td>你当前直接编辑文件的目录</td><td>修改文件、查看 <code>git diff</code></td></tr><tr><td>暂存区</td><td>准备进入下一次提交的内容集合</td><td><code>git add</code>、<code>git restore --staged</code></td></tr><tr><td>本地仓库</td><td>当前电脑里 <code>.git</code> 保存的提交历史</td><td><code>git commit</code>、<code>git log</code></td></tr><tr><td>远端仓库</td><td>GitLab、GitHub、Gitee 等托管仓库</td><td><code>git fetch</code>、<code>git pull</code>、<code>git push</code></td></tr></tbody></table><p>可以简单理解为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">工作区 -&gt; git add -&gt; 暂存区 -&gt; git commit -&gt; 本地仓库 -&gt; git push -&gt; 远端仓库<br></code></pre></td></tr></table></figure><h3 id="1-3-文件状态"><a href="#1-3-文件状态" class="headerlink" title="1.3 文件状态"></a>1.3 文件状态</h3><table><thead><tr><th>状态</th><th>含义</th></tr></thead><tbody><tr><td><code>untracked</code></td><td>新文件，还没有被 Git 跟踪</td></tr><tr><td><code>modified</code></td><td>文件已修改，但还没有加入暂存区</td></tr><tr><td><code>staged</code></td><td>文件已加入暂存区，等待提交</td></tr><tr><td><code>committed</code></td><td>文件内容已经提交到本地仓库</td></tr></tbody></table><p>常见流转：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs text">untracked / modified<br>  -&gt; git add &lt;文件&gt;<br>staged<br>  -&gt; git commit<br>committed<br></code></pre></td></tr></table></figure><h3 id="1-4-常见对象和术语"><a href="#1-4-常见对象和术语" class="headerlink" title="1.4 常见对象和术语"></a>1.4 常见对象和术语</h3><table><thead><tr><th>术语</th><th>含义</th></tr></thead><tbody><tr><td><code>commit</code></td><td>一次提交记录，包含改动内容、作者、时间和提交说明</td></tr><tr><td><code>HEAD</code></td><td>当前所在提交位置，通常指向当前分支最新提交</td></tr><tr><td><code>branch</code></td><td>分支，用于并行开发不同功能</td></tr><tr><td><code>remote</code></td><td>远端仓库地址，例如 <code>origin</code></td></tr><tr><td><code>origin</code></td><td>默认远端仓库名</td></tr><tr><td><code>tag</code></td><td>标签，通常用于标记某次上线版本</td></tr><tr><td><code>stash</code></td><td>临时存档，用于暂存还不想提交的改动</td></tr><tr><td><code>worktree</code></td><td>一个仓库对应多个工作目录，适合同时开发多个分支</td></tr><tr><td><code>merge</code></td><td>合并，把一个分支的改动合入另一个分支</td></tr><tr><td><code>MR</code> &#x2F; <code>Merge Request</code></td><td>GitLab 中的合并请求</td></tr></tbody></table><h3 id="1-5-fetch、pull、merge、push-的关系"><a href="#1-5-fetch、pull、merge、push-的关系" class="headerlink" title="1.5 fetch、pull、merge、push 的关系"></a>1.5 fetch、pull、merge、push 的关系</h3><table><thead><tr><th>命令</th><th>作用</th><th>是否改变当前代码</th></tr></thead><tbody><tr><td><code>git fetch origin</code></td><td>获取远端最新提交信息</td><td>不直接改变工作区代码</td></tr><tr><td><code>git merge origin/master</code></td><td>把指定分支合并进当前分支</td><td>会改变当前分支</td></tr><tr><td><code>git pull origin master</code></td><td>拉取远端并合并，相当于 fetch + merge</td><td>会改变当前分支</td></tr><tr><td><code>git push origin &lt;分支名&gt;</code></td><td>把本地提交推送到远端</td><td>改变远端分支</td></tr></tbody></table><p>建议新手先理解并多使用：</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><code class="hljs bash">git fetch origin<br>git merge origin/master<br></code></pre></td></tr></table></figure><p>这样更容易知道每一步发生了什么。</p><h2 id="2-初始化与基础配置"><a href="#2-初始化与基础配置" class="headerlink" title="2. 初始化与基础配置"></a>2. 初始化与基础配置</h2><h3 id="2-1-查看-Git-配置"><a href="#2-1-查看-Git-配置" class="headerlink" title="2.1 查看 Git 配置"></a>2.1 查看 Git 配置</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git config --list<br></code></pre></td></tr></table></figure><p>查看当前仓库和全局生效的 Git 配置。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git config --global --list<br></code></pre></td></tr></table></figure><p>只查看全局配置。</p><h3 id="2-2-配置用户名和邮箱"><a href="#2-2-配置用户名和邮箱" class="headerlink" title="2.2 配置用户名和邮箱"></a>2.2 配置用户名和邮箱</h3><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><code class="hljs bash">git config --global user.name <span class="hljs-string">&quot;你的名字&quot;</span><br>git config --global user.email <span class="hljs-string">&quot;你的邮箱&quot;</span><br></code></pre></td></tr></table></figure><p>用户名和邮箱会写入 commit 记录中，用来标识提交者。</p><h3 id="2-3-配置-Windows-换行符"><a href="#2-3-配置-Windows-换行符" class="headerlink" title="2.3 配置 Windows 换行符"></a>2.3 配置 Windows 换行符</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git config --global core.autocrlf <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><p>在 Windows 环境中推荐这样配置：</p><ul><li>提交时把 CRLF 转成 LF。</li><li>检出代码时把 LF 转成 Windows 常用的 CRLF。</li></ul><p>这可以减少不同系统之间因为换行符产生的大量无意义 diff。</p><h3 id="注：什么是-CRLF-和-LF？"><a href="#注：什么是-CRLF-和-LF？" class="headerlink" title="注：什么是 CRLF 和 LF？"></a>注：什么是 CRLF 和 LF？</h3><p>它们是文本文件中换行符的两种编码方式，因操作系统不同而有差异。</p><table><thead><tr><th align="left">名称</th><th align="left">缩写</th><th align="left">字符表示</th><th align="left">主要使用系统</th></tr></thead><tbody><tr><td align="left"><strong>CRLF</strong></td><td align="left"><code>\r\n</code></td><td align="left">回车 + 换行</td><td align="left">Windows</td></tr><tr><td align="left"><strong>LF</strong></td><td align="left"><code>\n</code></td><td align="left">仅换行</td><td align="left">Linux、macOS</td></tr></tbody></table><blockquote><p><strong>术语解释</strong>：</p><ul><li><code>CR</code> &#x3D; Carriage Return (回车 <code>\r</code>)</li><li><code>LF</code> &#x3D; Line Feed (换行 <code>\n</code>)</li></ul></blockquote><h3 id="为什么会影响到-Git-Diff？"><a href="#为什么会影响到-Git-Diff？" class="headerlink" title="为什么会影响到 Git Diff？"></a>为什么会影响到 Git Diff？</h3><p>跨平台协作时，换行符差异会导致大量无意义的 diff，主要原因如下：</p><h3 id="场景示例"><a href="#场景示例" class="headerlink" title="场景示例"></a>场景示例</h3><p>假设你在 Windows 上编写了一段 Python 代码，换行符为 CRLF：</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><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello</span>():   <span class="hljs-comment"># 换行符是 CRLF</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Hello&quot;</span>)   <span class="hljs-comment"># 换行符是 CRLF</span><br></code></pre></td></tr></table></figure><p>提交后，同事在 Linux 上查看该文件。如果 Git 不做任何转换，Linux 系统会多出无法识别的 \r 字符，可能显示为：</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><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello</span>():^M   <span class="hljs-comment"># ^M 代表多余的 \r</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Hello&quot;</span>)^M<br></code></pre></td></tr></table></figure><h3 id="Diff-噪音的产生过程"><a href="#Diff-噪音的产生过程" class="headerlink" title="Diff 噪音的产生过程"></a>Diff 噪音的产生过程</h3><p>1.你在 Windows 上修改了文件的第 2 行代码。</p><p>2.同事在 Linux 上打开并保存了该文件（未修改代码内容）。</p><p>Linux 编辑器自动将所有 CRLF 转换为 LF。</p><p>3.此时对比两个版本：</p><p>原文件：CRLF 结尾</p><p>新文件：LF 结尾</p><p>4.Git Diff 结果：显示整个文件的每一行都被修改了，尽管实际代码没有任何变化。</p><p>这就是因为换行符差异而产生的“无意义 diff”。</p><h3 id="解决方案：Git-的-core-autocrlf-配置"><a href="#解决方案：Git-的-core-autocrlf-配置" class="headerlink" title="解决方案：Git 的 core.autocrlf 配置"></a>解决方案：Git 的 core.autocrlf 配置</h3><p>在 Windows 环境中，推荐如下配置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git config --global core.autocrlf <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><h4 id="配置效果"><a href="#配置效果" class="headerlink" title="配置效果"></a>配置效果</h4><table><thead><tr><th>操作</th><th>行为</th><th>目的</th></tr></thead><tbody><tr><td>git commit(提交到仓库)</td><td>自动将 CRLF → LF</td><td>保证仓库内换行符统一</td></tr><tr><td>git checkout(检出到工作区)</td><td>自动将 LF → CRLF</td><td>适配 Windows 工具显示</td></tr></tbody></table><h4 id="最终结果"><a href="#最终结果" class="headerlink" title="最终结果"></a>最终结果</h4><ul><li>Git 仓库内：始终使用 LF，实现跨平台统一。</li><li>本地文件：显示为 CRLF，Windows 系统正常识别。</li><li>Diff 比较：基于仓库内的 LF 进行，不会因换行符差异产生噪音。</li></ul><h4 id="其他配置选项"><a href="#其他配置选项" class="headerlink" title="其他配置选项"></a>其他配置选项</h4><table><thead><tr><th>配置值</th><th>适用系统</th><th>行为说明</th></tr></thead><tbody><tr><td>true</td><td>Windows</td><td>提交转LF，检出转CRLF</td></tr><tr><td>input</td><td>Linux &#x2F; macOS</td><td>提交转 LF，检出保持 LF</td></tr><tr><td>false</td><td>特殊场景</td><td>不做任何转换（需团队统一约定）</td></tr></tbody></table><p><strong>补充</strong>：现代 Windows 开发环境（如 VS Code、Git Bash 等）大多已良好支持 LF 换行符。如果团队统一约定使用 LF，也可以设置为 false。但对于多数 Windows 用户，true 是最省心的默认配置。</p><h2 id="3-常用命令速查表"><a href="#3-常用命令速查表" class="headerlink" title="3. 常用命令速查表"></a>3. 常用命令速查表</h2><table><thead><tr><th>场景</th><th>命令</th><th>说明</th></tr></thead><tbody><tr><td>查看当前状态</td><td><code>git status</code></td><td>查看工作区和暂存区状态</td></tr><tr><td>精简查看状态</td><td><code>git status -s</code></td><td>短格式显示改动</td></tr><tr><td>查看当前分支</td><td><code>git branch --show-current</code></td><td>只输出当前分支名</td></tr><tr><td>查看工作区差异</td><td><code>git diff</code></td><td>比较工作区和暂存区</td></tr><tr><td>查看暂存区差异</td><td><code>git diff --cached</code></td><td>比较暂存区和最近一次提交</td></tr><tr><td>不分页查看差异</td><td><code>git --no-pager diff --cached</code></td><td>直接输出差异，不进入分页器</td></tr><tr><td>添加指定文件</td><td><code>git add &lt;文件&gt;</code></td><td>把指定文件加入暂存区</td></tr><tr><td>取消暂存</td><td><code>git restore --staged &lt;文件&gt;</code></td><td>从暂存区撤回，保留工作区修改</td></tr><tr><td>撤销工作区修改</td><td><code>git restore &lt;文件&gt;</code></td><td>丢弃未暂存的工作区修改</td></tr><tr><td>提交代码</td><td><code>git commit</code></td><td>打开编辑器填写完整提交信息</td></tr><tr><td>单行提交</td><td><code>git commit -m &quot;说明&quot;</code></td><td>直接在命令中写提交说明</td></tr><tr><td>查看日志</td><td><code>git log --oneline</code></td><td>一行展示提交历史</td></tr><tr><td>图形化日志</td><td><code>git log --graph --oneline --all</code></td><td>查看分支关系</td></tr><tr><td>查看本地分支</td><td><code>git branch</code></td><td>列出本地分支</td></tr><tr><td>查看全部分支</td><td><code>git branch -a</code></td><td>列出本地和远端分支</td></tr><tr><td>新建并切换分支</td><td><code>git switch -c &lt;分支名&gt;</code></td><td>推荐的新写法</td></tr><tr><td>切换分支</td><td><code>git switch &lt;分支名&gt;</code></td><td>切换到已有分支</td></tr><tr><td>获取远端更新</td><td><code>git fetch origin</code></td><td>只更新远端引用，不自动合并</td></tr><tr><td>拉取远端分支</td><td><code>git pull origin &lt;分支名&gt;</code></td><td>拉取并合并远端分支</td></tr><tr><td>推送分支</td><td><code>git push origin &lt;分支名&gt;</code></td><td>推送本地分支到远端</td></tr><tr><td>首次推送并关联</td><td><code>git push -u origin &lt;分支名&gt;</code></td><td>建立上游关系</td></tr><tr><td>临时保存修改</td><td><code>git stash</code></td><td>保存当前已跟踪文件的未提交改动</td></tr><tr><td>保存全部修改</td><td><code>git stash push -u -m &quot;说明&quot;</code></td><td>包含未跟踪文件</td></tr><tr><td>恢复 stash</td><td><code>git stash pop</code></td><td>恢复最近一次 stash 并删除记录</td></tr></tbody></table><h2 id="4-常用命令参数解释"><a href="#4-常用命令参数解释" class="headerlink" title="4. 常用命令参数解释"></a>4. 常用命令参数解释</h2><p>很多 Git 命令后面会带参数。参数的作用是改变命令行为，比如“用短格式输出”“显示全部分支”“把分支推送后建立上游关系”。</p><h3 id="4-1-怎么看命令里的占位符"><a href="#4-1-怎么看命令里的占位符" class="headerlink" title="4.1 怎么看命令里的占位符"></a>4.1 怎么看命令里的占位符</h3><p>文档里经常会写：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add &lt;文件&gt;<br>git switch &lt;分支名&gt;<br>git push origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>其中：</p><ul><li><code>&lt;文件&gt;</code>：表示替换成真实文件路径，不要把尖括号一起输入。</li><li><code>&lt;分支名&gt;</code>：表示替换成真实分支名。</li><li><code>&lt;提交ID&gt;</code>：表示替换成真实 commit hash。</li></ul><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add src/App.vue<br>git switch feat/export-select-all<br>git push origin feat/export-select-all<br></code></pre></td></tr></table></figure><h3 id="4-2-短参数和长参数"><a href="#4-2-短参数和长参数" class="headerlink" title="4.2 短参数和长参数"></a>4.2 短参数和长参数</h3><p>Git 中很多参数有短写和长写：</p><table><thead><tr><th>短参数</th><th>长参数</th><th>含义</th></tr></thead><tbody><tr><td><code>-s</code></td><td><code>--short</code></td><td>短格式输出</td></tr><tr><td><code>-a</code></td><td><code>--all</code></td><td>显示全部</td></tr><tr><td><code>-c</code></td><td><code>--create</code></td><td>创建</td></tr><tr><td><code>-m</code></td><td>无固定长参数</td><td>后面跟一段说明文字</td></tr><tr><td><code>-u</code></td><td><code>--set-upstream</code> &#x2F; <code>--include-untracked</code></td><td>含义取决于命令</td></tr></tbody></table><p>注意：同一个短参数在不同命令里可能含义不同。比如：</p><ul><li><code>git push -u origin &lt;分支名&gt;</code> 中的 <code>-u</code> 表示建立上游关系。</li><li><code>git stash push -u</code> 中的 <code>-u</code> 表示把未跟踪文件也保存进 stash。</li></ul><h3 id="4-3-状态查看相关参数"><a href="#4-3-状态查看相关参数" class="headerlink" title="4.3 状态查看相关参数"></a>4.3 状态查看相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status -s<br></code></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>-s</code>：<code>--short</code> 的简写，表示用短格式输出状态。</li></ul><p>适合场景：</p><ul><li>只想快速看哪些文件改了。</li><li>不想看 <code>git status</code> 的长说明文字。</li></ul><p>示例：</p><figure class="highlight text"><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><code class="hljs text"> M src/App.vue<br>A  README.md<br>?? temp.txt<br></code></pre></td></tr></table></figure><h3 id="4-4-差异查看相关参数"><a href="#4-4-差异查看相关参数" class="headerlink" title="4.4 差异查看相关参数"></a>4.4 差异查看相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git diff --cached<br></code></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>--cached</code>：查看暂存区和最近一次提交之间的差异。</li></ul><p>也就是说，它展示的是“下一次 commit 会提交什么”。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git --no-pager diff --cached<br></code></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>--no-pager</code>：Git 全局参数，表示不要进入分页器，直接把结果输出到终端。</li><li><code>diff</code>：真正执行的 Git 子命令。</li><li><code>--cached</code>：查看暂存区差异。</li></ul><p>适合场景：</p><ul><li>终端里只想直接看完 diff，不想按 <code>q</code> 退出分页器。</li><li>提交前快速检查暂存区。</li></ul><h3 id="4-5-暂存与恢复相关参数"><a href="#4-5-暂存与恢复相关参数" class="headerlink" title="4.5 暂存与恢复相关参数"></a>4.5 暂存与恢复相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore --staged &lt;文件&gt;<br></code></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>--staged</code>：表示操作暂存区，而不是工作区。</li></ul><p>作用：</p><ul><li>把文件从暂存区撤回工作区。</li><li>不会丢失文件内容。</li></ul><p>对比：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore &lt;文件&gt;<br></code></pre></td></tr></table></figure><p>这个命令没有 <code>--staged</code>，作用是撤销工作区修改，会丢弃未暂存内容。</p><h3 id="4-6-提交相关参数"><a href="#4-6-提交相关参数" class="headerlink" title="4.6 提交相关参数"></a>4.6 提交相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit -m <span class="hljs-string">&quot;说明&quot;</span><br></code></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>-m</code>：message 的意思，后面直接跟提交说明。</li></ul><p>适合场景：</p><ul><li>提交内容很简单，一句话能说清。</li></ul><p>不适合场景：</p><ul><li>提交内容较复杂，需要写 body 或 footer。</li></ul><p>复杂提交更推荐：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit<br></code></pre></td></tr></table></figure><p>这样会打开编辑器，可以写多行提交说明。</p><h3 id="4-7-日志相关参数"><a href="#4-7-日志相关参数" class="headerlink" title="4.7 日志相关参数"></a>4.7 日志相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">log</span> --oneline<br></code></pre></td></tr></table></figure><ul><li><code>--oneline</code>：每个 commit 只显示一行，包含短 hash 和标题。</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">log</span> --graph --oneline --all<br></code></pre></td></tr></table></figure><ul><li><code>--graph</code>：用字符图展示分支和合并关系。</li><li><code>--oneline</code>：一行展示一个提交。</li><li><code>--all</code>：显示所有分支的提交，不只看当前分支。</li></ul><p>适合场景：</p><ul><li>查看分支从哪里分出来。</li><li>查看是否产生 merge commit。</li><li>查看多个分支之间的历史关系。</li></ul><h3 id="4-8-分支相关参数"><a href="#4-8-分支相关参数" class="headerlink" title="4.8 分支相关参数"></a>4.8 分支相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git branch -a<br></code></pre></td></tr></table></figure><ul><li><code>-a</code>：<code>--all</code> 的简写，显示本地分支和远端分支。</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git branch --show-current<br></code></pre></td></tr></table></figure><ul><li><code>--show-current</code>：只输出当前分支名。</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch -c &lt;分支名&gt;<br></code></pre></td></tr></table></figure><ul><li><code>-c</code>：create 的意思，创建新分支并切换过去。</li></ul><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><code class="hljs bash">git branch -d &lt;分支名&gt;<br>git branch -D &lt;分支名&gt;<br></code></pre></td></tr></table></figure><ul><li><code>-d</code>：安全删除本地分支，如果分支未合并，Git 会阻止删除。</li><li><code>-D</code>：强制删除本地分支，即使分支未合并也会删除。</li></ul><p>注意：<code>-D</code> 风险更高，使用前确认分支内容已经合并、推送或不再需要。</p><h3 id="4-9-远端相关参数"><a href="#4-9-远端相关参数" class="headerlink" title="4.9 远端相关参数"></a>4.9 远端相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git remote -v<br></code></pre></td></tr></table></figure><ul><li><code>-v</code>：verbose 的意思，显示更详细的远端地址，包括 fetch 和 push 地址。</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><ul><li><code>-u</code>：设置上游分支，完整含义接近 <code>--set-upstream</code>。</li><li><code>origin</code>：远端仓库名，通常默认叫 <code>origin</code>。</li><li><code>&lt;分支名&gt;</code>：要推送到远端的分支。</li></ul><p>建立上游关系后，后续在同一分支通常只需要：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push<br></code></pre></td></tr></table></figure><h3 id="4-10-stash-相关参数"><a href="#4-10-stash-相关参数" class="headerlink" title="4.10 stash 相关参数"></a>4.10 stash 相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;说明&quot;</span><br></code></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>push</code>：创建一条新的 stash 记录。</li><li><code>-u</code>：include untracked，表示把未跟踪文件也保存进去。</li><li><code>-m</code>：message，给这条 stash 写说明，方便以后识别。</li></ul><p>常见子命令区别：</p><table><thead><tr><th>命令</th><th>含义</th></tr></thead><tbody><tr><td><code>git stash list</code></td><td>查看 stash 列表</td></tr><tr><td><code>git stash apply</code></td><td>恢复 stash，但保留 stash 记录</td></tr><tr><td><code>git stash pop</code></td><td>恢复 stash，并删除该 stash 记录</td></tr><tr><td><code>git stash drop stash@{0}</code></td><td>删除指定 stash</td></tr><tr><td><code>git stash clear</code></td><td>清空所有 stash</td></tr></tbody></table><p>注意：<code>git stash clear</code> 风险较高，会清空全部 stash 记录。</p><h3 id="4-11-reset-相关参数"><a href="#4-11-reset-相关参数" class="headerlink" title="4.11 reset 相关参数"></a>4.11 reset 相关参数</h3><p><code>git reset</code> 主要影响三个位置：</p><ul><li><code>HEAD</code>：当前提交指针。</li><li>暂存区：已经 <code>git add</code> 的内容。</li><li>工作区：你正在编辑的文件。</li></ul><table><thead><tr><th>命令</th><th>HEAD</th><th>暂存区</th><th>工作区</th><th>风险</th></tr></thead><tbody><tr><td><code>git reset --soft HEAD^</code></td><td>回退</td><td>保留</td><td>保留</td><td>较低</td></tr><tr><td><code>git reset --mixed HEAD^</code></td><td>回退</td><td>取消暂存</td><td>保留</td><td>中等</td></tr><tr><td><code>git reset --hard HEAD^</code></td><td>回退</td><td>丢弃</td><td>丢弃</td><td>高</td></tr></tbody></table><p>参数说明：</p><ul><li><code>--soft</code>：只移动 HEAD，保留暂存区和工作区。</li><li><code>--mixed</code>：移动 HEAD，并重置暂存区，工作区内容保留。</li><li><code>--hard</code>：移动 HEAD，重置暂存区和工作区，会丢弃改动。</li><li><code>HEAD^</code>：表示当前提交的上一个提交。</li></ul><p>注意：<code>--hard</code> 是高风险参数，不确定时先 <code>git stash push -u -m &quot;backup-before-reset&quot;</code>。</p><h3 id="4-12-worktree-相关参数"><a href="#4-12-worktree-相关参数" class="headerlink" title="4.12 worktree 相关参数"></a>4.12 worktree 相关参数</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree add &lt;目录&gt; -b &lt;分支名&gt; master<br></code></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>add</code>：新增一个工作区。</li><li><code>&lt;目录&gt;</code>：新工作区要放到哪里。</li><li><code>-b &lt;分支名&gt;</code>：创建一个新分支。</li><li><code>master</code>：新分支从哪个基准分支拉出来。</li></ul><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree add .worktrees/export-select-all -b feat/export-select-all master<br></code></pre></td></tr></table></figure><p>含义：</p><ul><li>新建目录 <code>.worktrees/export-select-all</code>。</li><li>创建并检出分支 <code>feat/export-select-all</code>。</li><li>该分支基于 <code>master</code>。</li></ul><h3 id="4-13-高风险参数提醒"><a href="#4-13-高风险参数提醒" class="headerlink" title="4.13 高风险参数提醒"></a>4.13 高风险参数提醒</h3><table><thead><tr><th>参数或命令</th><th>风险</th><th>建议</th></tr></thead><tbody><tr><td><code>--hard</code></td><td>丢弃工作区和暂存区改动</td><td>执行前先 stash 或确认不需要</td></tr><tr><td><code>-D</code></td><td>强制删除本地分支</td><td>确认分支已合并或不再需要</td></tr><tr><td><code>git stash clear</code></td><td>清空所有 stash</td><td>优先用 <code>git stash drop stash@{n}</code></td></tr><tr><td><code>git push --force</code></td><td>覆盖远端历史，影响他人</td><td>公共分支不要随便使用</td></tr><tr><td><code>git restore &lt;文件&gt;</code></td><td>丢弃未暂存修改</td><td>不确定时先备份或 stash</td></tr></tbody></table><h2 id="5-状态与差异查看"><a href="#5-状态与差异查看" class="headerlink" title="5. 状态与差异查看"></a>5. 状态与差异查看</h2><h3 id="5-1-查看当前状态"><a href="#5-1-查看当前状态" class="headerlink" title="5.1 查看当前状态"></a>5.1 查看当前状态</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status<br></code></pre></td></tr></table></figure><p>用于查看：</p><ul><li>当前在哪个分支。</li><li>哪些文件被修改。</li><li>哪些文件已经暂存。</li><li>是否有未跟踪文件。</li></ul><p>短格式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status -s<br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>status</code>：查看当前仓库状态。</li><li><code>-s</code>：用 short 短格式显示，只保留关键状态码和文件名。</li></ul><p>常见状态示例：</p><figure class="highlight text"><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><code class="hljs text"> M app.js<br>A  readme.md<br>?? temp.txt<br></code></pre></td></tr></table></figure><p>含义：</p><ul><li><code> M</code>：文件在工作区被修改，但未暂存。</li><li><code>A </code>：新文件已经暂存。</li><li><code>??</code>：未跟踪文件。</li></ul><h3 id="5-2-查看工作区差异"><a href="#5-2-查看工作区差异" class="headerlink" title="5.2 查看工作区差异"></a>5.2 查看工作区差异</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git diff<br></code></pre></td></tr></table></figure><p>查看工作区相对暂存区的改动。也就是：你已经改了但还没有 <code>git add</code> 的内容。</p><h3 id="5-3-查看暂存区差异"><a href="#5-3-查看暂存区差异" class="headerlink" title="5.3 查看暂存区差异"></a>5.3 查看暂存区差异</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git diff --cached<br></code></pre></td></tr></table></figure><p>查看暂存区相对最近一次提交的改动。也就是：下一次 commit 会提交什么。</p><p>如果不想进入分页器，可以用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git --no-pager diff --cached<br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>--no-pager</code>：不使用分页器，直接输出结果。</li><li><code>diff</code>：查看差异。</li><li><code>--cached</code>：查看暂存区里的差异。</li></ul><h2 id="6-暂存与提交"><a href="#6-暂存与提交" class="headerlink" title="6. 暂存与提交"></a>6. 暂存与提交</h2><h3 id="6-1-添加指定文件到暂存区"><a href="#6-1-添加指定文件到暂存区" class="headerlink" title="6.1 添加指定文件到暂存区"></a>6.1 添加指定文件到暂存区</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add &lt;文件&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add src/App.vue<br>git add package.json<br></code></pre></td></tr></table></figure><p>建议优先按文件添加，避免不小心把无关改动一起提交。</p><h3 id="6-2-不推荐默认使用-git-add"><a href="#6-2-不推荐默认使用-git-add" class="headerlink" title="6.2 不推荐默认使用 git add ."></a>6.2 不推荐默认使用 git add .</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add .<br></code></pre></td></tr></table></figure><p>这个命令会把当前目录下所有改动加入暂存区。它很方便，但容易把调试文件、临时文件、无关修改一起提交。</p><p>更推荐：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status<br>git add &lt;文件1&gt;<br>git add &lt;文件2&gt;<br>git --no-pager diff --cached<br>git commit<br></code></pre></td></tr></table></figure><h3 id="6-3-取消暂存"><a href="#6-3-取消暂存" class="headerlink" title="6.3 取消暂存"></a>6.3 取消暂存</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore --staged &lt;文件&gt;<br></code></pre></td></tr></table></figure><p>这个命令只会把文件从暂存区撤回工作区，不会丢失修改。</p><h3 id="6-4-提交代码"><a href="#6-4-提交代码" class="headerlink" title="6.4 提交代码"></a>6.4 提交代码</h3><p>打开编辑器填写完整提交信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit<br></code></pre></td></tr></table></figure><p>简单提交：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit -m <span class="hljs-string">&quot;docs: update git notes&quot;</span><br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>commit</code>：把暂存区内容提交到本地仓库。</li><li><code>-m</code>：message，后面跟本次提交说明。</li><li><code>&quot;docs: update git notes&quot;</code>：提交说明内容。</li></ul><p>如果提交内容较多，建议使用 <code>git commit</code> 打开编辑器，写清楚标题、正文和备注。</p><h2 id="7-查看提交历史"><a href="#7-查看提交历史" class="headerlink" title="7. 查看提交历史"></a>7. 查看提交历史</h2><h3 id="7-1-简洁日志"><a href="#7-1-简洁日志" class="headerlink" title="7.1 简洁日志"></a>7.1 简洁日志</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">log</span> --oneline<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">3f2a1b0 docs: update git notes<br>9c8d7e6 fix: correct login redirect<br></code></pre></td></tr></table></figure><h3 id="7-2-图形化查看分支历史"><a href="#7-2-图形化查看分支历史" class="headerlink" title="7.2 图形化查看分支历史"></a>7.2 图形化查看分支历史</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">log</span> --graph --oneline --all<br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>--graph</code>：显示分支和合并的字符图。</li><li><code>--oneline</code>：每个提交压缩成一行。</li><li><code>--all</code>：显示所有分支的历史。</li></ul><p>适合查看：</p><ul><li>分支从哪里拉出来。</li><li>merge commit 在哪里。</li><li>多个分支之间的提交关系。</li></ul><h3 id="7-3-查看两个提交之间的差异"><a href="#7-3-查看两个提交之间的差异" class="headerlink" title="7.3 查看两个提交之间的差异"></a>7.3 查看两个提交之间的差异</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git diff &lt;提交ID1&gt; &lt;提交ID2&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git diff 3f2a1b0 9c8d7e6<br></code></pre></td></tr></table></figure><h2 id="8-分支命令"><a href="#8-分支命令" class="headerlink" title="8. 分支命令"></a>8. 分支命令</h2><h3 id="8-1-查看分支"><a href="#8-1-查看分支" class="headerlink" title="8.1 查看分支"></a>8.1 查看分支</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git branch<br></code></pre></td></tr></table></figure><p>查看本地分支。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git branch -a<br></code></pre></td></tr></table></figure><p>查看本地和远端分支。</p><p>参数拆解：</p><ul><li><code>branch</code>：查看或管理分支。</li><li><code>-a</code>：all，显示全部分支，包括远端跟踪分支。</li></ul><h3 id="8-2-查看当前分支名"><a href="#8-2-查看当前分支名" class="headerlink" title="8.2 查看当前分支名"></a>8.2 查看当前分支名</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git branch --show-current<br></code></pre></td></tr></table></figure><h3 id="8-3-新建并切换分支"><a href="#8-3-新建并切换分支" class="headerlink" title="8.3 新建并切换分支"></a>8.3 新建并切换分支</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch -c &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>switch</code>：切换分支。</li><li><code>-c</code>：create，创建新分支。</li><li><code>&lt;分支名&gt;</code>：新分支的名字，使用时替换成真实分支名。</li></ul><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch -c feat/export-select-all<br></code></pre></td></tr></table></figure><h3 id="8-4-切换到已有分支"><a href="#8-4-切换到已有分支" class="headerlink" title="8.4 切换到已有分支"></a>8.4 切换到已有分支</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch master<br></code></pre></td></tr></table></figure><h3 id="8-5-删除本地分支"><a href="#8-5-删除本地分支" class="headerlink" title="8.5 删除本地分支"></a>8.5 删除本地分支</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git branch -d &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>安全删除：如果分支还没有被合并，Git 会阻止删除。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git branch -D &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>强制删除：会直接删除本地分支。使用前确认分支内容已经不需要，或已经推送到远端。</p><h2 id="9-远端仓库命令"><a href="#9-远端仓库命令" class="headerlink" title="9. 远端仓库命令"></a>9. 远端仓库命令</h2><h3 id="9-1-查看远端地址"><a href="#9-1-查看远端地址" class="headerlink" title="9.1 查看远端地址"></a>9.1 查看远端地址</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git remote -v<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">origin  git@gitlab.example.com:team/project.git (fetch)<br>origin  git@gitlab.example.com:team/project.git (push)<br></code></pre></td></tr></table></figure><h3 id="9-2-获取远端最新信息"><a href="#9-2-获取远端最新信息" class="headerlink" title="9.2 获取远端最新信息"></a>9.2 获取远端最新信息</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch origin<br></code></pre></td></tr></table></figure><p>这个命令只更新远端引用，例如 <code>origin/master</code>，不会自动修改当前工作区代码。</p><h3 id="9-3-拉取远端分支"><a href="#9-3-拉取远端分支" class="headerlink" title="9.3 拉取远端分支"></a>9.3 拉取远端分支</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git pull origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git pull origin master<br></code></pre></td></tr></table></figure><p>它相当于：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch origin<br>git merge origin/master<br></code></pre></td></tr></table></figure><h3 id="9-4-推送分支到远端"><a href="#9-4-推送分支到远端" class="headerlink" title="9.4 推送分支到远端"></a>9.4 推送分支到远端</h3><p>首次推送：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>push</code>：把本地提交推送到远端。</li><li><code>-u</code>：建立本地分支和远端分支的上游关系。</li><li><code>origin</code>：远端仓库名。</li><li><code>&lt;分支名&gt;</code>：要推送的分支名。</li></ul><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin feat/export-select-all<br></code></pre></td></tr></table></figure><p><code>-u</code> 表示建立本地分支和远端分支的上游关系。建立后，后续通常直接：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push<br></code></pre></td></tr></table></figure><h3 id="9-5-GitLab-页面看不到本地分支的原因"><a href="#9-5-GitLab-页面看不到本地分支的原因" class="headerlink" title="9.5 GitLab 页面看不到本地分支的原因"></a>9.5 GitLab 页面看不到本地分支的原因</h3><p>GitLab 只能看到远端分支，看不到你电脑上的本地分支。</p><p>如果你本地新建了分支，但 GitLab 的 source branch 下拉框里看不到，需要先推送：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><h2 id="10-Stash-临时存档"><a href="#10-Stash-临时存档" class="headerlink" title="10. Stash 临时存档"></a>10. Stash 临时存档</h2><h3 id="10-1-stash-适合什么场景"><a href="#10-1-stash-适合什么场景" class="headerlink" title="10.1 stash 适合什么场景"></a>10.1 stash 适合什么场景</h3><p>stash 用于临时保存未提交改动，适合：</p><ul><li>当前功能做到一半，需要切换分支处理别的任务。</li><li>当前改动还不能提交，但又不想丢。</li><li>本地分支上混入多个功能，需要先保存再拆分。</li></ul><h3 id="10-2-保存当前修改"><a href="#10-2-保存当前修改" class="headerlink" title="10.2 保存当前修改"></a>10.2 保存当前修改</h3><p>只保存已跟踪文件的修改：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash<br></code></pre></td></tr></table></figure><p>保存所有修改，包括未跟踪文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;临时说明&quot;</span><br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>stash</code>：临时保存未提交改动。</li><li><code>push</code>：创建一条新的 stash 记录。</li><li><code>-u</code>：把未跟踪文件也一起保存。</li><li><code>-m</code>：给 stash 写说明。</li></ul><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;before-switch-branch&quot;</span><br></code></pre></td></tr></table></figure><h3 id="10-3-查看-stash-记录"><a href="#10-3-查看-stash-记录" class="headerlink" title="10.3 查看 stash 记录"></a>10.3 查看 stash 记录</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash list<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">stash@&#123;0&#125;: On feat/export-select-all: before-switch-branch<br>stash@&#123;1&#125;: On master: temp-save-before-splitting-features<br></code></pre></td></tr></table></figure><h3 id="10-4-恢复-stash"><a href="#10-4-恢复-stash" class="headerlink" title="10.4 恢复 stash"></a>10.4 恢复 stash</h3><p>恢复最近一次 stash，但保留 stash 记录：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash apply<br></code></pre></td></tr></table></figure><p>恢复最近一次 stash，并删除该 stash 记录：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash pop<br></code></pre></td></tr></table></figure><p>恢复指定 stash：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash apply stash@&#123;1&#125;<br></code></pre></td></tr></table></figure><h3 id="10-5-从-stash-中恢复指定文件"><a href="#10-5-从-stash-中恢复指定文件" class="headerlink" title="10.5 从 stash 中恢复指定文件"></a>10.5 从 stash 中恢复指定文件</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git checkout stash@&#123;0&#125; -- &lt;文件路径&gt;<br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git checkout stash@&#123;0&#125; -- src/App.vue<br></code></pre></td></tr></table></figure><h3 id="10-6-删除-stash"><a href="#10-6-删除-stash" class="headerlink" title="10.6 删除 stash"></a>10.6 删除 stash</h3><p>删除指定 stash：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash drop stash@&#123;0&#125;<br></code></pre></td></tr></table></figure><p>清空全部 stash：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git stash clear<br></code></pre></td></tr></table></figure><p>注意：<code>git stash clear</code> 会清空所有 stash 记录，操作前一定确认不再需要这些临时改动。</p><h2 id="11-恢复与回退"><a href="#11-恢复与回退" class="headerlink" title="11. 恢复与回退"></a>11. 恢复与回退</h2><h3 id="11-1-撤销工作区修改"><a href="#11-1-撤销工作区修改" class="headerlink" title="11.1 撤销工作区修改"></a>11.1 撤销工作区修改</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore &lt;文件&gt;<br></code></pre></td></tr></table></figure><p>作用：丢弃指定文件在工作区中的未暂存修改。</p><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore src/App.vue<br></code></pre></td></tr></table></figure><p>注意：这个命令会丢弃修改，执行前确认文件内容不需要保留。</p><h3 id="11-2-取消暂存"><a href="#11-2-取消暂存" class="headerlink" title="11.2 取消暂存"></a>11.2 取消暂存</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git restore --staged &lt;文件&gt;<br></code></pre></td></tr></table></figure><p>作用：把文件从暂存区撤回工作区，修改还在。</p><h3 id="11-3-回退上一次提交但保留暂存区"><a href="#11-3-回退上一次提交但保留暂存区" class="headerlink" title="11.3 回退上一次提交但保留暂存区"></a>11.3 回退上一次提交但保留暂存区</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --soft HEAD^<br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>reset</code>：移动当前分支指针，并按参数决定是否影响暂存区和工作区。</li><li><code>--soft</code>：只回退提交记录，保留暂存区和工作区。</li><li><code>HEAD^</code>：当前提交的上一个提交。</li></ul><p>适合：刚提交完，发现 commit message 写错，或者想重新组织提交。</p><h3 id="11-4-回退上一次提交并取消暂存"><a href="#11-4-回退上一次提交并取消暂存" class="headerlink" title="11.4 回退上一次提交并取消暂存"></a>11.4 回退上一次提交并取消暂存</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --mixed HEAD^<br></code></pre></td></tr></table></figure><p>适合：想撤销最近一次提交，把改动重新放回工作区整理。</p><h3 id="11-5-强制回退并丢弃修改"><a href="#11-5-强制回退并丢弃修改" class="headerlink" title="11.5 强制回退并丢弃修改"></a>11.5 强制回退并丢弃修改</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --hard HEAD^<br></code></pre></td></tr></table></figure><p>作用：回到上一个提交，并丢弃工作区和暂存区内容。</p><p>注意：这是高风险命令。除非你非常确定这些改动不需要，否则不要随便使用。</p><h2 id="12-Worktree-多工作区"><a href="#12-Worktree-多工作区" class="headerlink" title="12. Worktree 多工作区"></a>12. Worktree 多工作区</h2><h3 id="12-1-worktree-是什么"><a href="#12-1-worktree-是什么" class="headerlink" title="12.1 worktree 是什么"></a>12.1 worktree 是什么</h3><p>worktree 可以让一个 Git 仓库同时拥有多个工作目录。每个工作目录可以对应不同分支。</p><p>适合场景：</p><ul><li>同时开发多个功能。</li><li>一个功能还没做完，又要处理另一个分支的问题。</li><li>不想频繁 stash 和切换分支。</li></ul><h3 id="12-2-新建-worktree"><a href="#12-2-新建-worktree" class="headerlink" title="12.2 新建 worktree"></a>12.2 新建 worktree</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree add &lt;目录&gt; -b &lt;分支名&gt; master<br></code></pre></td></tr></table></figure><p>参数拆解：</p><ul><li><code>worktree add</code>：新增一个独立工作区。</li><li><code>&lt;目录&gt;</code>：新工作区的目录。</li><li><code>-b &lt;分支名&gt;</code>：创建新分支并让新工作区使用它。</li><li><code>master</code>：新分支基于哪个分支创建。</li></ul><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree add .worktrees/export-select-all -b feat/export-select-all master<br></code></pre></td></tr></table></figure><p>含义：</p><ul><li><code>.worktrees/export-select-all</code>：新工作区目录。</li><li><code>-b feat/export-select-all</code>：创建新分支。</li><li><code>master</code>：从 master 拉出新分支。</li></ul><h3 id="12-3-查看-worktree"><a href="#12-3-查看-worktree" class="headerlink" title="12.3 查看 worktree"></a>12.3 查看 worktree</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git worktree list<br></code></pre></td></tr></table></figure><p>输出示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">C:/Users/lenovo/Desktop/BPMAX/hpt-frontend                                [master]<br>C:/Users/lenovo/Desktop/BPMAX/hpt-frontend/.worktrees/export-select-all   [feat/export-select-all]<br></code></pre></td></tr></table></figure><p>注意：你在哪个目录启动项目，运行的就是哪个目录对应分支的代码。</p><h2 id="13-日常操作示例"><a href="#13-日常操作示例" class="headerlink" title="13. 日常操作示例"></a>13. 日常操作示例</h2><h3 id="13-1-第一次配置-Git"><a href="#13-1-第一次配置-Git" class="headerlink" title="13.1 第一次配置 Git"></a>13.1 第一次配置 Git</h3><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><code class="hljs bash">git config --global user.name <span class="hljs-string">&quot;你的名字&quot;</span><br>git config --global user.email <span class="hljs-string">&quot;你的邮箱&quot;</span><br>git config --global core.autocrlf <span class="hljs-literal">true</span><br>git config --global --list<br></code></pre></td></tr></table></figure><h3 id="13-2-新建分支开发"><a href="#13-2-新建分支开发" class="headerlink" title="13.2 新建分支开发"></a>13.2 新建分支开发</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch master<br>git pull origin master<br>git switch -c feat/demo-feature<br></code></pre></td></tr></table></figure><h3 id="13-3-分文件提交"><a href="#13-3-分文件提交" class="headerlink" title="13.3 分文件提交"></a>13.3 分文件提交</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status<br>git add src/App.vue<br>git add src/components/Demo.vue<br>git --no-pager diff --cached<br>git commit<br></code></pre></td></tr></table></figure><h3 id="13-4-查看提交历史"><a href="#13-4-查看提交历史" class="headerlink" title="13.4 查看提交历史"></a>13.4 查看提交历史</h3><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><code class="hljs bash">git <span class="hljs-built_in">log</span> --oneline<br>git <span class="hljs-built_in">log</span> --graph --oneline --all<br></code></pre></td></tr></table></figure><h3 id="13-5-临时保存未完成代码"><a href="#13-5-临时保存未完成代码" class="headerlink" title="13.5 临时保存未完成代码"></a>13.5 临时保存未完成代码</h3><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><code class="hljs bash">git stash push -u -m <span class="hljs-string">&quot;before-fix-urgent-bug&quot;</span><br>git switch hotfix/urgent-bug<br></code></pre></td></tr></table></figure><p>恢复：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git switch feat/demo-feature<br>git stash pop<br></code></pre></td></tr></table></figure><h3 id="13-6-本地分支推送到-GitLab"><a href="#13-6-本地分支推送到-GitLab" class="headerlink" title="13.6 本地分支推送到 GitLab"></a>13.6 本地分支推送到 GitLab</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin feat/demo-feature<br></code></pre></td></tr></table></figure><p>后续同一个分支继续提交后：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push<br></code></pre></td></tr></table></figure><h3 id="13-7-从-stash-中恢复指定文件"><a href="#13-7-从-stash-中恢复指定文件" class="headerlink" title="13.7 从 stash 中恢复指定文件"></a>13.7 从 stash 中恢复指定文件</h3><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><code class="hljs bash">git stash list<br>git checkout stash@&#123;0&#125; -- src/App.vue<br></code></pre></td></tr></table></figure><h3 id="13-8-使用-worktree-同时开发多个功能"><a href="#13-8-使用-worktree-同时开发多个功能" class="headerlink" title="13.8 使用 worktree 同时开发多个功能"></a>13.8 使用 worktree 同时开发多个功能</h3><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><code class="hljs bash">git switch master<br>git pull origin master<br>git worktree add .worktrees/feature-a -b feat/feature-a master<br>git worktree add .worktrees/feature-b -b feat/feature-b master<br></code></pre></td></tr></table></figure><p>之后分别进入不同目录开发：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-built_in">cd</span> C:\Users\lenovo\Desktop\BPMAX\hpt<span class="hljs-literal">-frontend</span>\.worktrees\feature<span class="hljs-literal">-a</span><br></code></pre></td></tr></table></figure><h2 id="14-命令行常见现象"><a href="#14-命令行常见现象" class="headerlink" title="14. 命令行常见现象"></a>14. 命令行常见现象</h2><h3 id="14-1-git-diff-后终端像卡住了"><a href="#14-1-git-diff-后终端像卡住了" class="headerlink" title="14.1 git diff 后终端像卡住了"></a>14.1 git diff 后终端像卡住了</h3><p>大概率是进入了分页器。按：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">q<br></code></pre></td></tr></table></figure><p>即可退出。</p><p>如果不想进入分页器，可以使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git --no-pager diff<br>git --no-pager diff --cached<br></code></pre></td></tr></table></figure><h3 id="14-2-git-commit-打开-vim-后怎么写"><a href="#14-2-git-commit-打开-vim-后怎么写" class="headerlink" title="14.2 git commit 打开 vim 后怎么写"></a>14.2 git commit 打开 vim 后怎么写</h3><p>提交信息要写在最上方，写在 <code>#</code> 注释行上面。</p><p>如果打开的是 vim：</p><ol><li>按 <code>gg</code> 跳到顶部。</li><li>按 <code>O</code> 在顶部上方插入一行。</li><li>输入 commit message。</li><li>按 <code>Esc</code>。</li><li>输入 <code>:wq</code>。</li><li>按回车保存退出。</li></ol><p>如果不想进入编辑器，可以使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git commit -m <span class="hljs-string">&quot;docs: update git notes&quot;</span><br></code></pre></td></tr></table></figure><h3 id="14-3-出现-E163-只有一个文件可编辑"><a href="#14-3-出现-E163-只有一个文件可编辑" class="headerlink" title="14.3 出现 E163: 只有一个文件可编辑"></a>14.3 出现 E163: 只有一个文件可编辑</h3><p>这通常只是 vim 的提示，不代表提交失败。</p><p>处理方式：</p><ol><li>按 <code>Enter</code>。</li><li>继续输入 <code>:wq</code>。</li><li>按回车保存退出。</li></ol><h3 id="14-4-GitLab-看不到本地分支"><a href="#14-4-GitLab-看不到本地分支" class="headerlink" title="14.4 GitLab 看不到本地分支"></a>14.4 GitLab 看不到本地分支</h3><p>原因：分支只存在你的电脑上，还没有推送到远端。</p><p>解决：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git push -u origin &lt;分支名&gt;<br></code></pre></td></tr></table></figure><p>推送后，GitLab 的 source branch 下拉框才能看到该分支。</p><h2 id="15-学习建议"><a href="#15-学习建议" class="headerlink" title="15. 学习建议"></a>15. 学习建议</h2><p>新手可以按这个顺序掌握 Git：</p><ol><li>先理解工作区、暂存区、本地仓库、远端仓库。</li><li>熟练使用 <code>git status</code> 和 <code>git diff</code>，知道自己改了什么。</li><li>养成按文件 <code>git add</code> 的习惯。</li><li>提交前用 <code>git --no-pager diff --cached</code> 检查暂存区。</li><li>学会新建分支、切换分支、推送分支。</li><li>学会用 <code>stash</code> 临时保存未完成代码。</li><li>再学习 <code>merge</code>、冲突处理和 worktree。</li></ol><p>日常开发最常用的一组命令是：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash">git status<br>git diff<br>git add &lt;文件&gt;<br>git --no-pager diff --cached<br>git commit<br>git push<br></code></pre></td></tr></table></figure><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1776182215026" data-twikoo-path="article_1776182215026"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/13/git-chang-yong-ming-ling-zong-jie-bi-ji/</id>
    <link href="https://aoiblog.top/2026/04/13/git-chang-yong-ming-ling-zong-jie-bi-ji/"/>
    <published>2026-04-13T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>Git常用命令总结笔记</h2>
# Git 命令教学笔记

<p>这份笔记偏向 Git 命令的教学和日常查阅，重点解决三个问题：</p>
<ul>
<li>Git 的基本概念是什么。</li>
<li>常用命令怎么写、什么时候用。</li>
<li>日常开发中遇到常见命]]>
    </summary>
    <title>Git常用命令总结笔记</title>
    <updated>2026-04-16T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>Markdown 语法笔记</h2>## 1. Markdown 是什么<p>Markdown 是一种<strong>轻量级标记语言</strong>，用简洁的纯文本语法来表示标题、列表、引用、代码、表格等结构，常用于：</p><ul><li>文档编写</li><li>README 说明</li><li>博客写作</li><li>笔记整理</li><li>技术文档</li><li>评论区排版</li></ul><p>它的核心特点是：</p><ul><li>语法简单</li><li>可读性强</li><li>纯文本易保存</li><li>可转换为 HTML、PDF 等格式</li></ul><hr><h2 id="2-标题"><a href="#2-标题" class="headerlink" title="2. 标题"></a>2. 标题</h2><p>使用 <code>#</code> 表示标题，<code>#</code> 的数量对应标题级别。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs markdown"><span class="hljs-section"># 一级标题</span><br><span class="hljs-section">## 二级标题</span><br><span class="hljs-section">### 三级标题</span><br><span class="hljs-section">#### 四级标题</span><br><span class="hljs-section">##### 五级标题</span><br><span class="hljs-section">###### 六级标题</span><br></code></pre></td></tr></table></figure><p>效果：</p><h1 id="一级标题"><a href="#一级标题" class="headerlink" title="一级标题"></a>一级标题</h1><h2 id="二级标题"><a href="#二级标题" class="headerlink" title="二级标题"></a>二级标题</h2><h3 id="三级标题"><a href="#三级标题" class="headerlink" title="三级标题"></a>三级标题</h3><h4 id="四级标题"><a href="#四级标题" class="headerlink" title="四级标题"></a>四级标题</h4><h5 id="五级标题"><a href="#五级标题" class="headerlink" title="五级标题"></a>五级标题</h5><h6 id="六级标题"><a href="#六级标题" class="headerlink" title="六级标题"></a>六级标题</h6><p>注意：</p><ul><li><code>#</code> 后面通常加一个空格</li><li>最多支持六级标题</li></ul><hr><h2 id="3-段落与换行"><a href="#3-段落与换行" class="headerlink" title="3. 段落与换行"></a>3. 段落与换行</h2><h3 id="3-1-段落"><a href="#3-1-段落" class="headerlink" title="3.1 段落"></a>3.1 段落</h3><p>直接书写文字即可，一个自然段之间通常空一行。</p><figure class="highlight markdown"><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><code class="hljs markdown">这是第一段。<br><br>这是第二段。<br></code></pre></td></tr></table></figure><h3 id="3-2-换行"><a href="#3-2-换行" class="headerlink" title="3.2 换行"></a>3.2 换行</h3><p>Markdown 中直接回车通常<strong>不会立即换行</strong>。常见换行方式有：</p><h4 id="方法一：行尾加两个空格"><a href="#方法一：行尾加两个空格" class="headerlink" title="方法一：行尾加两个空格"></a>方法一：行尾加两个空格</h4><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown">第一行  <br>第二行<br></code></pre></td></tr></table></figure><h4 id="方法二：使用-HTML-的"><a href="#方法二：使用-HTML-的" class="headerlink" title="方法二：使用 HTML 的 &lt;br&gt;"></a>方法二：使用 HTML 的 <code>&lt;br&gt;</code></h4><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown">第一行<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span></span><br>第二行<br></code></pre></td></tr></table></figure><hr><h2 id="4-强调语法"><a href="#4-强调语法" class="headerlink" title="4. 强调语法"></a>4. 强调语法</h2><h3 id="4-1-斜体"><a href="#4-1-斜体" class="headerlink" title="4.1 斜体"></a>4.1 斜体</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-emphasis">*斜体*</span><br><span class="hljs-emphasis">_斜体_</span><br></code></pre></td></tr></table></figure><p>效果：</p><p><em>斜体</em></p><h3 id="4-2-粗体"><a href="#4-2-粗体" class="headerlink" title="4.2 粗体"></a>4.2 粗体</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-strong">**粗体**</span><br><span class="hljs-strong">__粗体__</span><br></code></pre></td></tr></table></figure><p>效果：</p><p><strong>粗体</strong></p><h3 id="4-3-粗斜体"><a href="#4-3-粗斜体" class="headerlink" title="4.3 粗斜体"></a>4.3 粗斜体</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-strong">**<span class="hljs-emphasis">*粗斜体*</span>**</span><br><span class="hljs-strong">__<span class="hljs-emphasis">_粗斜体_</span>__</span><br></code></pre></td></tr></table></figure><p>效果：</p><p><em><strong>粗斜体</strong></em></p><h3 id="4-4-删除线"><a href="#4-4-删除线" class="headerlink" title="4.4 删除线"></a>4.4 删除线</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">~~删除线~~<br></code></pre></td></tr></table></figure><p>效果：</p><p><del>删除线</del></p><h3 id="4-5-高亮"><a href="#4-5-高亮" class="headerlink" title="4.5 高亮"></a>4.5 高亮</h3><p>部分 Markdown 平台支持：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">==高亮==<br></code></pre></td></tr></table></figure><p>注意：<code>==高亮==</code> <strong>不是所有平台都支持</strong>。</p><hr><h2 id="5-引用"><a href="#5-引用" class="headerlink" title="5. 引用"></a>5. 引用</h2><p>使用 <code>&gt;</code> 表示引用。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-quote">&gt; 这是一段引用文字。</span><br></code></pre></td></tr></table></figure><p>效果：</p><blockquote><p>这是一段引用文字。</p></blockquote><p>多级引用：</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-quote">&gt; 一级引用</span><br>&gt;&gt; 二级引用<br>&gt;&gt;&gt; 三级引用<br></code></pre></td></tr></table></figure><p>效果：</p><blockquote><p>一级引用</p><blockquote><p>二级引用</p><blockquote><p>三级引用</p></blockquote></blockquote></blockquote><p>引用中也可以嵌套其他语法：</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-quote">&gt; ## 引用中的标题</span><br><span class="hljs-quote">&gt; - 列表项</span><br><span class="hljs-quote">&gt; <span class="hljs-strong">**加粗内容**</span></span><br></code></pre></td></tr></table></figure><hr><h2 id="6-列表"><a href="#6-列表" class="headerlink" title="6. 列表"></a>6. 列表</h2><h3 id="6-1-无序列表"><a href="#6-1-无序列表" class="headerlink" title="6.1 无序列表"></a>6.1 无序列表</h3><p>使用 <code>-</code>、<code>*</code> 或 <code>+</code>。</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-bullet">-</span> 苹果<br><span class="hljs-bullet">-</span> 香蕉<br><span class="hljs-bullet">-</span> 橙子<br></code></pre></td></tr></table></figure><p>或</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-bullet">*</span> 苹果<br><span class="hljs-bullet">*</span> 香蕉<br><span class="hljs-bullet">*</span> 橙子<br></code></pre></td></tr></table></figure><p>或</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-bullet">+</span> 苹果<br><span class="hljs-bullet">+</span> 香蕉<br><span class="hljs-bullet">+</span> 橙子<br></code></pre></td></tr></table></figure><p>效果：</p><ul><li>苹果</li><li>香蕉</li><li>橙子</li></ul><p>注意：</p><ul><li>同一层级尽量统一符号</li><li>符号后加空格</li></ul><h3 id="6-2-有序列表"><a href="#6-2-有序列表" class="headerlink" title="6.2 有序列表"></a>6.2 有序列表</h3><p>使用数字加 <code>.</code></p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-bullet">1.</span> 第一步<br><span class="hljs-bullet">2.</span> 第二步<br><span class="hljs-bullet">3.</span> 第三步<br></code></pre></td></tr></table></figure><p>效果：</p><ol><li>第一步</li><li>第二步</li><li>第三步</li></ol><h3 id="6-3-嵌套列表"><a href="#6-3-嵌套列表" class="headerlink" title="6.3 嵌套列表"></a>6.3 嵌套列表</h3><p>子项通常缩进 2 或 4 个空格。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs markdown"><span class="hljs-bullet">1.</span> 水果<br><span class="hljs-bullet">   -</span> 苹果<br><span class="hljs-bullet">   -</span> 香蕉<br><span class="hljs-bullet">2.</span> 蔬菜<br><span class="hljs-bullet">   -</span> 西红柿<br><span class="hljs-bullet">   -</span> 黄瓜<br></code></pre></td></tr></table></figure><p>效果：</p><ol><li>水果<ul><li>苹果</li><li>香蕉</li></ul></li><li>蔬菜<ul><li>西红柿</li><li>黄瓜</li></ul></li></ol><h3 id="6-4-任务列表"><a href="#6-4-任务列表" class="headerlink" title="6.4 任务列表"></a>6.4 任务列表</h3><p>很多平台支持任务列表：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">-</span> [ ] 待完成<br><span class="hljs-bullet">-</span> [x] 已完成<br></code></pre></td></tr></table></figure><p>效果：</p><ul><li><input disabled="" type="checkbox"> 待完成</li><li><input checked="" disabled="" type="checkbox"> 已完成</li></ul><p>注意：</p><ul><li><code>[ ]</code> 中间有空格表示未完成</li><li><code>[x]</code> 表示已完成</li></ul><hr><h2 id="7-链接"><a href="#7-链接" class="headerlink" title="7. 链接"></a>7. 链接</h2><h3 id="7-1-行内链接"><a href="#7-1-行内链接" class="headerlink" title="7.1 行内链接"></a>7.1 行内链接</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">[<span class="hljs-string">百度</span>](<span class="hljs-link">https://www.baidu.com</span>)<br></code></pre></td></tr></table></figure><p>效果：</p><p><a href="https://www.baidu.com/">百度</a></p><h3 id="7-2-带标题的链接"><a href="#7-2-带标题的链接" class="headerlink" title="7.2 带标题的链接"></a>7.2 带标题的链接</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">[<span class="hljs-string">百度</span>](<span class="hljs-link">https://www.baidu.com &quot;这是百度&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="7-3-引用式链接"><a href="#7-3-引用式链接" class="headerlink" title="7.3 引用式链接"></a>7.3 引用式链接</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs markdown">[<span class="hljs-string">百度</span>][<span class="hljs-symbol">1</span>]<br>[<span class="hljs-string">OpenAI</span>][<span class="hljs-symbol">2</span>]<br><br>[<span class="hljs-symbol">1</span>]: <span class="hljs-link">https://www.baidu.com</span><br>[<span class="hljs-symbol">2</span>]: <span class="hljs-link">https://www.openai.com</span><br></code></pre></td></tr></table></figure><p>适合长文档统一管理链接。</p><h3 id="7-4-自动链接"><a href="#7-4-自动链接" class="headerlink" title="7.4 自动链接"></a>7.4 自动链接</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="language-xml">&lt;https://www.baidu.com&gt;</span><br><span class="language-xml">&lt;test@example.com&gt;</span><br></code></pre></td></tr></table></figure><p>效果：</p><p><a href="https://www.baidu.com/">https://www.baidu.com</a><br><a href="mailto:&#116;&#x65;&#115;&#116;&#x40;&#x65;&#120;&#97;&#109;&#112;&#108;&#101;&#x2e;&#x63;&#111;&#x6d;">test@example.com</a></p><hr><h2 id="8-图片"><a href="#8-图片" class="headerlink" title="8. 图片"></a>8. 图片</h2><p>语法和链接很像，只是前面多一个 <code>!</code></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">![<span class="hljs-string">图片说明</span>](<span class="hljs-link">https://example.com/image.png</span>)<br></code></pre></td></tr></table></figure><h3 id="8-1-带标题的图片"><a href="#8-1-带标题的图片" class="headerlink" title="8.1 带标题的图片"></a>8.1 带标题的图片</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">![<span class="hljs-string">图片说明</span>](<span class="hljs-link">https://example.com/image.png &quot;图片标题&quot;</span>)<br></code></pre></td></tr></table></figure><h3 id="8-2-引用式图片"><a href="#8-2-引用式图片" class="headerlink" title="8.2 引用式图片"></a>8.2 引用式图片</h3><figure class="highlight markdown"><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><code class="hljs markdown">![<span class="hljs-string">Logo</span>][<span class="hljs-symbol">logo</span>]<br><br>[<span class="hljs-symbol">logo</span>]: <span class="hljs-link">https://example.com/logo.png</span><br></code></pre></td></tr></table></figure><p>注意：</p><ul><li><code>[]</code> 中内容是图片无法显示时的替代文本</li><li>Markdown 原生语法通常<strong>不能直接设置宽高</strong></li><li>如果平台支持 HTML，可写：</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://example.com/image.png&quot;</span> <span class="hljs-attr">width</span>=<span class="hljs-string">&quot;300&quot;</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">&quot;示例图片&quot;</span>&gt;</span><br></code></pre></td></tr></table></figure><hr><h2 id="9-行内代码与代码块"><a href="#9-行内代码与代码块" class="headerlink" title="9. 行内代码与代码块"></a>9. 行内代码与代码块</h2><h3 id="9-1-行内代码"><a href="#9-1-行内代码" class="headerlink" title="9.1 行内代码"></a>9.1 行内代码</h3><p>使用一对反引号 <code>`</code></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">请使用 <span class="hljs-code">`print()`</span> 输出内容。<br></code></pre></td></tr></table></figure><p>效果：</p><p>请使用 <code>print()</code> 输出内容。</p><h3 id="9-2-代码块"><a href="#9-2-代码块" class="headerlink" title="9.2 代码块"></a>9.2 代码块</h3><p>使用三个反引号包裹。</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-code">```markdown</span><br><span class="hljs-code">这是代码块</span><br><span class="hljs-code">```</span><br></code></pre></td></tr></table></figure><p>示例：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown">def hello():<br><span class="hljs-code">    print(&quot;Hello, Markdown!&quot;)</span><br></code></pre></td></tr></table></figure><h3 id="9-3-指定语言"><a href="#9-3-指定语言" class="headerlink" title="9.3 指定语言"></a>9.3 指定语言</h3><p>可以在开头的三个反引号后写语言名，便于高亮：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><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><code class="hljs markdown"><span class="hljs-code">```python</span><br><span class="hljs-code">def hello():</span><br><span class="hljs-code">    print(&quot;Hello&quot;)</span><br><span class="hljs-code">```</span><br></code></pre></td></tr></table></figure><p>效果：</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><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello</span>():<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Hello&quot;</span>)<br></code></pre></td></tr></table></figure><p>常见语言标识：</p><ul><li><code>python</code></li><li><code>javascript</code></li><li><code>java</code></li><li><code>c</code></li><li><code>cpp</code></li><li><code>html</code></li><li><code>css</code></li><li><code>bash</code></li><li><code>json</code></li><li><code>yaml</code></li><li><code>markdown</code></li></ul><h3 id="9-4-缩进代码块"><a href="#9-4-缩进代码块" class="headerlink" title="9.4 缩进代码块"></a>9.4 缩进代码块</h3><p>也可用 4 个空格或 1 个制表符缩进：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">这也是代码块<br></code></pre></td></tr></table></figure><p>但实际使用中更推荐三个反引号，清晰且方便标注语言。</p><hr><h2 id="10-分隔线"><a href="#10-分隔线" class="headerlink" title="10. 分隔线"></a>10. 分隔线</h2><p>使用三个或更多的 <code>-</code>、<code>*</code> 或 <code>_</code></p><figure class="highlight markdown"><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><code class="hljs markdown">---<br><span class="hljs-strong">***</span><br><span class="hljs-strong">__<span class="hljs-emphasis">_</span></span><br></code></pre></td></tr></table></figure><p>效果：</p><hr><p>注意：</p><ul><li>单独占一行</li><li>常用于章节分隔</li></ul><hr><h2 id="11-表格"><a href="#11-表格" class="headerlink" title="11. 表格"></a>11. 表格</h2><p>基本写法：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><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><code class="hljs markdown">| 姓名 | 年龄 | 城市 |<br>| ---- | ---- | ---- |<br>| 张三 | 18   | 北京 |<br>| 李四 | 20   | 上海 |<br></code></pre></td></tr></table></figure><p>效果：</p><table><thead><tr><th>姓名</th><th>年龄</th><th>城市</th></tr></thead><tbody><tr><td>张三</td><td>18</td><td>北京</td></tr><tr><td>李四</td><td>20</td><td>上海</td></tr></tbody></table><h3 id="11-1-对齐方式"><a href="#11-1-对齐方式" class="headerlink" title="11.1 对齐方式"></a>11.1 对齐方式</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><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><code class="hljs markdown">| 左对齐 | 居中对齐 | 右对齐 |<br>| :----- | :------: | -----: |<br>| A      | B        | C      |<br>| D      | E        | F      |<br></code></pre></td></tr></table></figure><p>效果：</p><table><thead><tr><th align="left">左对齐</th><th align="center">居中对齐</th><th align="right">右对齐</th></tr></thead><tbody><tr><td align="left">A</td><td align="center">B</td><td align="right">C</td></tr><tr><td align="left">D</td><td align="center">E</td><td align="right">F</td></tr></tbody></table><p>说明：</p><ul><li><code>:---</code> 左对齐</li><li><code>:---:</code> 居中</li><li><code>---:</code> 右对齐</li></ul><hr><h2 id="12-转义字符"><a href="#12-转义字符" class="headerlink" title="12. 转义字符"></a>12. 转义字符</h2><p>如果想显示 Markdown 语法符号本身，可在前面加反斜杠 <code>\</code></p><figure class="highlight markdown"><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><code class="hljs markdown">\<span class="hljs-emphasis">*这不是斜体\*</span><br>\# 这不是标题<br>\<span class="hljs-code">`这不是代码\`</span><br></code></pre></td></tr></table></figure><p>效果：</p><p>*这不是斜体*<br># 这不是标题<br>`这不是代码`</p><p>常见可转义字符：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs markdown">\ 反斜杠<br>` 反引号<br><span class="hljs-bullet">*</span> 星号<br>_ 下划线<br>&#123;&#125; 花括号<br>[] 方括号<br>() 小括号<br><span class="hljs-section"># 井号</span><br><span class="hljs-bullet">+</span> 加号<br><span class="hljs-bullet">-</span> 减号<br>. 点<br>! 感叹号<br></code></pre></td></tr></table></figure><hr><h2 id="13-HTML-混写"><a href="#13-HTML-混写" class="headerlink" title="13. HTML 混写"></a>13. HTML 混写</h2><p>许多 Markdown 解析器支持直接嵌入 HTML：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>加粗<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">i</span>&gt;</span>斜体<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>内容<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><p>常见用途：</p><ul><li>强制换行</li><li>设置图片大小</li><li>插入复杂布局</li><li>使用更丰富样式</li></ul><p>注意：</p><ul><li>不同平台对 HTML 支持不同</li><li>有些平台会过滤危险标签</li></ul><hr><h2 id="14-常见扩展语法"><a href="#14-常见扩展语法" class="headerlink" title="14. 常见扩展语法"></a>14. 常见扩展语法</h2><p>不同平台常支持一些扩展，不属于最基础标准，但很常见。</p><h3 id="14-1-脚注"><a href="#14-1-脚注" class="headerlink" title="14.1 脚注"></a>14.1 脚注</h3><figure class="highlight markdown"><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><code class="hljs markdown">这是一个脚注示例[^1]<br><br>[<span class="hljs-symbol">^1</span>]: <span class="hljs-link">这里是脚注内容。</span><br></code></pre></td></tr></table></figure><h3 id="14-2-定义列表"><a href="#14-2-定义列表" class="headerlink" title="14.2 定义列表"></a>14.2 定义列表</h3><p>部分平台支持：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown">术语<br>: 解释内容<br></code></pre></td></tr></table></figure><h3 id="14-3-数学公式"><a href="#14-3-数学公式" class="headerlink" title="14.3 数学公式"></a>14.3 数学公式</h3><p>有些平台支持 LaTeX 公式。</p><p>行内公式：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">$E = mc^2$<br></code></pre></td></tr></table></figure><p>块级公式：</p><figure class="highlight markdown"><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><code class="hljs markdown">$$<br>a^2 + b^2 = c^2<br>$$<br></code></pre></td></tr></table></figure><h3 id="14-4-目录"><a href="#14-4-目录" class="headerlink" title="14.4 目录"></a>14.4 目录</h3><p>部分编辑器支持根据标题自动生成目录，例如：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">[TOC]<br></code></pre></td></tr></table></figure><p>注意：并非所有平台支持。</p><h3 id="14-5-Mermaid-图表"><a href="#14-5-Mermaid-图表" class="headerlink" title="14.5 Mermaid 图表"></a>14.5 Mermaid 图表</h3><p>部分平台支持 Mermaid：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs markdown"><span class="hljs-code">```mermaid</span><br><span class="hljs-code">graph TD</span><br><span class="hljs-code">A[开始] --&gt; B[学习 Markdown]</span><br><span class="hljs-code">B --&gt; C[掌握语法]</span><br><span class="hljs-code">```</span><br></code></pre></td></tr></table></figure><hr><h2 id="15-Markdown-与平台差异"><a href="#15-Markdown-与平台差异" class="headerlink" title="15. Markdown 与平台差异"></a>15. Markdown 与平台差异</h2><p>Markdown 并不是完全统一的，不同平台支持的语法会有区别。</p><p>常见版本或实现：</p><ul><li>CommonMark</li><li>GitHub Flavored Markdown，简称 GFM</li><li>Markdown Extra</li><li>Typora 扩展语法</li><li>各博客平台自定义扩展</li></ul><p>常见差异体现在：</p><ul><li>是否支持表格</li><li>是否支持任务列表</li><li>是否支持脚注</li><li>是否支持数学公式</li><li>是否支持 HTML</li><li>是否支持目录 <code>[TOC]</code></li><li>是否支持高亮 <code>==内容==</code></li></ul><p>因此编写文档时要注意目标平台。</p><hr><h2 id="16-Markdown-编写规范建议"><a href="#16-Markdown-编写规范建议" class="headerlink" title="16. Markdown 编写规范建议"></a>16. Markdown 编写规范建议</h2><h3 id="16-1-标题层级清晰"><a href="#16-1-标题层级清晰" class="headerlink" title="16.1 标题层级清晰"></a>16.1 标题层级清晰</h3><p>推荐：</p><ul><li>一个文档通常只有一个一级标题</li><li>二级标题用于大章节</li><li>三级标题用于小节</li><li>不要跳级过多</li></ul><p>例如：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs markdown"><span class="hljs-section"># Python 学习笔记</span><br><br><span class="hljs-section">## 基础语法</span><br><span class="hljs-section">### 变量</span><br><span class="hljs-section">### 条件语句</span><br><br><span class="hljs-section">## 函数</span><br><span class="hljs-section">### 定义函数</span><br><span class="hljs-section">### 参数传递</span><br></code></pre></td></tr></table></figure><h3 id="16-2-列表风格统一"><a href="#16-2-列表风格统一" class="headerlink" title="16.2 列表风格统一"></a>16.2 列表风格统一</h3><p>同一文档中：</p><ul><li>无序列表尽量统一使用 <code>-</code></li><li>有序列表统一用 <code>1. 2. 3.</code></li><li>保持缩进一致</li></ul><h3 id="16-3-代码块标注语言"><a href="#16-3-代码块标注语言" class="headerlink" title="16.3 代码块标注语言"></a>16.3 代码块标注语言</h3><p>推荐：</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-code">```python</span><br><span class="hljs-code">print(&quot;hello&quot;)</span><br><span class="hljs-code">```</span><br></code></pre></td></tr></table></figure><p>不推荐：</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-code">```</span><br><span class="hljs-code">print(&quot;hello&quot;)</span><br><span class="hljs-code">```</span><br></code></pre></td></tr></table></figure><p>因为标注语言后更易读。</p><h3 id="16-4-合理留白"><a href="#16-4-合理留白" class="headerlink" title="16.4 合理留白"></a>16.4 合理留白</h3><p>建议：</p><ul><li>标题前后适当空行</li><li>段落间空一行</li><li>表格、代码块、列表与正文适当分隔</li></ul><p>这样文档更清晰。</p><h3 id="16-5-一行不要过长"><a href="#16-5-一行不要过长" class="headerlink" title="16.5 一行不要过长"></a>16.5 一行不要过长</h3><p>适合阅读和版本管理，尤其是技术文档。</p><h3 id="16-6-善用引用、表格、列表"><a href="#16-6-善用引用、表格、列表" class="headerlink" title="16.6 善用引用、表格、列表"></a>16.6 善用引用、表格、列表</h3><ul><li>说明步骤时用有序列表</li><li>枚举特点时用无序列表</li><li>参数对比时用表格</li><li>提示信息可用引用</li></ul><hr><h2 id="17-常见错误"><a href="#17-常见错误" class="headerlink" title="17. 常见错误"></a>17. 常见错误</h2><h3 id="17-1-后忘记加空格"><a href="#17-1-后忘记加空格" class="headerlink" title="17.1 # 后忘记加空格"></a>17.1 <code>#</code> 后忘记加空格</h3><p>错误：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section">#标题</span><br></code></pre></td></tr></table></figure><p>推荐：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># 标题</span><br></code></pre></td></tr></table></figure><h3 id="17-2-列表符号后忘记空格"><a href="#17-2-列表符号后忘记空格" class="headerlink" title="17.2 列表符号后忘记空格"></a>17.2 列表符号后忘记空格</h3><p>错误：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown">-苹果<br></code></pre></td></tr></table></figure><p>推荐：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">-</span> 苹果<br></code></pre></td></tr></table></figure><h3 id="17-3-代码块没有闭合"><a href="#17-3-代码块没有闭合" class="headerlink" title="17.3 代码块没有闭合"></a>17.3 代码块没有闭合</h3><p>错误：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-code">```python</span><br><span class="hljs-code">print(&quot;hello&quot;)</span><br></code></pre></td></tr></table></figure><p>推荐：</p><figure class="highlight markdown"><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><code class="hljs markdown"><span class="hljs-code">```python</span><br><span class="hljs-code">print(&quot;hello&quot;)</span><br><span class="hljs-code">```</span><br></code></pre></td></tr></table></figure><h3 id="17-4-表格竖线不规范"><a href="#17-4-表格竖线不规范" class="headerlink" title="17.4 表格竖线不规范"></a>17.4 表格竖线不规范</h3><p>虽然很多编辑器能容错，但建议写整齐。</p><h3 id="17-5-换行方式理解错误"><a href="#17-5-换行方式理解错误" class="headerlink" title="17.5 换行方式理解错误"></a>17.5 换行方式理解错误</h3><p>很多初学者以为直接回车就会换行，实际上很多 Markdown 环境中不会。</p><hr><h2 id="18-实用示例"><a href="#18-实用示例" class="headerlink" title="18. 实用示例"></a>18. 实用示例</h2><h3 id="18-1-简单笔记模板"><a href="#18-1-简单笔记模板" class="headerlink" title="18.1 简单笔记模板"></a>18.1 简单笔记模板</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># 今日学习笔记</span><br><br><span class="hljs-section">## 学习内容</span><br><br>今天学习了 Markdown 的基础语法，包括：<br><br><span class="hljs-bullet">-</span> 标题<br><span class="hljs-bullet">-</span> 列表<br><span class="hljs-bullet">-</span> 引用<br><span class="hljs-bullet">-</span> 代码块<br><br><span class="hljs-section">## 重点示例</span><br><br><span class="hljs-section">### Python 示例</span><br><br><span class="hljs-code">```python</span><br><span class="hljs-code">print(&quot;Hello, Markdown!&quot;)</span><br><span class="hljs-code">```</span><br><br><span class="hljs-section">## 总结</span><br><br>Markdown 适合写文档，语法简单，排版清晰。<br></code></pre></td></tr></table></figure><h3 id="18-2-README-模板"><a href="#18-2-README-模板" class="headerlink" title="18.2 README 模板"></a>18.2 README 模板</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs markdown"><span class="hljs-section"># 项目名称</span><br><br><span class="hljs-section">## 项目简介</span><br><br>这是一个用于学习 Markdown 的示例项目。<br><br><span class="hljs-section">## 功能特点</span><br><br><span class="hljs-bullet">-</span> 简单易用<br><span class="hljs-bullet">-</span> 结构清晰<br><span class="hljs-bullet">-</span> 支持扩展<br><br><span class="hljs-section">## 安装方法</span><br><br><span class="hljs-code">```bash</span><br><span class="hljs-code">git clone https://example.com/demo.git</span><br><span class="hljs-code">cd demo</span><br><span class="hljs-code">```</span><br><br><span class="hljs-section">## 使用方法</span><br><br>运行以下命令：<br><br><span class="hljs-code">```bash</span><br><span class="hljs-code">python main.py</span><br><span class="hljs-code">```</span><br><br><span class="hljs-section">## 目录结构</span><br><br>| 文件 | 说明 |<br>| ---- | ---- |<br>| main.py | 主程序 |<br>| README.md | 项目说明 |<br><br><span class="hljs-section">## License</span><br><br>MIT<br></code></pre></td></tr></table></figure><hr><h2 id="19-常用速查表"><a href="#19-常用速查表" class="headerlink" title="19. 常用速查表"></a>19. 常用速查表</h2><table><thead><tr><th>功能</th><th>语法</th></tr></thead><tbody><tr><td>一级标题</td><td><code># 标题</code></td></tr><tr><td>二级标题</td><td><code>## 标题</code></td></tr><tr><td>粗体</td><td><code>**文字**</code></td></tr><tr><td>斜体</td><td><code>*文字*</code></td></tr><tr><td>删除线</td><td><code>~~文字~~</code></td></tr><tr><td>引用</td><td><code>&gt; 引用内容</code></td></tr><tr><td>无序列表</td><td><code>- 项目</code></td></tr><tr><td>有序列表</td><td><code>1. 项目</code></td></tr><tr><td>行内代码</td><td><code>`代码`</code></td></tr><tr><td>代码块</td><td><code>```语言</code></td></tr><tr><td>链接</td><td><code>[名称](地址)</code></td></tr><tr><td>图片</td><td><code>![说明](地址)</code></td></tr><tr><td>分隔线</td><td><code>---</code></td></tr><tr><td>表格</td><td>&#96;</td></tr><tr><td>任务列表</td><td><code>- [ ] 任务</code></td></tr><tr><td>脚注</td><td><code>[^1]</code></td></tr></tbody></table><hr><h2 id="20-学习建议"><a href="#20-学习建议" class="headerlink" title="20. 学习建议"></a>20. 学习建议</h2><p>学习 Markdown 最好的方式不是死记，而是边写边练。推荐练习路径：</p><ol><li>先掌握标题、段落、强调、列表</li><li>再学习链接、图片、代码块、引用</li><li>然后练习表格、任务列表、脚注等扩展语法</li><li>最后结合实际写一篇完整笔记或 README</li></ol><p>适合新手的练习任务：</p><ul><li>写一篇自我介绍</li><li>写一份课程笔记</li><li>写一个项目 README</li><li>整理一份读书笔记</li></ul><hr><h2 id="21-结语"><a href="#21-结语" class="headerlink" title="21. 结语"></a>21. 结语</h2><p>Markdown 的本质不是“花哨排版”，而是用<strong>尽可能简单的语法，写出结构清晰、便于阅读的文档</strong>。</p><p>你可以继续围绕这份笔记做三类练习：</p><ul><li>对照语法手打一遍</li><li>自己写一份 README</li><li>把课堂笔记改写成 Markdown</li></ul><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1775191607175" data-twikoo-path="article_1775191607175"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/02/markdown-yu-fa-bi-ji/</id>
    <link href="https://aoiblog.top/2026/04/02/markdown-yu-fa-bi-ji/"/>
    <published>2026-04-02T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>Markdown 语法笔记</h2>
## 1. Markdown 是什么

<p>Markdown 是一种<strong>轻量级标记语言</strong>，用简洁的纯文本语法来表示标题、列表、引用、代码、表格等结构，常用于：</p>
<ul>
<li>文档编写</li>]]>
    </summary>
    <title>Markdown 语法笔记</title>
    <updated>2026-04-02T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>命令行学习指南</h2>## 1. 先建立正确认识<h3 id="1-1-什么是命令行"><a href="#1-1-什么是命令行" class="headerlink" title="1.1 什么是命令行"></a>1.1 什么是命令行</h3><p>命令行就是通过输入文本命令来操作电脑、项目和开发工具的方式。<br>在这次对话里，主要涉及了 4 类命令行环境：</p><ul><li>CMD</li><li>PowerShell</li><li>Node.js &#x2F; npm &#x2F; Yarn &#x2F; NVM</li><li>Windows 批处理文件（<code>.bat</code> &#x2F; <code>.cmd</code>）</li></ul><h3 id="1-2-命令行里最重要的-4-个概念"><a href="#1-2-命令行里最重要的-4-个概念" class="headerlink" title="1.2 命令行里最重要的 4 个概念"></a>1.2 命令行里最重要的 4 个概念</h3><h4 id="当前目录"><a href="#当前目录" class="headerlink" title="当前目录"></a>当前目录</h4><p>很多命令都依赖你“现在站在哪个目录”。</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cmd"><span class="hljs-built_in">cd</span>  显示当前所在的目录路径<br><span class="hljs-built_in">dir</span>  列出当前目录下的文件和文件夹<br></code></pre></td></tr></table></figure><h4 id="安装依赖和启动项目不是一回事"><a href="#安装依赖和启动项目不是一回事" class="headerlink" title="安装依赖和启动项目不是一回事"></a>安装依赖和启动项目不是一回事</h4><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><code class="hljs bash">yarn install  读取 package.json，把依赖包下载到 node_modules 文件夹<br>yarn start  启动项目开发服务器，关闭终端后服务就会停止<br></code></pre></td></tr></table></figure><ul><li><code>yarn install</code>：把依赖下载到硬盘，结果会保留下来</li><li><code>yarn start</code>：启动服务，关闭终端后服务就结束</li></ul><h4 id="临时效果和永久效果不是一回事"><a href="#临时效果和永久效果不是一回事" class="headerlink" title="临时效果和永久效果不是一回事"></a>临时效果和永久效果不是一回事</h4><p>例如：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;--no-experimental-fetch&quot;</span>  设置临时环境变量，关闭终端即失效<br></code></pre></td></tr></table></figure><p>这类环境变量通常只在当前窗口有效，关掉终端后就没了。</p><p>而下面这种命令执行后的结果会保留在硬盘上：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn install  安装的依赖文件会保存在硬盘<br>git <span class="hljs-built_in">clone</span> &lt;仓库地址&gt;  克隆的项目代码会保存在硬盘<br><span class="hljs-built_in">mkdir</span> demo  创建的新文件夹会保存在硬盘<br></code></pre></td></tr></table></figure><h4 id="一个前台终端通常只能跑一个长期任务"><a href="#一个前台终端通常只能跑一个长期任务" class="headerlink" title="一个前台终端通常只能跑一个长期任务"></a>一个前台终端通常只能跑一个长期任务</h4><p>如果一个命令会持续占用终端，例如：</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><code class="hljs bash">yarn start  启动前端项目服务，终端会被占用<br>yarn dev  启动开发环境服务，终端同样被占用<br></code></pre></td></tr></table></figure><p>那同一个窗口里一般就不能同时前台再跑另一个项目。<br>如果你要同时启动多个项目，通常需要多个终端窗口或多个终端标签页。</p><hr><h2 id="2-CMD-入门"><a href="#2-CMD-入门" class="headerlink" title="2. CMD 入门"></a>2. CMD 入门</h2><h3 id="2-1-查看当前位置和切换目录"><a href="#2-1-查看当前位置和切换目录" class="headerlink" title="2.1 查看当前位置和切换目录"></a>2.1 查看当前位置和切换目录</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cmd"><span class="hljs-built_in">cd</span>  显示当前路径<br><span class="hljs-built_in">cd</span> src  进入名为 src 的子目录<br><span class="hljs-built_in">cd</span> ..  返回上一级目录<br><span class="hljs-built_in">cd</span> ../..  返回上两级目录<br><span class="hljs-built_in">cd</span> ..\..\..  返回上三级目录（Windows 使用反斜杠也可）<br><span class="hljs-built_in">cd</span> \  直接返回当前盘符的根目录<br><span class="hljs-built_in">cd</span> /d D:\project\my-app  跨盘符直接切换到指定目录<br></code></pre></td></tr></table></figure><p>说明：</p><ul><li><code>cd</code>：显示当前路径</li><li><code>cd src</code>：进入子目录</li><li><code>cd ..</code>：回到上一级</li><li><code>cd \</code>：回到当前盘符根目录</li><li><code>cd /d D:\project\my-app</code>：跨盘符切换目录</li></ul><h3 id="2-2-跨盘符切换"><a href="#2-2-跨盘符切换" class="headerlink" title="2.2 跨盘符切换"></a>2.2 跨盘符切换</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cmd"><span class="hljs-function">D:  切换到 <span class="hljs-title">D</span> 盘</span><br><span class="hljs-function"><span class="hljs-title">cd</span> <span class="hljs-title">D</span>:\<span class="hljs-title">project</span>\<span class="hljs-title">my</span>-<span class="hljs-title">app</span>  在 <span class="hljs-title">D</span> 盘内切换到具体目录</span><br></code></pre></td></tr></table></figure><p>或者一步完成：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cmd"><span class="hljs-built_in">cd</span> /d D:\project\my-app  /d 参数允许同时切换盘符和路径<br></code></pre></td></tr></table></figure><h3 id="2-3-查看当前目录下有什么"><a href="#2-3-查看当前目录下有什么" class="headerlink" title="2.3 查看当前目录下有什么"></a>2.3 查看当前目录下有什么</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs cmd"><span class="hljs-built_in">dir</span>  列出当前目录下的文件和文件夹详情<br><span class="hljs-built_in">dir</span> /b  仅列出文件和文件夹的名称（精简模式）<br><span class="hljs-built_in">dir</span> /w  宽格式按列显示内容<br><span class="hljs-built_in">dir</span> /s  包含所有子目录中的文件（递归显示）<br><span class="hljs-built_in">dir</span> /a  显示所有文件，包含隐藏文件<br><span class="hljs-built_in">dir</span> /ad  仅显示文件夹（目录）<br><span class="hljs-built_in">dir</span> /a-d  仅显示文件，不显示文件夹<br><span class="hljs-built_in">dir</span> /p  列表太长时，满屏后暂停等待按键<br><span class="hljs-built_in">dir</span> /o  按名称首字母排序显示<br><span class="hljs-built_in">dir</span> &gt; filelist.txt  将 <span class="hljs-built_in">dir</span> 的输出结果保存到 filelist.txt 文件中<br><span class="hljs-built_in">tree</span>  以树状图显示当前目录结构<br><span class="hljs-built_in">tree</span> /f  以树状图显示目录结构，并包含文件名<br></code></pre></td></tr></table></figure><p>常见用法：</p><ul><li><code>dir</code>：普通列表</li><li><code>dir /b</code>：只看名称</li><li><code>dir /ad</code>：只看文件夹</li><li><code>dir /a-d</code>：只看文件</li><li><code>tree /f</code>：看目录树和文件</li><li><code>dir &gt; filelist.txt</code>：保存结果到文件</li></ul><h3 id="2-4-常用辅助命令"><a href="#2-4-常用辅助命令" class="headerlink" title="2.4 常用辅助命令"></a>2.4 常用辅助命令</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs cmd"><span class="hljs-built_in">cls</span>  清除当前终端窗口的屏幕内容<br><span class="hljs-built_in">doskey</span> /history  显示当前窗口输入过的命令历史记录<br><span class="hljs-built_in">echo</span> <span class="hljs-variable">%cd%</span>  打印当前所在的目录路径<br><span class="hljs-built_in">cd</span> <span class="hljs-variable">%USERPROFILE%</span>  切换到当前 Windows 用户的个人主目录（如 C:\Users\xxx）<br><span class="hljs-built_in">cd</span> <span class="hljs-variable">%USERPROFILE%</span>\Desktop  切换到当前用户的桌面目录<br></code></pre></td></tr></table></figure><h3 id="2-5-带空格目录的处理"><a href="#2-5-带空格目录的处理" class="headerlink" title="2.5 带空格目录的处理"></a>2.5 带空格目录的处理</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cmd"><span class="hljs-built_in">cd</span> &quot;Program Files&quot;  目录名有空格时，必须用双引号包起来<br><span class="hljs-built_in">cd</span> progra~<span class="hljs-number">1</span>  使用 Windows 的 <span class="hljs-number">8</span>.<span class="hljs-number">3</span> 短文件名格式（较老旧的做法）<br></code></pre></td></tr></table></figure><p>学习建议：路径里有空格时，优先用双引号包住。</p><hr><h2 id="3-PowerShell-常用命令"><a href="#3-PowerShell-常用命令" class="headerlink" title="3. PowerShell 常用命令"></a>3. PowerShell 常用命令</h2><p>PowerShell 比 CMD 更强，尤其擅长筛选、批量处理和对象管道。</p><h3 id="3-1-文件与目录"><a href="#3-1-文件与目录" class="headerlink" title="3.1 文件与目录"></a>3.1 文件与目录</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs powershell"><span class="hljs-built_in">Get-ChildItem</span>  列出当前目录的内容（等同于 <span class="hljs-built_in">dir</span> / <span class="hljs-built_in">ls</span>）<br><span class="hljs-built_in">ls</span>  <span class="hljs-built_in">Get-ChildItem</span> 的常用简写<br><span class="hljs-built_in">ls</span> <span class="hljs-literal">-Recurse</span>  递归列出所有子目录及内容<br><span class="hljs-built_in">Set-Location</span> D:\project  切换目录到 D:\project<br><span class="hljs-built_in">cd</span> D:\project  <span class="hljs-built_in">Set-Location</span> 的常用简写<br><span class="hljs-built_in">sl</span> D:\project  <span class="hljs-built_in">Set-Location</span> 的另一种简写<br><span class="hljs-built_in">Get-Location</span>  获取当前路径<br><span class="hljs-built_in">pwd</span>  <span class="hljs-built_in">Get-Location</span> 的常用简写<br><span class="hljs-built_in">New-Item</span> demo.txt  创建一个空文件 demo.txt<br><span class="hljs-built_in">New-Item</span> demo <span class="hljs-literal">-ItemType</span> Directory  创建一个名为 demo 的文件夹<br><span class="hljs-built_in">Remove-Item</span> demo.txt  删除 demo.txt 文件<br><span class="hljs-built_in">Remove-Item</span> demo <span class="hljs-literal">-Recurse</span>  递归删除 demo 文件夹及其所有内容<br><span class="hljs-built_in">Copy-Item</span> a.txt b.txt  将 a.txt 复制为 b.txt<br><span class="hljs-built_in">Move-Item</span> a.txt D:\  将 a.txt 移动到 D 盘根目录<br><span class="hljs-built_in">Rename-Item</span> a.txt b.txt  将 a.txt 重命名为 b.txt<br></code></pre></td></tr></table></figure><h3 id="3-2-查看和处理文件内容"><a href="#3-2-查看和处理文件内容" class="headerlink" title="3.2 查看和处理文件内容"></a>3.2 查看和处理文件内容</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs powershell"><span class="hljs-built_in">Get-Content</span> package.json  在终端中打印 package.json 的全部内容<br><span class="hljs-built_in">cat</span> package.json  <span class="hljs-built_in">Get-Content</span> 的常用简写<br><span class="hljs-built_in">type</span> package.json  也是 <span class="hljs-built_in">Get-Content</span> 的简写<br><span class="hljs-built_in">Set-Content</span> output.txt <span class="hljs-string">&quot;hello&quot;</span>  将 <span class="hljs-string">&quot;hello&quot;</span> 覆盖写入 output.txt<br><span class="hljs-built_in">Add-Content</span> output.txt <span class="hljs-string">&quot;world&quot;</span>  将 <span class="hljs-string">&quot;world&quot;</span> 追加到 output.txt 末尾<br><span class="hljs-built_in">Select-String</span> <span class="hljs-string">&quot;error&quot;</span> *.log  在所有 log 文件中搜索包含 <span class="hljs-string">&quot;error&quot;</span> 的行<br><span class="hljs-built_in">ls</span> | <span class="hljs-built_in">Out-File</span> list.txt  将 <span class="hljs-built_in">ls</span> 的输出结果保存到 list.txt 文件中<br></code></pre></td></tr></table></figure><h3 id="3-3-进程与服务"><a href="#3-3-进程与服务" class="headerlink" title="3.3 进程与服务"></a>3.3 进程与服务</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs powershell"><span class="hljs-built_in">Get-Process</span>  列出当前正在运行的所有进程<br><span class="hljs-built_in">ps</span>  <span class="hljs-built_in">Get-Process</span> 的简写<br><span class="hljs-built_in">ps</span> node  仅列出名为 node 的进程<br><span class="hljs-built_in">Stop-Process</span> <span class="hljs-literal">-Id</span> <span class="hljs-number">1234</span>  停止进程 ID 为 <span class="hljs-number">1234</span> 的进程<br><span class="hljs-built_in">Stop-Process</span> <span class="hljs-literal">-Id</span> <span class="hljs-number">1234</span> <span class="hljs-literal">-Force</span>  强制停止进程 ID 为 <span class="hljs-number">1234</span> 的进程<br><span class="hljs-built_in">kill</span> <span class="hljs-literal">-Name</span> node  强制停止所有名为 node 的进程<br><span class="hljs-built_in">kill</span> <span class="hljs-literal">-Id</span> <span class="hljs-number">1234</span>  强制停止进程 ID 为 <span class="hljs-number">1234</span> 的进程<br><span class="hljs-built_in">Start-Process</span> notepad.exe  启动记事本程序<br><span class="hljs-built_in">Get-Service</span>  列出系统所有的服务状态<br><span class="hljs-built_in">Get-Service</span> *mysql*  列出名字中包含 mysql 的服务<br><span class="hljs-built_in">Stop-Service</span> Spooler  停止打印后台处理服务（Spooler）<br><span class="hljs-built_in">Start-Service</span> Spooler  启动打印后台处理服务<br></code></pre></td></tr></table></figure><h3 id="3-4-网络与系统信息"><a href="#3-4-网络与系统信息" class="headerlink" title="3.4 网络与系统信息"></a>3.4 网络与系统信息</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs powershell"><span class="hljs-built_in">Test-Connection</span> google.com  测试是否能 ping 通 google.com<br><span class="hljs-built_in">Invoke-WebRequest</span> <span class="hljs-literal">-Uri</span> <span class="hljs-string">&quot;https://example.com/file.zip&quot;</span> <span class="hljs-literal">-OutFile</span> file.zip  从网络下载文件<br><span class="hljs-built_in">Test-NetConnection</span> google.com <span class="hljs-literal">-Port</span> <span class="hljs-number">443</span>  测试能否连接到 google.com 的 <span class="hljs-number">443</span> 端口<br><span class="hljs-built_in">Get-NetIPAddress</span>  查看本机的 IP 地址信息<br><span class="hljs-built_in">Get-Date</span>  获取当前系统日期和时间<br><span class="hljs-built_in">Get-History</span>  获取当前 PowerShell 窗口的命令输入历史<br><span class="hljs-built_in">Clear-Host</span>  清屏<br><span class="hljs-built_in">cls</span>  <span class="hljs-built_in">Clear-Host</span> 的简写<br><span class="hljs-keyword">exit</span>  退出当前 PowerShell 窗口<br></code></pre></td></tr></table></figure><h3 id="3-5-环境变量"><a href="#3-5-环境变量" class="headerlink" title="3.5 环境变量"></a>3.5 环境变量</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><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><code class="hljs powershell"><span class="hljs-built_in">Get-ChildItem</span> Env:  查看系统中所有的环境变量<br><span class="hljs-variable">$env:PATH</span>  打印系统的 PATH 环境变量内容<br><span class="hljs-variable">$env:NODE_ENV</span>=<span class="hljs-string">&quot;production&quot;</span>  临时设置 NODE_ENV 变量为 production<br><span class="hljs-built_in">Get-Command</span> node  查找 node 命令的实际可执行文件路径<br></code></pre></td></tr></table></figure><p>临时设置与撤销：</p><figure class="highlight powershell"><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><code class="hljs powershell"><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;--no-experimental-fetch&quot;</span>  临时设置 NODE_OPTIONS<br><span class="hljs-variable">$env:NODE_OPTIONS</span>  查看 NODE_OPTIONS 的当前值<br><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;&quot;</span>  将 NODE_OPTIONS 清空，相当于撤销设置<br></code></pre></td></tr></table></figure><h3 id="3-6-PowerShell-最值得学的能力：管道"><a href="#3-6-PowerShell-最值得学的能力：管道" class="headerlink" title="3.6 PowerShell 最值得学的能力：管道"></a>3.6 PowerShell 最值得学的能力：管道</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs powershell"><span class="hljs-built_in">Get-Process</span> | <span class="hljs-built_in">Where-Object</span> &#123; <span class="hljs-variable">$_</span>.ProcessName <span class="hljs-operator">-like</span> <span class="hljs-string">&quot;*node*&quot;</span> &#125;  筛选出名字包含 node 的进程<br><span class="hljs-built_in">ps</span> | <span class="hljs-built_in">where</span> &#123; <span class="hljs-variable">$_</span>.Name <span class="hljs-operator">-like</span> <span class="hljs-string">&quot;*node*&quot;</span> &#125;  上一条命令的简写版<br><span class="hljs-built_in">ls</span> | <span class="hljs-built_in">Select-Object</span> Name, Length  列出文件，但只保留名称和大小属性<br><span class="hljs-built_in">ls</span> | <span class="hljs-built_in">Sort-Object</span> Length <span class="hljs-literal">-Descending</span>  列出文件并按大小从大到小排序<br><span class="hljs-built_in">cat</span> log.txt | <span class="hljs-built_in">Measure-Object</span> <span class="hljs-literal">-Line</span>  统计 log.txt 文件的总行数<br><span class="hljs-built_in">cat</span> app.log | <span class="hljs-built_in">Select-String</span> <span class="hljs-string">&quot;error&quot;</span>  读取日志并筛选出包含 error 的行<br></code></pre></td></tr></table></figure><p>理解方式：</p><ul><li><code>|</code> 前面产出结果</li><li><code>|</code> 后面继续筛选、排序、统计</li></ul><h3 id="3-7-帮助、别名与命令发现"><a href="#3-7-帮助、别名与命令发现" class="headerlink" title="3.7 帮助、别名与命令发现"></a>3.7 帮助、别名与命令发现</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs powershell"><span class="hljs-built_in">Get-Help</span> <span class="hljs-built_in">ls</span>  查看 <span class="hljs-built_in">ls</span> 命令的官方帮助文档<br><span class="hljs-built_in">Get-Help</span> <span class="hljs-built_in">ls</span> <span class="hljs-literal">-Examples</span>  直接查看 <span class="hljs-built_in">ls</span> 命令的用法示例<br><span class="hljs-built_in">Get-Command</span>  列出所有可用的命令<br><span class="hljs-built_in">Get-Command</span> *<span class="hljs-keyword">process</span>*  查找名称中包含 <span class="hljs-keyword">process</span> 的命令<br><span class="hljs-built_in">Get-Alias</span>  列出所有命令的简写别名<br><span class="hljs-built_in">Get-Alias</span> <span class="hljs-built_in">ls</span>  查看 <span class="hljs-built_in">ls</span> 是哪个命令的别名（指向 <span class="hljs-built_in">Get-ChildItem</span>）<br><span class="hljs-built_in">Get-ExecutionPolicy</span>  查看当前 PowerShell 的脚本执行策略<br><span class="hljs-built_in">Set-ExecutionPolicy</span> RemoteSigned  设置允许运行本地编写的脚本<br></code></pre></td></tr></table></figure><hr><h2 id="4-Git：把项目拉到本地"><a href="#4-Git：把项目拉到本地" class="headerlink" title="4. Git：把项目拉到本地"></a>4. Git：把项目拉到本地</h2><p>这次对话里最核心的 Git 命令是：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> &lt;仓库地址&gt;  将远程仓库的代码完整下载到本地文件夹<br></code></pre></td></tr></table></figure><p>作用：</p><ul><li>把远程仓库完整复制到本地</li></ul><p>常见流程：</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><code class="hljs bash">git <span class="hljs-built_in">clone</span> &lt;仓库地址&gt;  克隆项目代码到本地<br><span class="hljs-built_in">cd</span> 项目目录  进入刚克隆出来的项目文件夹<br></code></pre></td></tr></table></figure><p>如果仓库是私有的，通常还需要提前准备权限，例如：</p><ul><li>登录对应平台</li><li>配置 SSH</li><li>或使用 Token</li></ul><hr><h2 id="5-Yarn：项目依赖与脚本管理"><a href="#5-Yarn：项目依赖与脚本管理" class="headerlink" title="5. Yarn：项目依赖与脚本管理"></a>5. Yarn：项目依赖与脚本管理</h2><h3 id="5-1-Yarn-是干什么的"><a href="#5-1-Yarn-是干什么的" class="headerlink" title="5.1 Yarn 是干什么的"></a>5.1 Yarn 是干什么的</h3><p>Yarn 是 JavaScript 包管理工具，主要做 3 件事：</p><ul><li>安装依赖</li><li>管理依赖版本</li><li>运行项目脚本</li></ul><h3 id="5-2-安装-Yarn"><a href="#5-2-安装-Yarn" class="headerlink" title="5.2 安装 Yarn"></a>5.2 安装 Yarn</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm install -g yarn  使用 npm 将 Yarn 安装到全局环境<br>yarn --version  查看已安装的 Yarn 版本号<br>yarn -v  yarn --version 的简写<br></code></pre></td></tr></table></figure><h3 id="5-3-安装项目依赖"><a href="#5-3-安装项目依赖" class="headerlink" title="5.3 安装项目依赖"></a>5.3 安装项目依赖</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn  安装 package.json 中列出的所有依赖<br>yarn install  明确写出 install，功能和直接输入 yarn 一样<br>yarn install --immutable  严格按照锁文件安装，不允许修改锁文件<br>yarn install --frozen-lockfile  锁文件不匹配时直接报错，常用于 CI/CD<br>yarn install --verbose  安装时输出极为详细的日志信息，用于排错<br></code></pre></td></tr></table></figure><p>说明：</p><ul><li><code>yarn</code> 和 <code>yarn install</code> 常常等价</li><li><code>--frozen-lockfile</code>：更严格地按锁文件安装</li><li><code>--immutable</code>：在部分 Yarn 版本&#x2F;工作区场景中使用</li><li><code>--verbose</code>：看更详细日志</li></ul><h3 id="5-4-添加、删除、更新依赖"><a href="#5-4-添加、删除、更新依赖" class="headerlink" title="5.4 添加、删除、更新依赖"></a>5.4 添加、删除、更新依赖</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn add &lt;package-name&gt;  将包安装并记录到 dependencies (生产依赖)<br>yarn add -D &lt;package-name&gt;  将包安装并记录到 devDependencies (开发依赖)<br>yarn add -O &lt;package-name&gt;  将包安装并记录到 optionalDependencies (可选依赖)<br>yarn add &lt;package&gt;@&lt;version&gt;  安装某个包的指定版本<br>yarn remove &lt;package-name&gt;  卸载包并从 package.json 中移除记录<br>yarn upgrade  按照 package.json 的版本范围升级所有包<br>yarn upgrade &lt;package-name&gt;  仅升级指定的包<br>yarn upgrade-interactive  提供一个交互式界面让你勾选要升级的包<br>yarn upgrade-interactive --latest  交互式升级，且允许跨越主版本号升到最新<br>yarn outdated  检查并列出哪些包有新版本可更新<br></code></pre></td></tr></table></figure><h3 id="5-5-查看依赖信息"><a href="#5-5-查看依赖信息" class="headerlink" title="5.5 查看依赖信息"></a>5.5 查看依赖信息</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn list  树状打印当前项目安装的所有依赖<br>yarn list --depth=0  仅打印第一层直接依赖，不展开子依赖<br>yarn why &lt;package&gt;  查询解释为什么当前项目里会安装这个包<br>yarn info &lt;package&gt;  查看某个包在远程仓库中的版本、协议等详细信息<br>yarn cache clean  清理 Yarn 下载在本地的全局缓存包<br>yarn cache list  列出当前 Yarn 缓存的所有包<br></code></pre></td></tr></table></figure><h3 id="5-6-初始化项目"><a href="#5-6-初始化项目" class="headerlink" title="5.6 初始化项目"></a>5.6 初始化项目</h3><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><code class="hljs bash">yarn init  通过一问一答的方式创建一个新的 package.json<br>yarn init -y  跳过所有提问，直接使用默认值创建 package.json<br></code></pre></td></tr></table></figure><h3 id="5-7-运行项目脚本"><a href="#5-7-运行项目脚本" class="headerlink" title="5.7 运行项目脚本"></a>5.7 运行项目脚本</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn run  列出 package.json 里定义的所有可用脚本<br>yarn start  运行名为 start 的脚本（通常用于启动项目）<br>yarn dev  运行名为 dev 的脚本（通常用于本地开发）<br>yarn serve  运行名为 serve 的脚本（通常用于启动本地服务器）<br>yarn start:dev  运行自定义名字的 start:dev 脚本<br>yarn start:<span class="hljs-built_in">local</span>  运行自定义名字的 start:<span class="hljs-built_in">local</span> 脚本<br>yarn start:win-local  运行自定义名字的 start:win-local 脚本<br>yarn start-win-local  运行自定义名字的 start-win-local 脚本<br></code></pre></td></tr></table></figure><p>理解方式：</p><ul><li><code>yarn run</code>：列出或运行脚本</li><li><code>yarn start</code>、<code>yarn dev</code>：是否可用，取决于 <code>package.json</code> 的 <code>scripts</code></li></ul><h3 id="5-8-查看项目有哪些脚本"><a href="#5-8-查看项目有哪些脚本" class="headerlink" title="5.8 查看项目有哪些脚本"></a>5.8 查看项目有哪些脚本</h3><p>Windows 常见写法：</p><figure class="highlight cmd"><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><code class="hljs cmd"><span class="hljs-built_in">type</span> package.json | <span class="hljs-built_in">findstr</span> &quot;scripts&quot;  在 package.json 中过滤出包含 scripts 的行<br><span class="hljs-built_in">type</span> package.json | <span class="hljs-built_in">findstr</span> &quot;<span class="hljs-built_in">start</span>-win-local&quot;  查找特定启动脚本的配置详情<br>notepad package.json  直接用记事本打开 package.json 查看<br></code></pre></td></tr></table></figure><p>类 Unix 风格写法：</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><code class="hljs bash"><span class="hljs-built_in">cat</span> package.json | grep <span class="hljs-string">&quot;scripts&quot;</span> -A 10  找到 scripts 所在行，并往下多看 10 行<br><span class="hljs-built_in">cat</span> package.json | findstr <span class="hljs-string">&quot;start:win-local&quot;</span>  跨平台终端里用 findstr 查找（混用示例）<br></code></pre></td></tr></table></figure><p>更直接的方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn run  直接让 Yarn 列出所有可执行的脚本清单<br></code></pre></td></tr></table></figure><h3 id="5-9-Yarn-与-npm-的常见对照"><a href="#5-9-Yarn-与-npm-的常见对照" class="headerlink" title="5.9 Yarn 与 npm 的常见对照"></a>5.9 Yarn 与 npm 的常见对照</h3><table><thead><tr><th>目的</th><th>Yarn</th><th>npm</th></tr></thead><tbody><tr><td>安装所有依赖</td><td><code>yarn install</code></td><td><code>npm install</code></td></tr><tr><td>添加依赖</td><td><code>yarn add axios</code></td><td><code>npm install axios</code></td></tr><tr><td>删除依赖</td><td><code>yarn remove axios</code></td><td><code>npm uninstall axios</code></td></tr><tr><td>运行脚本</td><td><code>yarn start</code></td><td><code>npm start</code> &#x2F; <code>npm run start</code></td></tr></tbody></table><h3 id="5-10-什么时候要重新执行-Yarn"><a href="#5-10-什么时候要重新执行-Yarn" class="headerlink" title="5.10 什么时候要重新执行 Yarn"></a>5.10 什么时候要重新执行 Yarn</h3><h4 id="关闭终端后"><a href="#关闭终端后" class="headerlink" title="关闭终端后"></a>关闭终端后</h4><ul><li>不需要重新执行 <code>yarn install</code></li><li>需要重新执行 <code>yarn start</code> 或 <code>yarn dev</code></li></ul><h4 id="什么时候才需要重新安装依赖"><a href="#什么时候才需要重新安装依赖" class="headerlink" title="什么时候才需要重新安装依赖"></a>什么时候才需要重新安装依赖</h4><ul><li>第一次拿到项目</li><li><code>package.json</code> 或锁文件变了</li><li>依赖损坏需要重装</li></ul><hr><h2 id="6-Yarn-Link：本地包联调"><a href="#6-Yarn-Link：本地包联调" class="headerlink" title="6. Yarn Link：本地包联调"></a>6. Yarn Link：本地包联调</h2><h3 id="6-1-建立链接"><a href="#6-1-建立链接" class="headerlink" title="6.1 建立链接"></a>6.1 建立链接</h3><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><code class="hljs bash">yarn <span class="hljs-built_in">link</span>  在本地包目录下执行，将其注册到全局 <span class="hljs-built_in">link</span> 列表<br>yarn <span class="hljs-built_in">link</span> &lt;package-name&gt;  在使用方项目中执行，将包软链接过来<br></code></pre></td></tr></table></figure><h3 id="6-2-取消链接"><a href="#6-2-取消链接" class="headerlink" title="6.2 取消链接"></a>6.2 取消链接</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn <span class="hljs-built_in">unlink</span>  在本地包目录下执行，将其从全局 <span class="hljs-built_in">link</span> 列表移除<br>yarn <span class="hljs-built_in">unlink</span> &lt;package-name&gt;  在使用方项目中执行，断开软链接<br>yarn add &lt;package-name&gt; --force  强制重新下载并覆盖，清除可能残留的链接状态<br></code></pre></td></tr></table></figure><h3 id="6-3-查看与排查"><a href="#6-3-查看与排查" class="headerlink" title="6.3 查看与排查"></a>6.3 查看与排查</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn <span class="hljs-built_in">link</span> list  列出当前项目建立的所有软链接<br>yarn config get nmLink  查看 Yarn 关于 <span class="hljs-built_in">link</span> 的底层配置机制<br><span class="hljs-built_in">ls</span> -la node_modules/&lt;package-name&gt;  查看该模块是否是一个指向其他路径的快捷方式<br></code></pre></td></tr></table></figure><h3 id="6-4-关闭终端后会怎样"><a href="#6-4-关闭终端后会怎样" class="headerlink" title="6.4 关闭终端后会怎样"></a>6.4 关闭终端后会怎样</h3><p>学习时可以先这样记：</p><ul><li>全局注册这一步通常保留</li><li>使用方项目里的联调关系、启动进程经常需要重新处理</li><li>如果你长期本地联调，<code>file:</code> 依赖或 monorepo&#x2F;workspaces 往往更省心</li></ul><hr><h2 id="7-Node-js-与-NVM"><a href="#7-Node-js-与-NVM" class="headerlink" title="7. Node.js 与 NVM"></a>7. Node.js 与 NVM</h2><h3 id="7-1-为什么学-NVM"><a href="#7-1-为什么学-NVM" class="headerlink" title="7.1 为什么学 NVM"></a>7.1 为什么学 NVM</h3><p>NVM 用来管理多个 Node.js 版本。<br>典型场景：</p><ul><li>这个项目要 Node 16</li><li>另一个项目要 Node 18</li><li>你不想每次卸载重装 Node</li></ul><h3 id="7-2-安装指定版本、最新版、LTS-版"><a href="#7-2-安装指定版本、最新版、LTS-版" class="headerlink" title="7.2 安装指定版本、最新版、LTS 版"></a>7.2 安装指定版本、最新版、LTS 版</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash">nvm install 16  安装 Node 16 的最新小版本<br>nvm install 16.20.2  精确安装 Node 16.20.2 版本<br>nvm install latest  安装当前 Node.js 的最新版<br>nvm install lts  安装当前 Node.js 的长期维护版<br>nvm install node  (类 Unix 系统下) 安装最新的 Node.js<br>nvm install --lts  (类 Unix 系统下) 安装 LTS 版本的 Node.js<br></code></pre></td></tr></table></figure><p>说明：</p><ul><li>Windows 对话里提到：<code>nvm install latest</code></li><li>macOS &#x2F; Linux 对话里提到：<code>nvm install node</code></li><li><code>nvm install 16</code> 适合快速装主版本</li><li><code>nvm install 16.20.2</code> 适合精确版本</li></ul><h3 id="7-3-切换版本"><a href="#7-3-切换版本" class="headerlink" title="7.3 切换版本"></a>7.3 切换版本</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">nvm use 16  切换当前使用的 Node 版本为 16.x<br>nvm use latest  切换到已安装的最新版本<br>nvm use node  (类 Unix 系统下) 切换到默认/最新版本<br></code></pre></td></tr></table></figure><h3 id="7-4-查看当前状态"><a href="#7-4-查看当前状态" class="headerlink" title="7.4 查看当前状态"></a>7.4 查看当前状态</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">nvm <span class="hljs-built_in">ls</span>  列出本地已安装的所有 Node 版本<br>nvm list  nvm <span class="hljs-built_in">ls</span> 的另一种写法<br>nvm list available  (Windows下) 列出网络上可供下载的 Node 版本列表<br>nvm ls-remote  (类 Unix系统下) 列出网络上可供下载的版本列表<br>nvm current  显示当前正在使用的 Node 版本号<br>nvm version  查看 nvm 工具自身的版本号<br>nvm -v  nvm version 的简写<br>nvm <span class="hljs-built_in">arch</span>  查看 Node 运行在 32 位还是 64 位架构下<br></code></pre></td></tr></table></figure><h3 id="7-5-设置默认版本、卸载版本"><a href="#7-5-设置默认版本、卸载版本" class="headerlink" title="7.5 设置默认版本、卸载版本"></a>7.5 设置默认版本、卸载版本</h3><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><code class="hljs bash">nvm <span class="hljs-built_in">alias</span> default 16  设置打开新终端时默认使用的版本为 16<br>nvm <span class="hljs-built_in">alias</span> default &lt;版本号&gt;  设置默认版本为指定版本<br>nvm uninstall 16  卸载 Node 16 版本<br>nvm uninstall &lt;版本号&gt;  卸载指定的 Node 版本<br></code></pre></td></tr></table></figure><h3 id="7-6-其他常见命令"><a href="#7-6-其他常见命令" class="headerlink" title="7.6 其他常见命令"></a>7.6 其他常见命令</h3><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><code class="hljs bash">nvm on  启用 nvm 的版本管理接管功能<br>nvm off  停用 nvm，恢复系统原本的 Node 路径<br>nvm reinstall-packages  重装 npm 全局包到新切换的版本中<br>nvm run &lt;version&gt; &lt;script&gt;  不切换环境的情况下，临时用指定版本执行脚本<br></code></pre></td></tr></table></figure><h3 id="7-7-验证-Node-和-npm-是否正常"><a href="#7-7-验证-Node-和-npm-是否正常" class="headerlink" title="7.7 验证 Node 和 npm 是否正常"></a>7.7 验证 Node 和 npm 是否正常</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">node -v  打印当前使用的 Node 版本号<br>npm -v  打印当前使用的 npm 版本号<br>npm config get prefix  查看 npm 全局包的安装位置<br></code></pre></td></tr></table></figure><h3 id="7-8-其他安装方式（对话里提到过）"><a href="#7-8-其他安装方式（对话里提到过）" class="headerlink" title="7.8 其他安装方式（对话里提到过）"></a>7.8 其他安装方式（对话里提到过）</h3><p>Windows（直接安装系统级 Node 16）：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">winget install OpenJS.NodeJS.<span class="hljs-number">16</span>  使用 Windows 包管理器安装 Node <span class="hljs-number">16</span><br></code></pre></td></tr></table></figure><p>macOS &#x2F; Linux（安装 nvm 的脚本方式）：</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><code class="hljs bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash  用 curl 下载并执行 nvm 安装脚本<br>wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash  用 wget 下载并执行 nvm 安装脚本<br></code></pre></td></tr></table></figure><p>Linux（Debian&#x2F;Ubuntu 用 NodeSource 仓库安装 Node 16）：</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><code class="hljs bash">curl -fsSL https://deb.nodesource.com/setup_16.x | <span class="hljs-built_in">sudo</span> -E bash -  添加 NodeSource 的源配置<br><span class="hljs-built_in">sudo</span> apt install -y nodejs  使用 apt 最终安装 nodejs<br></code></pre></td></tr></table></figure><hr><h2 id="8-环境变量与跨平台写法"><a href="#8-环境变量与跨平台写法" class="headerlink" title="8. 环境变量与跨平台写法"></a>8. 环境变量与跨平台写法</h2><h3 id="8-1-同一个意思，不同终端写法不同"><a href="#8-1-同一个意思，不同终端写法不同" class="headerlink" title="8.1 同一个意思，不同终端写法不同"></a>8.1 同一个意思，不同终端写法不同</h3><p>设置 <code>NODE_OPTIONS</code> 的写法：</p><p>CMD：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cmd"><span class="hljs-built_in">set</span> NODE_OPTIONS=--no-experimental-fetch  在 <span class="hljs-built_in">CMD</span> 中临时设置环境变量<br></code></pre></td></tr></table></figure><p>PowerShell：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;--no-experimental-fetch&quot;</span>  在 PowerShell 中临时设置环境变量<br></code></pre></td></tr></table></figure><p>macOS &#x2F; Linux：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">export</span> NODE_OPTIONS=<span class="hljs-string">&quot;--no-experimental-fetch&quot;</span>  在 Bash/Zsh 中临时设置环境变量<br></code></pre></td></tr></table></figure><h3 id="8-2-撤销临时环境变量"><a href="#8-2-撤销临时环境变量" class="headerlink" title="8.2 撤销临时环境变量"></a>8.2 撤销临时环境变量</h3><p>PowerShell：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;&quot;</span>  将变量赋空，等同于撤销<br><span class="hljs-variable">$env:NODE_OPTIONS</span>  打印该变量验证是否已清空<br></code></pre></td></tr></table></figure><h3 id="8-3-为什么-yarn-start-no-experimental-fetch-会报错"><a href="#8-3-为什么-yarn-start-no-experimental-fetch-会报错" class="headerlink" title="8.3 为什么 yarn start --no-experimental-fetch 会报错"></a>8.3 为什么 <code>yarn start --no-experimental-fetch</code> 会报错</h3><p>因为这个参数是给 <code>node</code> 的，不是给 <code>yarn</code> 的。<br>正确思路是先设置环境变量，再执行启动命令。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs powershell"><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;--no-experimental-fetch&quot;</span>  先设置环境变量<br>yarn <span class="hljs-built_in">start</span>  然后再启动项目，项目里的 Node 就能读到上面的配置<br></code></pre></td></tr></table></figure><h3 id="8-4-Shell-配置相关命令"><a href="#8-4-Shell-配置相关命令" class="headerlink" title="8.4 Shell 配置相关命令"></a>8.4 Shell 配置相关命令</h3><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><code class="hljs bash"><span class="hljs-built_in">echo</span> <span class="hljs-variable">$SHELL</span>  查看当前使用的默认 Shell 类型（如 bash 还是 zsh）<br>nano ~/.zshrc  用终端文本编辑器 nano 修改 zsh 配置文件<br><span class="hljs-built_in">source</span> ~/.zshrc  让刚才修改的 zsh 配置文件立刻生效<br><span class="hljs-built_in">source</span> ~/.bashrc  让修改后的 bash 配置文件立刻生效<br></code></pre></td></tr></table></figure><hr><h2 id="9-运行文件与脚本"><a href="#9-运行文件与脚本" class="headerlink" title="9. 运行文件与脚本"></a>9. 运行文件与脚本</h2><h3 id="9-1-运行当前目录下的程序"><a href="#9-1-运行当前目录下的程序" class="headerlink" title="9.1 运行当前目录下的程序"></a>9.1 运行当前目录下的程序</h3><figure class="highlight powershell"><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><code class="hljs powershell">.\node_modules\.bin\webpack  运行当前目录下相对路径里的 webpack 命令<br>.\script.ps1  运行当前目录下的 PowerShell 脚本<br>.\build.bat  运行当前目录下的 CMD 批处理脚本<br></code></pre></td></tr></table></figure><p>在 PowerShell 中，当前目录下的文件通常要写 <code>.\</code>。</p><h3 id="9-2-运行指定路径程序"><a href="#9-2-运行指定路径程序" class="headerlink" title="9.2 运行指定路径程序"></a>9.2 运行指定路径程序</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs powershell">D:\tools\ffmpeg.exe <span class="hljs-literal">-version</span>  直接调用 D 盘绝对路径下的 exe 程序<br></code></pre></td></tr></table></figure><h3 id="9-3-运行常见脚本文件"><a href="#9-3-运行常见脚本文件" class="headerlink" title="9.3 运行常见脚本文件"></a>9.3 运行常见脚本文件</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs powershell">python .\app.py  用 Python 解释器执行 app.py 脚本<br>python app.py  同上，省略了 .\<br>node .\index.js  用 Node.js 执行 index.js 脚本<br>node index.js  同上，省略了 .\<br>PowerShell <span class="hljs-literal">-NoExit</span> <span class="hljs-operator">-File</span> <span class="hljs-string">&quot;.\myscript.ps1&quot;</span>  新开一个 PowerShell 跑脚本，跑完不退出窗口<br>msiexec /i .\installer.msi  调用 Windows 安装程序静默或显式安装 msi 包<br></code></pre></td></tr></table></figure><h3 id="9-4-在-PATH-里的程序可以直接运行"><a href="#9-4-在-PATH-里的程序可以直接运行" class="headerlink" title="9.4 在 PATH 里的程序可以直接运行"></a>9.4 在 PATH 里的程序可以直接运行</h3><figure class="highlight powershell"><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><code class="hljs powershell">node <span class="hljs-literal">--version</span>  由于 node 在系统环境变量 PATH 里，随处都可直接敲<br>npm install  npm 也在 PATH 里，直接敲即可<br>git status  git 也在 PATH 里，直接敲即可<br></code></pre></td></tr></table></figure><hr><h2 id="10-端口、进程、排错"><a href="#10-端口、进程、排错" class="headerlink" title="10. 端口、进程、排错"></a>10. 端口、进程、排错</h2><h3 id="10-1-查看端口是否被占用"><a href="#10-1-查看端口是否被占用" class="headerlink" title="10.1 查看端口是否被占用"></a>10.1 查看端口是否被占用</h3><figure class="highlight powershell"><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><code class="hljs powershell">netstat <span class="hljs-literal">-ano</span> | findstr :<span class="hljs-number">3000</span>  查找占用 <span class="hljs-number">3000</span> 端口的网络连接及进程 PID<br>netstat <span class="hljs-literal">-ano</span> | findstr :%port%  查找指定端口的占用情况（%port% 需替换为数字）<br><span class="hljs-built_in">Get-NetTCPConnection</span> <span class="hljs-literal">-LocalPort</span> <span class="hljs-number">3000</span>  PowerShell 原生查 <span class="hljs-number">3000</span> 端口占用的命令<br></code></pre></td></tr></table></figure><h3 id="10-2-结束占用端口的进程"><a href="#10-2-结束占用端口的进程" class="headerlink" title="10.2 结束占用端口的进程"></a>10.2 结束占用端口的进程</h3><figure class="highlight powershell"><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><code class="hljs powershell"><span class="hljs-built_in">Stop-Process</span> <span class="hljs-literal">-Id</span> <span class="hljs-number">1234</span> <span class="hljs-literal">-Force</span>  强制干掉进程 ID 为 <span class="hljs-number">1234</span> 的进程<br><span class="hljs-built_in">kill</span> <span class="hljs-literal">-Id</span> <span class="hljs-number">1234</span>  <span class="hljs-built_in">Stop-Process</span> 的简写版<br><span class="hljs-built_in">kill</span> <span class="hljs-literal">-Name</span> node  强制干掉所有名字叫 node 的进程<br></code></pre></td></tr></table></figure><h3 id="10-3-检查网络连通"><a href="#10-3-检查网络连通" class="headerlink" title="10.3 检查网络连通"></a>10.3 检查网络连通</h3><figure class="highlight powershell"><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><code class="hljs powershell"><span class="hljs-built_in">Test-Connection</span> google.com  类似 ping，测试能否连接对方服务器<br><span class="hljs-built_in">Test-NetConnection</span> google.com <span class="hljs-literal">-Port</span> <span class="hljs-number">443</span>  高级测试，明确测对方的 <span class="hljs-number">443</span> 端口通不通<br><span class="hljs-built_in">Get-NetIPAddress</span>  查自己电脑分配到了什么 IP<br></code></pre></td></tr></table></figure><hr><h2 id="11-批处理文件基础"><a href="#11-批处理文件基础" class="headerlink" title="11. 批处理文件基础"></a>11. 批处理文件基础</h2><h3 id="11-1-最常见的开头"><a href="#11-1-最常见的开头" class="headerlink" title="11.1 最常见的开头"></a>11.1 最常见的开头</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs batch">@echo off  告诉终端：后面的所有命令本身不要打印出来，只打印命令的结果<br></code></pre></td></tr></table></figure><p>作用：关闭后续命令回显，让输出更干净。</p><h3 id="11-2-注释"><a href="#11-2-注释" class="headerlink" title="11.2 注释"></a>11.2 注释</h3><figure class="highlight plaintext"><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><code class="hljs batch">REM 这是注释（标准注释写法示例）  使用 REM 关键字作为官方注释<br>:: 这也是注释（常见的“标签式”注释写法示例）  冒号开头本是标签，常被借用当注释<br>@REM 这也是注释（示例：@ 表示本行不回显）  确保即使没有 echo off，这行注释也不会打印<br></code></pre></td></tr></table></figure><h3 id="11-3-输出、暂停、退出"><a href="#11-3-输出、暂停、退出" class="headerlink" title="11.3 输出、暂停、退出"></a>11.3 输出、暂停、退出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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><code class="hljs batch">echo Hello  在屏幕上打印出 Hello 文字<br>echo.  打印一个空行<br>pause  暂停脚本执行，提示“请按任意键继续...”<br>exit /b 0  退出当前脚本并返回成功状态码 0<br></code></pre></td></tr></table></figure><h3 id="11-4-变量"><a href="#11-4-变量" class="headerlink" title="11.4 变量"></a>11.4 变量</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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><code class="hljs batch">set name=John  定义一个名为 name 的变量，值为 John<br>echo %name%  打印变量 name 的值<br>set /a num=5+3  进行数学计算，num 被赋值为 8<br>set /p input=请输入内容：  暂停并在屏幕提示，将用户的输入赋值给 input 变量<br></code></pre></td></tr></table></figure><h3 id="11-5-参数"><a href="#11-5-参数" class="headerlink" title="11.5 参数"></a>11.5 参数</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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><code class="hljs batch">echo %0  打印脚本本身的名字或路径<br>echo %1  打印运行脚本时传进来的第 1 个参数<br>echo %2  打印运行脚本时传进来的第 2 个参数<br>echo %*  打印运行脚本时传进来的所有参数<br></code></pre></td></tr></table></figure><h3 id="11-6-条件判断"><a href="#11-6-条件判断" class="headerlink" title="11.6 条件判断"></a>11.6 条件判断</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs batch">if &quot;%name%&quot;==&quot;John&quot; (  判断 name 变量的值是否等于 John<br>    echo yes  条件成立时打印 yes<br>) else (  否则<br>    echo no  条件不成立时打印 no<br>)<br><br>if exist &quot;C:\test.txt&quot; (  判断指定路径的文件存不存在<br>    echo exist  存在则打印 exist<br>)<br><br>if defined name (  判断 name 这个变量有没有被声明过<br>    echo defined  如果定义过则打印 defined<br>)<br></code></pre></td></tr></table></figure><p>常见比较符：</p><ul><li><code>EQU</code></li><li><code>NEQ</code></li><li><code>LSS</code></li><li><code>LEQ</code></li><li><code>GTR</code></li><li><code>GEQ</code></li></ul><h3 id="11-7-循环"><a href="#11-7-循环" class="headerlink" title="11.7 循环"></a>11.7 循环</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs batch">for %%f in (*.txt) do (  遍历当前目录下所有后缀为 .txt 的文件<br>    echo %%f  打印出找到的文件名<br>)<br><br>for /l %%i in (1,1,10) do (  计数循环，从 1 开始，每次加 1，直到 10<br>    echo %%i  打印当前数字<br>)<br><br>for /d %%d in (*) do (  遍历当前目录下所有的文件夹<br>    echo %%d  打印文件夹名<br>)<br><br>for /f &quot;tokens=*&quot; %%l in (file.txt) do (  逐行读取 file.txt 的文本内容<br>    echo %%l  打印读到的每一行<br>)<br></code></pre></td></tr></table></figure><h3 id="11-8-跳转"><a href="#11-8-跳转" class="headerlink" title="11.8 跳转"></a>11.8 跳转</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs batch">goto :label1  命令跳到 :label1 标记处继续执行<br><br>:label1  定义一个名为 label1 的跳转标记<br>echo hello  打印 hello<br>goto :end  跳到结尾处<br><br>:end  定义一个名为 end 的标记<br>echo done  打印 done<br></code></pre></td></tr></table></figure><h3 id="11-9-调用其他脚本和等待"><a href="#11-9-调用其他脚本和等待" class="headerlink" title="11.9 调用其他脚本和等待"></a>11.9 调用其他脚本和等待</h3><figure class="highlight plaintext"><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><code class="hljs batch">call other.bat  在当前脚本里调用另一个批处理，等它执行完再回来<br>timeout /t 5 /nobreak  倒计时 5 秒，且不允许用户按键跳过<br>start notepad.exe  启动记事本，不等待记事本关闭，脚本继续往下走<br></code></pre></td></tr></table></figure><h3 id="11-10-错误处理与静默输出"><a href="#11-10-错误处理与静默输出" class="headerlink" title="11.10 错误处理与静默输出"></a>11.10 错误处理与静默输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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><code class="hljs batch">copy file.txt D:\  尝试复制文件<br>if %errorlevel% equ 0 (  检查上一条复制命令的返回码是否为 0（代表成功）<br>    echo success  成功则打印<br>) else (  否则<br>    echo fail  失败则打印<br>)<br><br>copy file.txt D:\ &amp;&amp; echo success || echo fail  简写逻辑：成功打印 success，失败打印 fail<br>dir nonexistent 2&gt;nul  查不存在的文件时，把报错信息扔进黑洞不显示<br>command &gt;nul 2&gt;&amp;1  将命令的正常输出和报错输出全部隐藏<br></code></pre></td></tr></table></figure><h3 id="11-11-编码、标题、颜色、延迟变量"><a href="#11-11-编码、标题、颜色、延迟变量" class="headerlink" title="11.11 编码、标题、颜色、延迟变量"></a>11.11 编码、标题、颜色、延迟变量</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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><code class="hljs batch">chcp 65001 &gt;nul  将终端字符编码改为 UTF-8 以防中文乱码，且不打印提示<br>title 我的脚本  将终端窗口的左上角标题改成“我的脚本”<br>color 0a  改变终端字体颜色（0a 代表黑底亮绿字，黑客帝国风）<br>setlocal enabledelayedexpansion  开启变量延迟扩展（解决在 for/if 块中变量不更新的坑）<br></code></pre></td></tr></table></figure><h3 id="11-12-常见路径技巧"><a href="#11-12-常见路径技巧" class="headerlink" title="11.12 常见路径技巧"></a>11.12 常见路径技巧</h3><ul><li><code>%~dp0</code></li><li><code>%~d0</code></li><li><code>%~p0</code></li></ul><hr><h2 id="12-批处理里的文件操作"><a href="#12-批处理里的文件操作" class="headerlink" title="12. 批处理里的文件操作"></a>12. 批处理里的文件操作</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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><code class="hljs batch">md newfolder  创建一个名为 newfolder 的文件夹<br>mkdir new_folder  也是创建文件夹<br>rd /s /q folder  递归删除 folder 文件夹，不弹出确认提示<br>rmdir /s /q folder  和 rd 功能一样<br>del *.txt  删除当前目录下所有 txt 文件<br>del /f /q &quot;%temp%\*.*&quot; 2&gt;nul  强制、静默清理系统临时文件夹，忽略报错<br>copy a.txt b.txt  复制文件<br>copy config.ini backup_config.ini  备份配置文件<br>move a.txt D:\  移动文件<br>ren a.txt b.txt  重命名文件<br></code></pre></td></tr></table></figure><p>对话里也出现了下面这种“清理并重装依赖”的思路：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">rm</span> -rf node_modules  强制递归删除 node_modules 文件夹（Linux/macOS 写法）<br><span class="hljs-built_in">rm</span> -rf node_modules yarn.lock  一次性强制删除文件夹和锁文件<br>yarn install  重新安装纯净的依赖<br></code></pre></td></tr></table></figure><p>说明：这是类 Unix 风格的删除写法，在纯 Windows CMD 里不能直接照抄。</p><hr><h2 id="13-Redis-命令行基础"><a href="#13-Redis-命令行基础" class="headerlink" title="13. Redis 命令行基础"></a>13. Redis 命令行基础</h2><p>对话里还提到了 Redis 的一些入门命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">redis-cli <span class="hljs-built_in">set</span> username <span class="hljs-string">&quot;zhangsan&quot;</span>  连接 Redis 并存入一个 key 为 username 的数据<br>redis-cli get username  从 Redis 中取出 username 的值<br>redis-cli hset user:1001 name <span class="hljs-string">&quot;zhangsan&quot;</span> age 25  以哈希结构存入用户数据<br>redis-cli hget user:1001 name  从哈希结构中查出用户的 name<br>redis-cli -h &lt;host&gt; -p &lt;port&gt;  指定 IP 和端口连接到远程 Redis 数据库<br>keys *  在 Redis 控制台内，列出所有的 key<br>get some_key  在 Redis 控制台内，获取某个 key 的值<br>incr article:123:likes  在 Redis 控制台内，给文章点赞数自增 1<br></code></pre></td></tr></table></figure><p>可以把它理解成：</p><ul><li><code>set/get</code>：最基础的键值读写</li><li><code>hset/hget</code>：操作哈希</li><li><code>incr</code>：计数器自增</li></ul><hr><h2 id="14-多项目同时启动的理解"><a href="#14-多项目同时启动的理解" class="headerlink" title="14. 多项目同时启动的理解"></a>14. 多项目同时启动的理解</h2><p>如果一个文件夹下有多个项目，例如前端和后端，那么通常要这样做：</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><code class="hljs bash"><span class="hljs-built_in">cd</span> frontend  进入前端文件夹<br>yarn dev  启动前端开发服务<br></code></pre></td></tr></table></figure><p>再开一个新终端：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cd</span> backend  进入后端文件夹<br>yarn start  启动后端服务<br></code></pre></td></tr></table></figure><p>原因是：</p><ul><li><code>yarn dev</code></li><li><code>yarn start</code></li></ul><p>这类命令大多会持续占用当前终端。</p><hr><h2 id="15-关闭终端后哪些还有效"><a href="#15-关闭终端后哪些还有效" class="headerlink" title="15. 关闭终端后哪些还有效"></a>15. 关闭终端后哪些还有效</h2><h3 id="15-1-通常还有效"><a href="#15-1-通常还有效" class="headerlink" title="15.1 通常还有效"></a>15.1 通常还有效</h3><ul><li><code>git clone</code></li><li><code>yarn install</code></li><li><code>mkdir</code></li><li><code>copy</code></li></ul><p>因为这些命令把结果写进了硬盘。</p><h3 id="15-2-通常会失效"><a href="#15-2-通常会失效" class="headerlink" title="15.2 通常会失效"></a>15.2 通常会失效</h3><ul><li><code>yarn start</code></li><li><code>yarn dev</code></li><li>临时环境变量</li><li>当前窗口里运行的批处理进程</li></ul><p>因为这些依赖当前终端会话或当前运行中的进程。</p><hr><h2 id="16-建议你优先掌握的命令"><a href="#16-建议你优先掌握的命令" class="headerlink" title="16. 建议你优先掌握的命令"></a>16. 建议你优先掌握的命令</h2><p>如果你是从零开始学命令行，建议按这个顺序练：</p><h3 id="第-1-组：目录与文件"><a href="#第-1-组：目录与文件" class="headerlink" title="第 1 组：目录与文件"></a>第 1 组：目录与文件</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs cmd"><span class="hljs-built_in">cd</span>  查路径<br><span class="hljs-built_in">cd</span> ..  回上级<br><span class="hljs-built_in">cd</span> /d D:\project  切盘符进项目<br><span class="hljs-built_in">dir</span>  看文件<br><span class="hljs-built_in">dir</span> /b  精简看文件<br><span class="hljs-built_in">tree</span> /f  看目录树<br><span class="hljs-built_in">cls</span>  清屏<br></code></pre></td></tr></table></figure><h3 id="第-2-组：项目启动"><a href="#第-2-组：项目启动" class="headerlink" title="第 2 组：项目启动"></a>第 2 组：项目启动</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> &lt;仓库地址&gt;  下载代码<br><span class="hljs-built_in">cd</span> 项目目录  进代码目录<br>yarn install  装依赖<br>yarn start  跑服务<br>yarn run  看脚本<br></code></pre></td></tr></table></figure><h3 id="第-3-组：Node-版本管理"><a href="#第-3-组：Node-版本管理" class="headerlink" title="第 3 组：Node 版本管理"></a>第 3 组：Node 版本管理</h3><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><code class="hljs bash">nvm install 16  装指定版本<br>nvm use 16  切换版本<br>node -v  检查 node 生效没<br>npm -v  检查 npm 生效没<br></code></pre></td></tr></table></figure><h3 id="第-4-组：排错"><a href="#第-4-组：排错" class="headerlink" title="第 4 组：排错"></a>第 4 组：排错</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><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><code class="hljs powershell">netstat <span class="hljs-literal">-ano</span> | findstr :<span class="hljs-number">3000</span>  查端口谁占了<br><span class="hljs-built_in">Get-NetTCPConnection</span> <span class="hljs-literal">-LocalPort</span> <span class="hljs-number">3000</span>  也是查端口<br><span class="hljs-built_in">Stop-Process</span> <span class="hljs-literal">-Id</span> <span class="hljs-number">1234</span> <span class="hljs-literal">-Force</span>  强杀进程<br><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;&quot;</span>  清空异常环境变量<br></code></pre></td></tr></table></figure><h3 id="第-5-组：批处理自动化"><a href="#第-5-组：批处理自动化" class="headerlink" title="第 5 组：批处理自动化"></a>第 5 组：批处理自动化</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs batch">@echo off  关回显<br>set name=demo  设变量<br>if exist &quot;a.txt&quot; echo yes  判断文件存在<br>for %%f in (*.txt) do echo %%f  循环遍历<br>pause  暂停看结果<br></code></pre></td></tr></table></figure><hr><h2 id="17-最后给你的学习结论"><a href="#17-最后给你的学习结论" class="headerlink" title="17. 最后给你的学习结论"></a>17. 最后给你的学习结论</h2><p>你可以把这次对话里出现的命令行知识总结成下面这几句话：</p><ol><li>命令行最先学的是“目录”和“当前路径”。</li><li>项目开发最常见的是 <code>git clone</code>、<code>yarn install</code>、<code>yarn start</code>。</li><li>Node 版本不对时，优先想到 <code>nvm install</code> 和 <code>nvm use</code>。</li><li>服务起不来时，优先检查脚本、环境变量、端口占用。</li><li>批处理适合把重复步骤写成脚本自动执行。</li><li>关闭终端后，长期运行的服务会停，但写进硬盘的结果会保留。</li></ol><h2 id="附录：命令行核心速查表"><a href="#附录：命令行核心速查表" class="headerlink" title="附录：命令行核心速查表"></a>附录：命令行核心速查表</h2><h3 id="1-目录与基础操作"><a href="#1-目录与基础操作" class="headerlink" title="1. 目录与基础操作"></a>1. 目录与基础操作</h3><table><thead><tr><th>目标</th><th>Windows CMD</th><th>PowerShell</th><th>macOS &#x2F; Linux</th></tr></thead><tbody><tr><td>查看当前路径</td><td><code>cd</code></td><td><code>pwd</code> 或 <code>Get-Location</code></td><td><code>pwd</code></td></tr><tr><td>进入目录</td><td><code>cd folder</code></td><td><code>cd folder</code></td><td><code>cd folder</code></td></tr><tr><td>返回上一级</td><td><code>cd ..</code></td><td><code>cd ..</code></td><td><code>cd ..</code></td></tr><tr><td>跨盘符切换</td><td><code>cd /d D:\folder</code></td><td><code>cd D:\folder</code></td><td>不适用</td></tr><tr><td>列出文件</td><td><code>dir</code></td><td><code>ls</code> 或 <code>Get-ChildItem</code></td><td><code>ls</code></td></tr><tr><td>清屏</td><td><code>cls</code></td><td><code>cls</code> 或 <code>Clear-Host</code></td><td><code>clear</code></td></tr></tbody></table><h3 id="2-Node-与-NVM-版本管理"><a href="#2-Node-与-NVM-版本管理" class="headerlink" title="2. Node 与 NVM (版本管理)"></a>2. Node 与 NVM (版本管理)</h3><table><thead><tr><th>命令</th><th>作用说明</th></tr></thead><tbody><tr><td><code>node -v</code></td><td>验证并查看当前 Node.js 版本</td></tr><tr><td><code>npm -v</code></td><td>验证并查看当前 npm 版本</td></tr><tr><td><code>nvm ls</code></td><td>查看本地已安装的所有 Node 版本</td></tr><tr><td><code>nvm install 16</code></td><td>安装 Node.js 16 版本</td></tr><tr><td><code>nvm use 16</code></td><td>切换并使用 Node.js 16 版本</td></tr><tr><td><code>nvm alias default 16</code></td><td>设置默认启动使用的版本为 16</td></tr></tbody></table><h3 id="3-Yarn-包管理与脚本"><a href="#3-Yarn-包管理与脚本" class="headerlink" title="3. Yarn (包管理与脚本)"></a>3. Yarn (包管理与脚本)</h3><table><thead><tr><th>命令</th><th>作用说明</th><th>等效的 npm 命令</th></tr></thead><tbody><tr><td><code>yarn install</code> 或 <code>yarn</code></td><td>根据 package.json 安装所有依赖</td><td><code>npm install</code></td></tr><tr><td><code>yarn add &lt;pkg&gt;</code></td><td>安装并添加某个依赖到生产环境</td><td><code>npm install &lt;pkg&gt;</code></td></tr><tr><td><code>yarn start</code></td><td>启动项目服务 (依赖 package.json scripts)</td><td><code>npm start</code></td></tr><tr><td><code>yarn dev</code></td><td>启动开发服务</td><td><code>npm run dev</code></td></tr><tr><td><code>yarn run</code></td><td>列出当前项目有哪些脚本可以运行</td><td><code>npm run</code></td></tr><tr><td><code>yarn cache clean</code></td><td>清理 Yarn 本地缓存</td><td><code>npm cache clean --force</code></td></tr></tbody></table><h3 id="4-环境变量-临时设置"><a href="#4-环境变量-临时设置" class="headerlink" title="4. 环境变量 (临时设置)"></a>4. 环境变量 (临时设置)</h3><table><thead><tr><th>终端类型</th><th>设置语法示例</th><th>撤销&#x2F;清空示例</th></tr></thead><tbody><tr><td>Windows CMD</td><td><code>set NODE_OPTIONS=--abc</code></td><td><code>set NODE_OPTIONS=</code></td></tr><tr><td>PowerShell</td><td><code>$env:NODE_OPTIONS=&quot;--abc&quot;</code></td><td><code>$env:NODE_OPTIONS=&quot;&quot;</code></td></tr><tr><td>macOS &#x2F; Linux</td><td><code>export NODE_OPTIONS=&quot;--abc&quot;</code></td><td><code>unset NODE_OPTIONS</code></td></tr></tbody></table><h3 id="5-端口与排错"><a href="#5-端口与排错" class="headerlink" title="5. 端口与排错"></a>5. 端口与排错</h3><table><thead><tr><th>目标</th><th>Windows PowerShell</th></tr></thead><tbody><tr><td>查 3000 端口被谁占用</td><td><code>netstat -ano | findstr :3000</code></td></tr><tr><td>强制结束指定 ID 进程</td><td><code>kill -Id 1234</code> 或 <code>Stop-Process -Id 1234 -Force</code></td></tr><tr><td>强制结束名为 node 进程</td><td><code>kill -Name node</code></td></tr><tr><td>测试网络能否连通</td><td><code>Test-Connection google.com</code></td></tr></tbody></table><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1775182229767" data-twikoo-path="article_1775182229767"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/02/ming-ling-xing-xue-xi-zhi-nan/</id>
    <link href="https://aoiblog.top/2026/04/02/ming-ling-xing-xue-xi-zhi-nan/"/>
    <published>2026-04-02T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>命令行学习指南</h2>
## 1. 先建立正确认识

<h3 id="1-1-什么是命令行"><a href="#1-1-什么是命令行" class="headerlink" title="1.1 什么是命令行"></a>1.1 什么是命令行</h3><p>命令行就是通]]>
    </summary>
    <title>命令行学习指南</title>
    <updated>2026-04-02T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="产品" scheme="https://aoiblog.top/categories/%E4%BA%A7%E5%93%81/"/>
    <content>
      <![CDATA[<h2>业务与产品</h2>2026年4月3日18:09:58先把我和ai讨论的内容总结放上来，之后我会结合我实习的经历详细谈谈我自己对业务和产品的理解<h2 id="一、什么是“业务”"><a href="#一、什么是“业务”" class="headerlink" title="一、什么是“业务”"></a>一、什么是“业务”</h2><p>业务，不只是某个产品或某个功能，而是一整套“创造价值、交付价值、获得收入和利润”的商业闭环。</p><p>理解一项业务，先抓住三个基本问题：</p><ol><li>为谁服务：目标客户是谁</li><li>解决什么问题：提供了什么价值</li><li>怎么赚钱：用什么方式盈利和交付</li></ol><p>可以把这三句话当成分析任何业务的最基础框架：</p><ul><li>客户</li><li>价值</li><li>盈利与交付</li></ul><p>只要这三个答案明显不同，就可能是公司里的不同业务。</p><p>比如，不同业务常见的区分方式有：</p><ul><li>客户不同</li><li>需求或痛点不同</li><li>产品形态不同</li><li>盈利模式不同</li><li>所需核心能力不同</li></ul><h2 id="二、业务、产品、商业模式、战略的区别"><a href="#二、业务、产品、商业模式、战略的区别" class="headerlink" title="二、业务、产品、商业模式、战略的区别"></a>二、业务、产品、商业模式、战略的区别</h2><h3 id="1-业务-vs-产品"><a href="#1-业务-vs-产品" class="headerlink" title="1. 业务 vs. 产品"></a>1. 业务 vs. 产品</h3><ul><li>产品是业务的一部分</li><li>业务不只包含产品，还包含获客、成交、交付、服务、复购、盈利</li></ul><p>一句话理解：</p><ul><li>产品更像“工具或载体”</li><li>业务更像“围绕这个工具持续创造和变现的闭环”</li></ul><h3 id="2-业务-vs-商业模式"><a href="#2-业务-vs-商业模式" class="headerlink" title="2. 业务 vs. 商业模式"></a>2. 业务 vs. 商业模式</h3><ul><li>商业模式是公司整体怎么赚钱的逻辑</li><li>业务是具体的价值创造单元</li></ul><p>一句话理解：</p><ul><li>商业模式回答“公司总体怎么赚”</li><li>业务回答“某一块具体怎么跑”</li></ul><h3 id="3-业务-vs-战略"><a href="#3-业务-vs-战略" class="headerlink" title="3. 业务 vs. 战略"></a>3. 业务 vs. 战略</h3><ul><li>战略是选择做什么、不做什么、怎么竞争</li><li>业务是被选择后，真正运转起来的那套机制</li></ul><p>一句话理解：</p><ul><li>战略偏选择</li><li>业务偏落地</li></ul><h2 id="三、什么叫“懂业务”"><a href="#三、什么叫“懂业务”" class="headerlink" title="三、什么叫“懂业务”"></a>三、什么叫“懂业务”</h2><p>懂业务，不是只会背“客户、价值、盈利模式”。</p><p>真正懂业务，是能把这三句话背后的细节、流程、数据、竞争和决策展开讲清楚。</p><p>可以把“懂业务”分成四层：</p><h3 id="第一层：知道是什么"><a href="#第一层：知道是什么" class="headerlink" title="第一层：知道是什么"></a>第一层：知道是什么</h3><p>知道：</p><ul><li>我们卖什么</li><li>卖给谁</li><li>怎么赚钱</li></ul><h3 id="第二层：知道怎么运转"><a href="#第二层：知道怎么运转" class="headerlink" title="第二层：知道怎么运转"></a>第二层：知道怎么运转</h3><p>知道：</p><ul><li>关键流程是什么</li><li>关键角色是谁</li><li>关键指标有哪些</li><li>成本主要发生在哪</li></ul><h3 id="第三层：知道为什么"><a href="#第三层：知道为什么" class="headerlink" title="第三层：知道为什么"></a>第三层：知道为什么</h3><p>理解：</p><ul><li>为什么这样设计业务</li><li>为什么这样定价</li><li>为什么先做A不做B</li><li>为什么竞争对手会这么做</li></ul><h3 id="第四层：知道怎么办"><a href="#第四层：知道怎么办" class="headerlink" title="第四层：知道怎么办"></a>第四层：知道怎么办</h3><p>能够：</p><ul><li>发现问题</li><li>拆解问题</li><li>判断优先级</li><li>预测影响</li><li>提出行动方案</li></ul><p>一句话概括：</p><p>懂业务 &#x3D; 知道定义 + 理解运转 + 看懂逻辑 + 能做决策</p><h2 id="四、判断自己是否真正懂业务的方法"><a href="#四、判断自己是否真正懂业务的方法" class="headerlink" title="四、判断自己是否真正懂业务的方法"></a>四、判断自己是否真正懂业务的方法</h2><p>一个很好的测试题是：</p><p>“如果这项业务的收入突然下降 30%，你觉得最可能的原因是什么？你会先查什么数据？”</p><p>只懂表面的人，往往只会说：</p><ul><li>客户不买了</li><li>市场变差了</li></ul><p>真正懂业务的人，会顺着链路去拆：</p><ul><li>新客户是不是变少了</li><li>线索来了是不是没转化</li><li>转化了是不是没支付</li><li>支付了是不是退款增多</li><li>交付环节是不是出了问题</li><li>竞品最近是不是有动作</li></ul><p>这背后体现的是一种很重要的能力：</p><ul><li>从结果倒推原因</li><li>从宏观拆到环节</li><li>从环节落到指标</li></ul><h2 id="五、BPMAX-案例的启发"><a href="#五、BPMAX-案例的启发" class="headerlink" title="五、BPMAX 案例的启发"></a>五、BPMAX 案例的启发</h2><p>文档用 BPMAX 做案例，不是为了记住这家公司本身，而是为了学会一套拆业务的方法。</p><h3 id="1-先用三句话定义业务"><a href="#1-先用三句话定义业务" class="headerlink" title="1. 先用三句话定义业务"></a>1. 先用三句话定义业务</h3><ul><li>为谁：头部连锁茶饮品牌</li><li>解决什么问题：把开店和运营流程在线化、自动化</li><li>怎么赚钱：SaaS 订阅费 + 定制实施服务费</li></ul><h3 id="2-再把每个词往深里展开"><a href="#2-再把每个词往深里展开" class="headerlink" title="2. 再把每个词往深里展开"></a>2. 再把每个词往深里展开</h3><p>比如“客户”不能只停留在“茶饮品牌”，还要继续追问：</p><ul><li>多大规模的客户</li><li>处于什么发展阶段</li><li>最核心的管理痛点是什么</li></ul><p>再比如“价值”不能只停留在“自动化”，还要追问：</p><ul><li>具体优化了哪一段流程</li><li>替代了原来的什么做法</li><li>给客户带来了什么效率提升或管理提升</li></ul><h3 id="3-再继续看业务全局"><a href="#3-再继续看业务全局" class="headerlink" title="3. 再继续看业务全局"></a>3. 再继续看业务全局</h3><p>要进一步关注：</p><ul><li>关键指标是什么</li><li>核心竞争力是什么</li><li>主要竞争对手是谁</li><li>增长机会在哪里</li></ul><h3 id="4-最后用问题来验证理解深度"><a href="#4-最后用问题来验证理解深度" class="headerlink" title="4. 最后用问题来验证理解深度"></a>4. 最后用问题来验证理解深度</h3><p>比如：</p><ul><li>新客户签约数为什么下降</li><li>哪个环节的转化率出了问题</li><li>是销售线索问题、产品演示问题、价格问题，还是竞品问题</li></ul><p>学案例，不是记案例本身，而是学会“先定义，再展开，再验证”的分析路径。</p><h2 id="六、技术人为什么也必须懂业务和产品"><a href="#六、技术人为什么也必须懂业务和产品" class="headerlink" title="六、技术人为什么也必须懂业务和产品"></a>六、技术人为什么也必须懂业务和产品</h2><p>在今天这个时代，技术岗懂业务和产品，不再只是加分项，而越来越接近必备能力。</p><h3 id="原因-1：开发越来越快，不能只当“翻译机”"><a href="#原因-1：开发越来越快，不能只当“翻译机”" class="headerlink" title="原因 1：开发越来越快，不能只当“翻译机”"></a>原因 1：开发越来越快，不能只当“翻译机”</h3><p>过去信息是层层传递的，开发把文档写成代码就行。</p><p>现在迭代更快，如果开发不懂业务，就会：</p><ul><li>反复沟通</li><li>反复返工</li><li>理解偏差</li><li>成为链路中的瓶颈</li></ul><h3 id="原因-2：AI-在替代标准编码，人的价值更偏向判断"><a href="#原因-2：AI-在替代标准编码，人的价值更偏向判断" class="headerlink" title="原因 2：AI 在替代标准编码，人的价值更偏向判断"></a>原因 2：AI 在替代标准编码，人的价值更偏向判断</h3><p>AI 能帮助写标准代码，但很难替代这些能力：</p><ul><li>理解真实场景</li><li>判断什么更重要</li><li>理解为什么用户在意这个指标</li><li>理解哪种实现更符合业务价值</li></ul><h3 id="原因-3：懂业务的技术人更有职业杠杆"><a href="#原因-3：懂业务的技术人更有职业杠杆" class="headerlink" title="原因 3：懂业务的技术人更有职业杠杆"></a>原因 3：懂业务的技术人更有职业杠杆</h3><p>懂业务的开发者，不只是执行需求，而是能：</p><ul><li>参与方案设计</li><li>反向给产品建议</li><li>理解需求优先级</li><li>用技术支持商业目标</li></ul><p>一句话理解：</p><ul><li>不懂业务，容易写出“能跑”的代码</li><li>懂业务，才可能写出“真正有商业价值”的代码</li></ul><h2 id="七、给技术岗的实践建议"><a href="#七、给技术岗的实践建议" class="headerlink" title="七、给技术岗的实践建议"></a>七、给技术岗的实践建议</h2><h3 id="1-多问一个“为什么”"><a href="#1-多问一个“为什么”" class="headerlink" title="1. 多问一个“为什么”"></a>1. 多问一个“为什么”</h3><p>接到需求时，不只问“做什么”，还要问：</p><ul><li>为什么要做</li><li>解决谁的问题</li><li>成功的标准是什么</li></ul><h3 id="2-主动接触客户声音"><a href="#2-主动接触客户声音" class="headerlink" title="2. 主动接触客户声音"></a>2. 主动接触客户声音</h3><p>比如：</p><ul><li>旁听客户支持</li><li>旁听销售沟通</li><li>看用户访谈记录</li></ul><p>因为客户真正的问题，往往和文档里的表述不完全一样。</p><h3 id="3-亲自走一遍产品流程"><a href="#3-亲自走一遍产品流程" class="headerlink" title="3. 亲自走一遍产品流程"></a>3. 亲自走一遍产品流程</h3><p>不要只看代码和接口，也要像真实用户一样使用产品，感受：</p><ul><li>操作顺不顺</li><li>哪一步最麻烦</li><li>哪一步最容易错</li></ul><h3 id="4-关注一个关键业务指标"><a href="#4-关注一个关键业务指标" class="headerlink" title="4. 关注一个关键业务指标"></a>4. 关注一个关键业务指标</h3><p>可以主动问：</p><ul><li>我负责的模块，最重要的业务指标是什么</li></ul><p>比如：</p><ul><li>流程完成时长</li><li>月活跃度</li><li>支付成功率</li><li>续费率</li></ul><h2 id="八、常见业务与产品术语"><a href="#八、常见业务与产品术语" class="headerlink" title="八、常见业务与产品术语"></a>八、常见业务与产品术语</h2><h3 id="1-商业模式与角色"><a href="#1-商业模式与角色" class="headerlink" title="1. 商业模式与角色"></a>1. 商业模式与角色</h3><ul><li>B2B：企业对企业</li><li>B2C：企业对消费者</li><li>B2B2C：企业通过企业服务最终消费者</li><li>SaaS：软件即服务</li><li>PaaS：平台即服务</li><li>IaaS：基础设施即服务</li><li>O2O：线上到线下</li><li>C2C：消费者对消费者</li><li>G2C：政府对个人</li></ul><h3 id="2-产品与研发"><a href="#2-产品与研发" class="headerlink" title="2. 产品与研发"></a>2. 产品与研发</h3><ul><li>MVP：最小可行产品</li><li>PRD：产品需求文档</li><li>UX：用户体验</li><li>UI：用户界面</li><li>API：应用程序接口</li><li>SDK：软件开发工具包</li><li>DAU&#x2F;WAU&#x2F;MAU：日&#x2F;周&#x2F;月活跃用户</li><li>ARR：年度经常性收入</li><li>NDR：净收入留存率</li></ul><h3 id="3-运营与增长"><a href="#3-运营与增长" class="headerlink" title="3. 运营与增长"></a>3. 运营与增长</h3><ul><li>CAC：获客成本</li><li>LTV：用户生命周期价值</li><li>LTV&#x2F;CAC：衡量商业模式是否健康的重要比值</li><li>ARPU：每用户平均收入</li><li>GMV：商品交易总额</li><li>ROI：投资回报率</li><li>SEO：搜索引擎优化</li><li>SEM：搜索引擎营销</li></ul><h3 id="4-战略与管理"><a href="#4-战略与管理" class="headerlink" title="4. 战略与管理"></a>4. 战略与管理</h3><ul><li>PMF：产品市场匹配</li><li>GTM：产品上市策略</li><li>KPI：关键绩效指标</li><li>OKR：目标与关键结果</li><li>P&amp;L：损益表</li><li>OP：运营计划</li></ul><h2 id="九、这份文档之外，同样很重要的业务与产品概念"><a href="#九、这份文档之外，同样很重要的业务与产品概念" class="headerlink" title="九、这份文档之外，同样很重要的业务与产品概念"></a>九、这份文档之外，同样很重要的业务与产品概念</h2><p>原文已经覆盖了很好的基础框架，但如果想真正建立完整认知，还要补上下面这些内容。</p><h3 id="1-用户分层"><a href="#1-用户分层" class="headerlink" title="1. 用户分层"></a>1. 用户分层</h3><p>不是所有客户都一样，要区分：</p><ul><li>核心用户和边缘用户</li><li>使用者和付费者</li><li>决策者、购买者、使用者、影响者</li></ul><p>这点在 B2B 里尤其重要。</p><h3 id="2-需求优先级"><a href="#2-需求优先级" class="headerlink" title="2. 需求优先级"></a>2. 需求优先级</h3><p>产品不是把所有需求都做完，而是判断：</p><ul><li>什么最重要</li><li>什么最紧急</li><li>什么最值得现在做</li><li>什么应该先放弃</li></ul><h3 id="3-指标体系"><a href="#3-指标体系" class="headerlink" title="3. 指标体系"></a>3. 指标体系</h3><p>不能只看一个总指标，还要有完整指标体系：</p><ul><li>北极星指标</li><li>过程指标</li><li>健康指标</li></ul><p>只有这样，才能既看结果，也看过程，还能防止“假增长”。</p><h3 id="4-留存与复购"><a href="#4-留存与复购" class="headerlink" title="4. 留存与复购"></a>4. 留存与复购</h3><p>新增用户很重要，但留存往往更重要。</p><p>要持续追问：</p><ul><li>用户为什么留下</li><li>用户为什么回来</li><li>用户为什么愿意继续付费</li></ul><h3 id="5-单位经济模型"><a href="#5-单位经济模型" class="headerlink" title="5. 单位经济模型"></a>5. 单位经济模型</h3><p>业务好不好，不只看规模，还要看单位经济是否成立：</p><ul><li>获得一个客户要花多少钱</li><li>一个客户能带来多少钱</li><li>回本周期多长</li><li>规模扩大后利润会不会变好</li></ul><h3 id="6-增长飞轮与网络效应"><a href="#6-增长飞轮与网络效应" class="headerlink" title="6. 增长飞轮与网络效应"></a>6. 增长飞轮与网络效应</h3><p>优秀业务不只是“买流量”，还会形成自增长结构。</p><p>例如：</p><ul><li>用户越多，体验越好</li><li>商家越多，消费者越多</li><li>数据越多，算法越准</li></ul><h3 id="7-竞争壁垒"><a href="#7-竞争壁垒" class="headerlink" title="7. 竞争壁垒"></a>7. 竞争壁垒</h3><p>不能只问“我们有什么功能”，还要问：</p><ul><li>别人为什么不容易复制我们</li></ul><p>常见壁垒包括：</p><ul><li>品牌</li><li>渠道</li><li>数据</li><li>供应链</li><li>网络效应</li><li>组织执行力</li></ul><h3 id="8-组织协同"><a href="#8-组织协同" class="headerlink" title="8. 组织协同"></a>8. 组织协同</h3><p>产品成功不是产品经理一个人的功劳，而是多个职能共同完成：</p><ul><li>产品</li><li>研发</li><li>运营</li><li>销售</li><li>客服</li><li>财务</li></ul><p>很多“产品问题”，本质其实是协同问题。</p><h2 id="十、通用业务分析框架"><a href="#十、通用业务分析框架" class="headerlink" title="十、通用业务分析框架"></a>十、通用业务分析框架</h2><p>以后分析任何一家公司、一个产品、一条业务线，都可以按这个顺序来：</p><ol><li>客户是谁</li><li>解决什么问题</li><li>怎么赚钱</li><li>业务怎么运转</li><li>关键指标是什么</li><li>成本和风险在哪</li><li>竞争对手是谁</li><li>核心优势和壁垒是什么</li><li>当前增长机会在哪</li><li>如果结果变差，先查什么</li></ol><h2 id="十一、思维导图式提纲"><a href="#十一、思维导图式提纲" class="headerlink" title="十一、思维导图式提纲"></a>十一、思维导图式提纲</h2><h3 id="业务与产品"><a href="#业务与产品" class="headerlink" title="业务与产品"></a>业务与产品</h3><ul><li><ol><li>什么是业务</li></ol><ul><li>为谁服务</li><li>解决什么问题</li><li>怎么赚钱</li></ul></li><li><ol start="2"><li>相关概念区分</li></ol><ul><li>业务 vs 产品</li><li>业务 vs 商业模式</li><li>业务 vs 战略</li></ul></li><li><ol start="3"><li>什么叫懂业务</li></ol><ul><li>知道是什么</li><li>知道怎么运转</li><li>知道为什么</li><li>知道怎么办</li></ul></li><li><ol start="4"><li>如何验证是否懂业务</li></ol><ul><li>收入下降如何拆解</li><li>从结果倒推环节</li><li>从环节定位指标</li></ul></li><li><ol start="5"><li>案例学习方法</li></ol><ul><li>先用三句话定义</li><li>再展开客户、价值、盈利</li><li>再看指标、竞争、增长</li><li>最后用问题检验</li></ul></li><li><ol start="6"><li>技术人为什么要懂业务</li></ol><ul><li>迭代更快</li><li>AI 替代标准编码</li><li>决策价值上升</li><li>职业发展更强</li></ul></li><li><ol start="7"><li>技术人的行动建议</li></ol><ul><li>多问为什么</li><li>听客户声音</li><li>亲自用产品</li><li>关注关键指标</li></ul></li><li><ol start="8"><li>常见术语</li></ol><ul><li>商业模式类</li><li>产品研发类</li><li>增长运营类</li><li>战略管理类</li></ul></li><li><ol start="9"><li>文档外的重要补充</li></ol><ul><li>用户分层</li><li>需求优先级</li><li>指标体系</li><li>留存与复购</li><li>单位经济模型</li><li>增长飞轮</li><li>竞争壁垒</li><li>组织协同</li></ul></li><li><ol start="10"><li>通用分析框架</li></ol><ul><li>客户</li><li>价值</li><li>盈利</li><li>运转</li><li>指标</li><li>成本与风险</li><li>竞争</li><li>壁垒</li><li>增长</li><li>排查路径</li></ul></li></ul><h2 id="十二、适合反复提醒自己的几句话"><a href="#十二、适合反复提醒自己的几句话" class="headerlink" title="十二、适合反复提醒自己的几句话"></a>十二、适合反复提醒自己的几句话</h2><ul><li>业务的本质，是价值创造、价值交付和价值变现的闭环。</li><li>产品不是目的，产品是承载业务价值的手段。</li><li>懂业务，不是会描述现状，而是能解释变化、判断问题、推动决策。</li><li>技术、产品、运营最终都要回到业务价值。</li><li>只看功能容易忙，理解业务才能真正做成事。</li></ul><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1775210699454" data-twikoo-path="article_1775210699454"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/04/02/ye-wu-yu-chan-pin/</id>
    <link href="https://aoiblog.top/2026/04/02/ye-wu-yu-chan-pin/"/>
    <published>2026-04-02T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>业务与产品</h2>
2026年4月3日18:09:58
先把我和ai讨论的内容总结放上来，之后我会结合我实习的经历详细谈谈我自己对业务和产品的理解

<h2 id="一、什么是“业务”"><a href="#一、什么是“业务”" class="headerlink" t]]>
    </summary>
    <title>业务与产品</title>
    <updated>2026-04-02T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>SQL</h2># SQL学习笔记<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li>1 SQL基础<ul><li>1.1 SQL通用语法</li><li>1.2 SQL分类</li></ul></li><li>2 DDL<ul><li>2.1 数据库操作<ul><li>2.1.1 查询</li><li>2.1.2 创建</li><li>2.1.3 删除</li><li>2.1.4 使用</li></ul></li><li>2.2 表操作<ul><li>2.2.1 查询</li><li>2.2.2 创建</li><li>2.2.3 修改</li><li>2.2.4 删除</li></ul></li><li>2.3 SQL常用字段和字段类型<ul><li>2.3.1 一、数值类型（Numeric Types）<ul><li>2.3.1.1 整数类型</li><li>2.3.1.2 小数类型</li></ul></li><li>2.3.2 二、字符串类型（String Types）<ul><li>2.3.2.1 定长 vs 变长</li><li>2.3.2.2 大文本类型</li></ul></li><li>2.3.3 三、日期和时间类型（Date &amp; Time）</li><li>2.3.4 四、枚举与集合类型（慎用）</li><li>2.3.5 五、二进制与特殊类型</li><li>2.3.6 六、选择字段类型的黄金原则</li><li>2.3.7 七、常见字段类型搭配示例</li></ul></li></ul></li><li>3 DML<ul><li>3.1 添加数据</li><li>3.2 修改数据</li><li>3.3 删除数据</li></ul></li><li>4 DQL<ul><li>4.1 语法<ul><li>4.1.1 常见查询分类</li></ul></li><li>4.2 基本查询<ul><li>4.2.1 查询多个字段</li><li>4.2.2 设置别名</li><li>4.2.3 去除重复记录</li></ul></li><li>4.3 条件查询<ul><li>4.3.1 语法<ul><li>4.3.1.1 比较运算符</li><li>4.3.1.2 逻辑运算符</li></ul></li></ul></li><li>4.4 聚合函数<ul><li>4.4.1 介绍</li><li>4.4.2 常见聚合函数</li><li>4.4.3 语法</li></ul></li><li>4.5 分组查询<ul><li>4.5.1 语法</li><li>4.5.2 <code>WHERE</code> 与 <code>HAVING</code> 区别</li></ul></li><li>4.6 排序查询<ul><li>4.6.1 语法</li><li>4.6.2 排序方式</li></ul></li><li>4.7 分页查询<ul><li>4.7.1 语法</li></ul></li><li>4.8 执行顺序<ul><li>4.8.1 编写顺序</li><li>4.8.2 执行顺序</li></ul></li></ul></li><li>5 DCL<ul><li>5.1 管理用户<ul><li>5.1.1 查询用户</li><li>5.1.2 创建用户</li><li>5.1.3 修改用户密码</li><li>5.1.4 删除用户</li></ul></li><li>5.2 权限控制<ul><li>5.2.1 常用权限</li><li>5.2.2 查询权限</li><li>5.2.3 授予权限</li><li>5.2.4 撤销权限</li></ul></li></ul></li><li>6 常用函数<ul><li>6.1 常用函数概述</li><li>6.2 字符串函数<ul><li>6.2.1 函数说明</li><li>6.2.2 示例</li><li>6.2.3 案例</li></ul></li><li>6.3 数值函数<ul><li>6.3.1 函数说明</li><li>6.3.2 示例</li><li>6.3.3 案例</li></ul></li><li>6.4 日期函数<ul><li>6.4.1 函数说明</li><li>6.4.2 示例</li><li>6.4.3 案例</li></ul></li><li>6.5 流程函数<ul><li>6.5.1 函数说明</li><li>6.5.2 示例</li><li>6.5.3 案例1</li><li>6.5.4 案例2</li></ul></li><li>6.6 窗口函数<ul><li>6.6.1 概述</li><li>6.6.2 基本语法</li><li>6.6.3 常见窗口函数</li><li>6.6.4 排名函数示例</li><li>6.6.5 分析函数示例</li></ul></li></ul></li><li>7 约束<ul><li>7.1 概述</li><li>7.2 常见约束</li><li>7.3 约束示例</li><li>7.4 外键约束<ul><li>7.4.1 介绍</li><li>7.4.2 添加外键</li><li>7.4.3 删除外键</li><li>7.4.4 删除 &#x2F; 更新行为</li><li>7.4.5 指定级联行为</li></ul></li></ul></li><li>8 多表查询<ul><li>8.1 多表关系<ul><li>8.1.1 一对多</li><li>8.1.2 多对多</li><li>8.1.3 一对一</li></ul></li><li>8.2 多表查询概述<ul><li>8.2.1 概述</li><li>8.2.2 分类</li><li>8.2.3 连接查询分类</li></ul></li><li>8.3 内连接<ul><li>8.3.1 隐式内连接</li><li>8.3.2 显式内连接</li><li>8.3.3 示例</li></ul></li><li>8.4 外连接<ul><li>8.4.1 左外连接</li><li>8.4.2 右外连接</li><li>8.4.3 示例</li></ul></li><li>8.5 自连接<ul><li>8.5.1 语法</li><li>8.5.2 示例</li></ul></li><li>8.6 联合查询<ul><li>8.6.1 语法</li><li>8.6.2 说明</li><li>8.6.3 示例</li></ul></li><li>8.7 子查询<ul><li>8.7.1 概述<ul><li>8.7.1.1 按结果分类</li><li>8.7.1.2 按位置分类</li></ul></li></ul></li><li>8.8 标量子查询<ul><li>8.8.1 常用操作符</li><li>8.8.2 示例</li></ul></li><li>8.9 列子查询<ul><li>8.9.1 常用操作符</li><li>8.9.2 示例</li></ul></li><li>8.10 行子查询<ul><li>8.10.1 常用操作符</li><li>8.10.2 示例</li></ul></li><li>8.11 表子查询<ul><li>8.11.1 常用操作符</li><li>8.11.2 示例</li></ul></li><li>8.12 多表查询案例<ul><li>8.12.1 查询所有部门信息，并统计部门员工人数</li><li>8.12.2 查询所有学生的选课情况</li></ul></li></ul></li><li>9 事务<ul><li>9.1 事务简介</li><li>9.2 事务操作<ul><li>9.2.1 方式一：控制自动提交</li><li>9.2.2 方式二：手动开启事务</li><li>9.2.3 转账案例</li></ul></li><li>9.3 事务四大特性</li><li>9.4 并发事务问题<ul><li>9.4.1 脏读</li><li>9.4.2 不可重复读</li><li>9.4.3 幻读</li></ul></li><li>9.5 事务隔离级别<ul><li>9.5.1 隔离级别说明</li><li>9.5.2 查看隔离级别</li><li>9.5.3 设置隔离级别</li></ul></li><li>9.6 补充-事务控制过程<ul><li>9.6.1 未控制事务<ul><li>9.6.1.1 正常情况</li><li>9.6.1.2 异常情况</li></ul></li><li>9.6.2 控制事务一<ul><li>9.6.2.1 查看 &#x2F; 设置事务提交方式</li><li>9.6.2.2 提交事务</li><li>9.6.2.3 回滚事务</li></ul></li><li>9.6.3 控制事务二<ul><li>9.6.3.1 开启事务</li><li>9.6.3.2 提交事务</li><li>9.6.3.3 回滚事务</li></ul></li></ul></li></ul></li><li>10 综合案例-员工与部门管理<ul><li>10.1 案例说明</li><li>10.2 创建数据库并切换</li><li>10.3 创建表</li><li>10.4 插入数据</li><li>10.5 修改数据</li><li>10.6 删除数据</li><li>10.7 基础查询<ul><li>10.7.1 查询所有员工</li><li>10.7.2 查询指定字段并设置别名</li><li>10.7.3 去重查询</li></ul></li><li>10.8 条件查询<ul><li>10.8.1 比较 &#x2F; 范围 &#x2F; 模糊 &#x2F; 空值判断</li><li>10.8.2 IN &#x2F; OR &#x2F; NOT</li></ul></li><li>10.9 聚合、分组、排序、分页<ul><li>10.9.1 聚合函数</li><li>10.9.2 分组查询</li><li>10.9.3 排序 + 分页</li></ul></li><li>10.10 常用函数<ul><li>10.10.1 字符串函数</li><li>10.10.2 日期函数</li><li>10.10.3 流程函数</li></ul></li><li>10.11 多表查询<ul><li>10.11.1 内连接</li><li>10.11.2 左外连接</li><li>10.11.3 自连接</li><li>10.11.4 联合查询</li></ul></li><li>10.12 子查询<ul><li>10.12.1 标量子查询</li><li>10.12.2 列子查询</li><li>10.12.3 行子查询</li><li>10.12.4 表子查询</li></ul></li><li>10.13 事务</li><li>10.14 执行顺序理解</li></ul></li><li>11 正则表达式<ul><li>11.1 概述</li><li>11.2 常用元字符</li><li>11.3 常用正则函数</li><li>11.4 示例操作<ul><li>11.4.1 基础匹配 (<code>REGEXP</code> &#x2F; <code>RLIKE</code>)</li><li>11.4.2 提取子串 (<code>REGEXP_SUBSTR</code>)</li><li>11.4.3 替换子串 (<code>REGEXP_REPLACE</code>)</li><li>11.4.4 查找位置 (<code>REGEXP_INSTR</code>)</li></ul></li><li>11.5 注意事项</li></ul></li></ul><h2 id="1-SQL基础"><a href="#1-SQL基础" class="headerlink" title="1 SQL基础"></a>1 SQL基础</h2><h3 id="1-1-SQL通用语法"><a href="#1-1-SQL通用语法" class="headerlink" title="1.1 SQL通用语法"></a>1.1 SQL通用语法</h3><ol><li>SQL 语句可以单行或多行书写，以分号 <code>;</code> 结尾。</li><li>SQL 语句可以使用空格 &#x2F; 缩进来增强语句的可读性。</li><li>MySQL 数据库的 SQL 语句不区分大小写，关键字建议使用大写。</li><li>注释：<ul><li>单行注释：<code>-- 注释内容</code> 或 <code># 注释内容</code></li><li>多行注释：<code>/* 注释内容 */</code></li></ul></li></ol><h3 id="1-2-SQL分类"><a href="#1-2-SQL分类" class="headerlink" title="1.2 SQL分类"></a>1.2 SQL分类</h3><table><thead><tr><th>分类</th><th>全称</th><th>说明</th></tr></thead><tbody><tr><td><code>DDL</code></td><td>Data Definition Language</td><td>数据定义语言，用来定义数据库对象（数据库、表、字段）</td></tr><tr><td><code>DML</code></td><td>Data Manipulation Language</td><td>数据操作语言，用来对数据库表中的数据进行增删改</td></tr><tr><td><code>DQL</code></td><td>Data Query Language</td><td>数据查询语言，用来查询数据库中表的记录</td></tr><tr><td><code>DCL</code></td><td>Data Control Language</td><td>数据控制语言，用来创建数据库用户、控制数据库的访问权限</td></tr></tbody></table><h2 id="2-DDL"><a href="#2-DDL" class="headerlink" title="2 DDL"></a>2 DDL</h2><h3 id="2-1-数据库操作"><a href="#2-1-数据库操作" class="headerlink" title="2.1 数据库操作"></a>2.1 数据库操作</h3><h4 id="2-1-1-查询"><a href="#2-1-1-查询" class="headerlink" title="2.1.1 查询"></a>2.1.1 查询</h4><ul><li><p><strong>查询所有数据库</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SHOW</span> DATABASES;<br></code></pre></td></tr></table></figure></li><li><p><strong>查询当前数据库</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> DATABASE();<br></code></pre></td></tr></table></figure></li></ul><h4 id="2-1-2-创建"><a href="#2-1-2-创建" class="headerlink" title="2.1.2 创建"></a>2.1.2 创建</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">CREATE</span> DATABASE [ IF <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> ] 数据库名 [ <span class="hljs-keyword">DEFAULT</span> CHARSET 字符集 ] [ <span class="hljs-keyword">COLLATE</span> 排序规则 ];<br></code></pre></td></tr></table></figure><h4 id="2-1-3-删除"><a href="#2-1-3-删除" class="headerlink" title="2.1.3 删除"></a>2.1.3 删除</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">DROP</span> DATABASE [ IF <span class="hljs-keyword">EXISTS</span> ] 数据库名;<br></code></pre></td></tr></table></figure><h4 id="2-1-4-使用"><a href="#2-1-4-使用" class="headerlink" title="2.1.4 使用"></a>2.1.4 使用</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql">USE 数据库名;<br></code></pre></td></tr></table></figure><h3 id="2-2-表操作"><a href="#2-2-表操作" class="headerlink" title="2.2 表操作"></a>2.2 表操作</h3><h4 id="2-2-1-查询"><a href="#2-2-1-查询" class="headerlink" title="2.2.1 查询"></a>2.2.1 查询</h4><ul><li><p><strong>查询当前数据库所有表</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SHOW</span> TABLES;<br></code></pre></td></tr></table></figure></li><li><p><strong>查询表结构</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">DESC</span> 表名;<br></code></pre></td></tr></table></figure></li><li><p><strong>查询指定表的建表语句</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SHOW</span> <span class="hljs-keyword">CREATE TABLE</span> 表名;<br></code></pre></td></tr></table></figure></li></ul><h4 id="2-2-2-创建"><a href="#2-2-2-创建" class="headerlink" title="2.2.2 创建"></a>2.2.2 创建</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">CREATE TABLE</span> 表名(<br>    字段<span class="hljs-number">1</span> 字段<span class="hljs-number">1</span>类型 [COMMENT 字段<span class="hljs-number">1</span>注释],<br>    字段<span class="hljs-number">2</span> 字段<span class="hljs-number">2</span>类型 [COMMENT 字段<span class="hljs-number">2</span>注释],<br>    字段<span class="hljs-number">3</span> 字段<span class="hljs-number">3</span>类型 [COMMENT 字段<span class="hljs-number">3</span>注释],<br>    ......<br>    字段n 字段n类型 [COMMENT 字段n注释]<br>)[COMMENT 表注释];<br></code></pre></td></tr></table></figure><p><strong>注意</strong>：<code>[...])</code> 为可选参数，最后一个字段后面没有逗号</p><h4 id="2-2-3-修改"><a href="#2-2-3-修改" class="headerlink" title="2.2.3 修改"></a>2.2.3 修改</h4><ul><li><p><strong>添加字段</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 表名 <span class="hljs-keyword">ADD</span> 字段名 类型 [COMMENT 注释];<br></code></pre></td></tr></table></figure></li><li><p><strong>修改字段类型</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 表名 MODIFY 字段名 新类型 [COMMENT 注释];<br></code></pre></td></tr></table></figure></li><li><p><strong>修改字段名和类型</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 表名 CHANGE 旧字段名 新字段名 新类型 [COMMENT 注释];<br></code></pre></td></tr></table></figure></li><li><p><strong>删除字段</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 表名 <span class="hljs-keyword">DROP</span> 字段名;<br></code></pre></td></tr></table></figure></li><li><p><strong>修改表名</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 旧表名 RENAME <span class="hljs-keyword">TO</span> 新表名;<br></code></pre></td></tr></table></figure></li></ul><h4 id="2-2-4-删除"><a href="#2-2-4-删除" class="headerlink" title="2.2.4 删除"></a>2.2.4 删除</h4><ul><li><p><strong>删除表</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TABLE</span> [ IF <span class="hljs-keyword">EXISTS</span> ] 表名;<br></code></pre></td></tr></table></figure></li><li><p><strong>删除指定表，并重新创建该表</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">TRUNCATE</span> <span class="hljs-keyword">TABLE</span> 表名;<br></code></pre></td></tr></table></figure></li></ul><h3 id="2-3-SQL常用字段和字段类型"><a href="#2-3-SQL常用字段和字段类型" class="headerlink" title="2.3 SQL常用字段和字段类型"></a>2.3 SQL常用字段和字段类型</h3><p>在 SQL（特别是 MySQL）中，<strong>字段类型</strong>（也叫数据类型）决定了该字段能存储什么种类的数据、占用多少存储空间、以及支持哪些操作。选择合适的数据类型对数据库的<strong>性能、存储效率和数据准确性</strong>至关重要。</p><h4 id="2-3-1-一、数值类型（Numeric-Types）"><a href="#2-3-1-一、数值类型（Numeric-Types）" class="headerlink" title="2.3.1 一、数值类型（Numeric Types）"></a>2.3.1 一、数值类型（Numeric Types）</h4><h5 id="2-3-1-1-整数类型"><a href="#2-3-1-1-整数类型" class="headerlink" title="2.3.1.1 整数类型"></a>2.3.1.1 整数类型</h5><table><thead><tr><th>类型</th><th>范围</th><th>占用空间</th><th>适用场景</th></tr></thead><tbody><tr><td><code>TINYINT</code></td><td>-128 ~ 127（有符号）0 ~ 255（无符号）</td><td>1 字节</td><td>状态标志（如 <code>status: 0/1</code>）、性别</td></tr><tr><td><code>SMALLINT</code></td><td>-32,768 ~ 32,767</td><td>2 字节</td><td>小计数器、年份</td></tr><tr><td><code>MEDIUMINT</code></td><td>-8,388,608 ~ 8,388,607</td><td>3 字节</td><td>中等ID或计数</td></tr><tr><td><code>INT</code> &#x2F; <code>INTEGER</code></td><td>-21亿 ~ 21亿</td><td>4 字节</td><td>用户ID、订单ID（中小系统）</td></tr><tr><td><code>BIGINT</code></td><td>±9.2×10¹⁸</td><td>8 字节</td><td>高并发ID、分布式系统主键</td></tr></tbody></table><p><strong>建议</strong>：</p><ul><li>主键优先用 <code>BIGINT</code>（避免 ID 耗尽）</li><li>状态字段用 <code>TINYINT UNSIGNED</code>（0~255）</li></ul><h5 id="2-3-1-2-小数类型"><a href="#2-3-1-2-小数类型" class="headerlink" title="2.3.1.2 小数类型"></a>2.3.1.2 小数类型</h5><table><thead><tr><th>类型</th><th>说明</th><th>适用场景</th></tr></thead><tbody><tr><td><code>DECIMAL(M,D)</code></td><td>精确小数，M&#x3D;总位数，D&#x3D;小数位例如 <code>DECIMAL(10,2)</code> 表示最多 8 位整数 + 2 位小数</td><td><strong>金额、价格、财务数据</strong>（必须用它！）</td></tr><tr><td><code>FLOAT</code></td><td>单精度浮点（约7位有效数字）</td><td>科学计算、不需精确的场景</td></tr><tr><td><code>DOUBLE</code></td><td>双精度浮点（约15位有效数字）</td><td>地理坐标、物理量</td></tr></tbody></table><p><strong>重要</strong>：<strong>不要用 <code>FLOAT</code>&#x2F;<code>DOUBLE</code> 存金额</strong>！会有精度丢失（如 0.1 + 0.2 ≠ 0.3）。<br><strong>正确做法</strong>：<code>price DECIMAL(10,2)</code></p><h4 id="2-3-2-二、字符串类型（String-Types）"><a href="#2-3-2-二、字符串类型（String-Types）" class="headerlink" title="2.3.2 二、字符串类型（String Types）"></a>2.3.2 二、字符串类型（String Types）</h4><h5 id="2-3-2-1-定长-vs-变长"><a href="#2-3-2-1-定长-vs-变长" class="headerlink" title="2.3.2.1 定长 vs 变长"></a>2.3.2.1 定长 vs 变长</h5><table><thead><tr><th>类型</th><th>最大长度</th><th>特点</th><th>适用场景</th></tr></thead><tbody><tr><td><code>CHAR(n)</code></td><td>0~255 字符</td><td>固定长度，不足补空格</td><td>国家代码（’CN’）、MD5（32位）</td></tr><tr><td><code>VARCHAR(n)</code></td><td>0~65,535 字节</td><td>可变长度，节省空间</td><td>姓名、标题、邮箱、地址</td></tr></tbody></table><p><strong>建议</strong>：</p><ul><li>大多数文本用 <code>VARCHAR</code></li><li>长度明确且固定用 <code>CHAR</code></li></ul><h5 id="2-3-2-2-大文本类型"><a href="#2-3-2-2-大文本类型" class="headerlink" title="2.3.2.2 大文本类型"></a>2.3.2.2 大文本类型</h5><table><thead><tr><th>类型</th><th>最大容量</th><th>适用场景</th></tr></thead><tbody><tr><td><code>TINYTEXT</code></td><td>255 字节</td><td>极短描述</td></tr><tr><td><code>TEXT</code></td><td>65,535 字节（≈64KB）</td><td>文章内容、评论</td></tr><tr><td><code>MEDIUMTEXT</code></td><td>16MB</td><td>长文章、日志</td></tr><tr><td><code>LONGTEXT</code></td><td>4GB</td><td>小说、PDF文本提取</td></tr></tbody></table><p><strong>提示</strong>：<code>TEXT</code> 类型不能有默认值，且可能影响查询性能（尽量不 SELECT *）</p><h4 id="2-3-3-三、日期和时间类型（Date-Time）"><a href="#2-3-3-三、日期和时间类型（Date-Time）" class="headerlink" title="2.3.3 三、日期和时间类型（Date &amp; Time）"></a>2.3.3 三、日期和时间类型（Date &amp; Time）</h4><table><thead><tr><th>类型</th><th>格式</th><th>范围</th><th>适用场景</th></tr></thead><tbody><tr><td><code>DATE</code></td><td><code>YYYY-MM-DD</code></td><td>1000-01-01 ~ 9999-12-31</td><td>生日、日期选择</td></tr><tr><td><code>TIME</code></td><td><code>HH:MM:SS</code></td><td>-838:59:59 ~ 838:59:59</td><td>持续时间、时间段</td></tr><tr><td><code>DATETIME</code></td><td><code>YYYY-MM-DD HH:MM:SS</code></td><td>1000-01-01 00:00:00 ~ 9999-12-31 23:59:59</td><td><strong>创建时间、更新时间</strong>（推荐）</td></tr><tr><td><code>TIMESTAMP</code></td><td>同上</td><td>1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC</td><td>需要自动时区转换的场景（但有 2038 年问题）</td></tr></tbody></table><p><strong>建议</strong>：</p><ul><li>优先使用 <code>DATETIME</code>（范围大、无时区副作用）</li><li>自动设置当前时间：<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql">created_at DATETIME <span class="hljs-keyword">DEFAULT</span> <span class="hljs-built_in">CURRENT_TIMESTAMP</span><br></code></pre></td></tr></table></figure></li></ul><h4 id="2-3-4-四、枚举与集合类型（慎用）"><a href="#2-3-4-四、枚举与集合类型（慎用）" class="headerlink" title="2.3.4 四、枚举与集合类型（慎用）"></a>2.3.4 四、枚举与集合类型（慎用）</h4><table><thead><tr><th>类型</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><code>ENUM(&#39;值1&#39;,&#39;值2&#39;,...)</code></td><td>枚举，只能选预定义值</td><td><code>gender ENUM(&#39;男&#39;,&#39;女&#39;,&#39;未知&#39;)</code></td></tr><tr><td><code>SET(&#39;值1&#39;,&#39;值2&#39;,...)</code></td><td>集合，可多选</td><td><code>hobby SET(&#39;读书&#39;,&#39;电影&#39;,&#39;旅行&#39;)</code></td></tr></tbody></table><p><strong>缺点</strong>：</p><ul><li>修改选项需改表结构</li><li>不利于扩展（微服务、多语言场景）<br><strong>替代方案</strong>：用 <code>TINYINT</code> + 代码注释，或单独建字典表</li></ul><h4 id="2-3-5-五、二进制与特殊类型"><a href="#2-3-5-五、二进制与特殊类型" class="headerlink" title="2.3.5 五、二进制与特殊类型"></a>2.3.5 五、二进制与特殊类型</h4><table><thead><tr><th>类型</th><th>用途</th></tr></thead><tbody><tr><td><code>BLOB</code> &#x2F; <code>LONGBLOB</code></td><td>存储图片、文件等二进制数据（<strong>一般不推荐</strong>，建议存文件路径）</td></tr><tr><td><code>JSON</code>（MySQL 5.7+）</td><td>存储 JSON 文档，支持 JSON 查询</td></tr><tr><td><code>BOOLEAN</code></td><td>实际是 <code>TINYINT(1)</code> 的别名</td></tr></tbody></table><p><strong>JSON 示例</strong>：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql">config JSON COMMENT <span class="hljs-string">&#x27;用户个性化设置&#x27;</span><br></code></pre></td></tr></table></figure><p>查询：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> users <span class="hljs-keyword">WHERE</span> config<span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span><span class="hljs-string">&#x27;$.theme&#x27;</span> <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;dark&#x27;</span>;<br></code></pre></td></tr></table></figure><h4 id="2-3-6-六、选择字段类型的黄金原则"><a href="#2-3-6-六、选择字段类型的黄金原则" class="headerlink" title="2.3.6 六、选择字段类型的黄金原则"></a>2.3.6 六、选择字段类型的黄金原则</h4><ol><li><strong>够用就好</strong>：能用 <code>TINYINT</code> 就不用 <code>INT</code>，节省空间 &#x3D; 提升性能。</li><li><strong>避免 NULL</strong>：除非必要，字段设为 <code>NOT NULL</code> + 默认值，减少判断复杂度。</li><li><strong>金额用 <code>DECIMAL</code></strong>：永远不要用浮点数存钱！</li><li><strong>时间用 <code>DATETIME</code></strong>：避开 <code>TIMESTAMP</code> 的 2038 问题。</li><li><strong>大文本分离</strong>：如果某字段很少查询，可拆到单独表（垂直分表）。</li><li><strong>字符集统一</strong>：建议 <code>utf8mb4</code>（支持 emoji）。</li></ol><h4 id="2-3-7-七、常见字段类型搭配示例"><a href="#2-3-7-七、常见字段类型搭配示例" class="headerlink" title="2.3.7 七、常见字段类型搭配示例"></a>2.3.7 七、常见字段类型搭配示例</h4><table><thead><tr><th>业务含义</th><th>推荐类型</th></tr></thead><tbody><tr><td>用户ID</td><td><code>BIGINT</code></td></tr><tr><td>用户名</td><td><code>VARCHAR(50)</code></td></tr><tr><td>邮箱</td><td><code>VARCHAR(150)</code></td></tr><tr><td>手机号</td><td><code>VARCHAR(20)</code>（含国际区号）</td></tr><tr><td>密码哈希</td><td><code>VARCHAR(255)</code>（兼容 bcrypt）</td></tr><tr><td>性别</td><td><code>TINYINT</code>（0&#x2F;1&#x2F;2）或 <code>CHAR(1)</code>（’M’&#x2F;‘F’）</td></tr><tr><td>价格</td><td><code>DECIMAL(10,2)</code></td></tr><tr><td>商品描述</td><td><code>TEXT</code></td></tr><tr><td>注册时间</td><td><code>DATETIME</code></td></tr><tr><td>是否启用</td><td><code>TINYINT</code>（0&#x2F;1）</td></tr><tr><td>头像URL</td><td><code>VARCHAR(255)</code></td></tr></tbody></table><h2 id="3-DML"><a href="#3-DML" class="headerlink" title="3 DML"></a>3 DML</h2><h3 id="3-1-添加数据"><a href="#3-1-添加数据" class="headerlink" title="3.1 添加数据"></a>3.1 添加数据</h3><ul><li><p><strong>语法</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT INTO</span> 表名(字段名<span class="hljs-number">1</span>, 字段名<span class="hljs-number">2</span>, ...)<br><span class="hljs-keyword">VALUES</span> (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...);<br></code></pre></td></tr></table></figure></li><li><p><strong>给全部字段添加数据</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT INTO</span> 表名<br><span class="hljs-keyword">VALUES</span> (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...);<br></code></pre></td></tr></table></figure></li><li><p><strong>批量添加数据</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT INTO</span> 表名(字段名<span class="hljs-number">1</span>, 字段名<span class="hljs-number">2</span>, ...)<br><span class="hljs-keyword">VALUES</span> (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...), (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...), (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...);<br></code></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT INTO</span> 表名<br><span class="hljs-keyword">VALUES</span> (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...), (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...), (值<span class="hljs-number">1</span>, 值<span class="hljs-number">2</span>, ...);<br></code></pre></td></tr></table></figure></li></ul><p><strong>注意：</strong></p><ul><li>插入数据时，指定的字段顺序需要与值的顺序一一对应。</li><li>字符串和日期类型数据应该包含在引号中。</li><li>插入的数据大小，应该在字段的规定范围内。</li></ul><h3 id="3-2-修改数据"><a href="#3-2-修改数据" class="headerlink" title="3.2 修改数据"></a>3.2 修改数据</h3><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">UPDATE</span> 表名<br><span class="hljs-keyword">SET</span> 字段名<span class="hljs-number">1</span> <span class="hljs-operator">=</span> 值<span class="hljs-number">1</span>, 字段名<span class="hljs-number">2</span> <span class="hljs-operator">=</span> 值<span class="hljs-number">2</span>, ...<br>[<span class="hljs-keyword">WHERE</span> 条件];<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>修改语句的条件可以有，也可以没有，如果没有条件，则会修改整张表的所有数据。</li></ul><h3 id="3-3-删除数据"><a href="#3-3-删除数据" class="headerlink" title="3.3 删除数据"></a>3.3 删除数据</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> 表名 [<span class="hljs-keyword">WHERE</span> 条件];<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>DELETE 语句的条件可以有，也可以没有，如果没有条件，则会删除整张表的所有数据。</li><li>DELETE 语句不能删除某一个字段的值（可以使用 <code>UPDATE</code>）。</li></ul><h2 id="4-DQL"><a href="#4-DQL" class="headerlink" title="4 DQL"></a>4 DQL</h2><h3 id="4-1-语法"><a href="#4-1-语法" class="headerlink" title="4.1 语法"></a>4.1 语法</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    字段列表，聚合函数（字段列表）<br><span class="hljs-keyword">FROM</span><br>    表名列表<br><span class="hljs-keyword">WHERE</span><br>    条件列表 #分组之前进行过滤<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>    分组字段列表<br><span class="hljs-keyword">HAVING</span><br>    分组后条件列表<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span><br>    排序字段列表<br>LIMIT<br>    分页参数;<br></code></pre></td></tr></table></figure><h4 id="4-1-1-常见查询分类"><a href="#4-1-1-常见查询分类" class="headerlink" title="4.1.1 常见查询分类"></a>4.1.1 常见查询分类</h4><ul><li>基本查询</li><li>条件查询（<code>WHERE</code>）</li><li>聚合函数（<code>COUNT</code>、<code>MAX</code>、<code>MIN</code>、<code>AVG</code>、<code>SUM</code>）</li><li>分组查询（<code>GROUP BY</code>）</li><li>排序查询（<code>ORDER BY</code>）</li><li>分页查询（<code>LIMIT</code>）</li></ul><h3 id="4-2-基本查询"><a href="#4-2-基本查询" class="headerlink" title="4.2 基本查询"></a>4.2 基本查询</h3><h4 id="4-2-1-查询多个字段"><a href="#4-2-1-查询多个字段" class="headerlink" title="4.2.1 查询多个字段"></a>4.2.1 查询多个字段</h4>  <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段<span class="hljs-number">1</span>, 字段<span class="hljs-number">2</span>, 字段<span class="hljs-number">3</span>, ...<br><span class="hljs-keyword">FROM</span> 表名;<br></code></pre></td></tr></table></figure>  <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> 表名;<br></code></pre></td></tr></table></figure><h4 id="4-2-2-设置别名"><a href="#4-2-2-设置别名" class="headerlink" title="4.2.2 设置别名"></a>4.2.2 设置别名</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段<span class="hljs-number">1</span> [<span class="hljs-keyword">AS</span> 别名<span class="hljs-number">1</span>], 字段<span class="hljs-number">2</span> [<span class="hljs-keyword">AS</span> 别名<span class="hljs-number">2</span>], ...<br><span class="hljs-keyword">FROM</span> 表名;<br></code></pre></td></tr></table></figure><h4 id="4-2-3-去除重复记录"><a href="#4-2-3-去除重复记录" class="headerlink" title="4.2.3 去除重复记录"></a>4.2.3 去除重复记录</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表名;<br></code></pre></td></tr></table></figure><h3 id="4-3-条件查询"><a href="#4-3-条件查询" class="headerlink" title="4.3 条件查询"></a>4.3 条件查询</h3><h4 id="4-3-1-语法"><a href="#4-3-1-语法" class="headerlink" title="4.3.1 语法"></a>4.3.1 语法</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表名<br><span class="hljs-keyword">WHERE</span> 条件列表;<br></code></pre></td></tr></table></figure><h5 id="4-3-1-1-比较运算符"><a href="#4-3-1-1-比较运算符" class="headerlink" title="4.3.1.1 比较运算符"></a>4.3.1.1 比较运算符</h5><table><thead><tr><th>运算符</th><th>功能</th></tr></thead><tbody><tr><td><code>&gt;</code></td><td>大于</td></tr><tr><td><code>&gt;=</code></td><td>大于等于</td></tr><tr><td><code>&lt;</code></td><td>小于</td></tr><tr><td><code>&lt;=</code></td><td>小于等于</td></tr><tr><td><code>=</code></td><td>等于</td></tr><tr><td><code>&lt;&gt;</code> &#x2F; <code>!=</code></td><td>不等于</td></tr><tr><td><code>BETWEEN ... AND ...</code></td><td>在某个范围之内（包含最小值、最大值）</td></tr><tr><td><code>IN(...)</code></td><td>在 <code>IN</code> 之后的列表中的值，多选一</td></tr><tr><td><code>LIKE</code></td><td>模糊匹配（<code>_</code> 匹配单个字符，<code>%</code> 匹配任意个字符）</td></tr><tr><td><code>IS NULL</code></td><td>是 <code>NULL</code></td></tr></tbody></table><h5 id="4-3-1-2-逻辑运算符"><a href="#4-3-1-2-逻辑运算符" class="headerlink" title="4.3.1.2 逻辑运算符"></a>4.3.1.2 逻辑运算符</h5><table><thead><tr><th>运算符</th><th>功能</th></tr></thead><tbody><tr><td><code>AND</code> &#x2F; <code>&amp;&amp;</code></td><td>并且（多个条件同时成立）</td></tr><tr><td><code>OR</code> &#x2F; &#96;</td><td></td></tr><tr><td><code>NOT</code> &#x2F; <code>!</code></td><td>非，不是</td></tr></tbody></table><h3 id="4-4-聚合函数"><a href="#4-4-聚合函数" class="headerlink" title="4.4 聚合函数"></a>4.4 聚合函数</h3><h4 id="4-4-1-介绍"><a href="#4-4-1-介绍" class="headerlink" title="4.4.1 介绍"></a>4.4.1 介绍</h4><p>将一列数据作为一个整体，进行纵向计算。</p><h4 id="4-4-2-常见聚合函数"><a href="#4-4-2-常见聚合函数" class="headerlink" title="4.4.2 常见聚合函数"></a>4.4.2 常见聚合函数</h4><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td><code>COUNT</code></td><td>统计数量</td></tr><tr><td><code>MAX</code></td><td>最大值</td></tr><tr><td><code>MIN</code></td><td>最小值</td></tr><tr><td><code>AVG</code></td><td>平均值</td></tr><tr><td><code>SUM</code></td><td>求和</td></tr></tbody></table><ul><li>注：COUNT函数还有一种用法 <code>COUNT(DISTINCT 字段1, 字段2)</code> 意为(字段1, 字段2) 这个组合有多少种不同的值</li><li>COUNT用法详解：<br><code>COUNT(*)</code> &#x3D; 数行数<br><code>COUNT(字段)</code> &#x3D; 数该字段有值的行数<br><code>COUNT(DISTINCT 字段)</code> &#x3D; 数该字段有几种不同的值</li></ul><h4 id="4-4-3-语法"><a href="#4-4-3-语法" class="headerlink" title="4.4.3 语法"></a>4.4.3 语法</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 聚合函数(字段列表)<br><span class="hljs-keyword">FROM</span> 表名;<br></code></pre></td></tr></table></figure><h3 id="4-5-分组查询"><a href="#4-5-分组查询" class="headerlink" title="4.5 分组查询"></a>4.5 分组查询</h3><h4 id="4-5-1-语法"><a href="#4-5-1-语法" class="headerlink" title="4.5.1 语法"></a>4.5.1 语法</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表名<br>[<span class="hljs-keyword">WHERE</span> 条件]<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> 分组字段名<br>[<span class="hljs-keyword">HAVING</span> 分组后过滤条件];<br></code></pre></td></tr></table></figure><h4 id="4-5-2-WHERE-与-HAVING-区别"><a href="#4-5-2-WHERE-与-HAVING-区别" class="headerlink" title="4.5.2 WHERE 与 HAVING 区别"></a>4.5.2 <code>WHERE</code> 与 <code>HAVING</code> 区别</h4><ul><li>执行时机不同：<code>WHERE</code> 是分组之前进行过滤，不满足 <code>WHERE</code> 条件，不参与分组；<code>HAVING</code> 是分组之后对结果进行过滤。</li><li>判断条件不同：<code>WHERE</code> 不能对聚合函数进行判断，而 <code>HAVING</code> 可以。</li></ul><p><strong>注意：</strong></p><ul><li>执行顺序：<code>WHERE</code> &gt; 聚合函数 &gt; <code>HAVING</code>。</li><li>分组之后，查询的字段一般为聚合函数和分组字段，查询其他字段无实际意义。</li></ul><h3 id="4-6-排序查询"><a href="#4-6-排序查询" class="headerlink" title="4.6 排序查询"></a>4.6 排序查询</h3><h4 id="4-6-1-语法"><a href="#4-6-1-语法" class="headerlink" title="4.6.1 语法"></a>4.6.1 语法</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表名<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> 字段<span class="hljs-number">1</span> 排序方式<span class="hljs-number">1</span>, 字段<span class="hljs-number">2</span> 排序方式<span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><h4 id="4-6-2-排序方式"><a href="#4-6-2-排序方式" class="headerlink" title="4.6.2 排序方式"></a>4.6.2 排序方式</h4><ul><li><code>ASC</code>：升序（默认值）</li><li><code>DESC</code>：降序</li></ul><p><strong>注意：</strong></p><ul><li>如果是多字段排序，当第一个字段值相同时，才会根据第二个字段进行排序。</li></ul><h3 id="4-7-分页查询"><a href="#4-7-分页查询" class="headerlink" title="4.7 分页查询"></a>4.7 分页查询</h3><h4 id="4-7-1-语法"><a href="#4-7-1-语法" class="headerlink" title="4.7.1 语法"></a>4.7.1 语法</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表名<br>LIMIT 起始索引, 查询记录数;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>起始索引从 0 开始，起始索引 &#x3D; （查询页码 - 1） * 每页显示记录数。</li><li>分页查询是数据库的方言，不同的数据库有不同的实现，MySQL 中是 <code>LIMIT</code>。</li><li>如果查询的是第一页数据，起始索引可以省略，直接简写为 <code>LIMIT 10</code>。</li></ul><h3 id="4-8-执行顺序"><a href="#4-8-执行顺序" class="headerlink" title="4.8 执行顺序"></a>4.8 执行顺序</h3><h4 id="4-8-1-编写顺序"><a href="#4-8-1-编写顺序" class="headerlink" title="4.8.1 编写顺序"></a>4.8.1 编写顺序</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表名列表<br><span class="hljs-keyword">JOIN</span> 其他表名<br><span class="hljs-keyword">ON</span> 连接条件 (若还要连其他的表就继续<span class="hljs-keyword">JOIN</span>……<span class="hljs-keyword">ON</span>……)<br><span class="hljs-keyword">WHERE</span> 条件列表<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> 分组字段列表<br><span class="hljs-keyword">HAVING</span> 分组后条件列表<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> 排序字段列表<br>LIMIT 分页参数;<br></code></pre></td></tr></table></figure><h4 id="4-8-2-执行顺序"><a href="#4-8-2-执行顺序" class="headerlink" title="4.8.2 执行顺序"></a>4.8.2 执行顺序</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">FROM</span><br><span class="hljs-keyword">WHERE</span><br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br><span class="hljs-keyword">HAVING</span><br><span class="hljs-keyword">SELECT</span><br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span><br>LIMIT<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>实际执行时并不是从 <code>SELECT</code> 开始，而是先 <code>FROM</code> 再逐步过滤、分组、筛选、排序和分页。</li></ul><h2 id="5-DCL"><a href="#5-DCL" class="headerlink" title="5 DCL"></a>5 DCL</h2><h3 id="5-1-管理用户"><a href="#5-1-管理用户" class="headerlink" title="5.1 管理用户"></a>5.1 管理用户</h3><h4 id="5-1-1-查询用户"><a href="#5-1-1-查询用户" class="headerlink" title="5.1.1 查询用户"></a>5.1.1 查询用户</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> mysql.user;<br></code></pre></td></tr></table></figure><p><strong>说明：</strong></p><ul><li><code>Host</code> 代表当前用户允许访问的主机。</li><li><code>User</code> 代表访问该数据库的用户名。</li><li>在 MySQL 中，需要通过 <code>Host</code> 和 <code>User</code> 来唯一标识一个用户。</li></ul><h4 id="5-1-2-创建用户"><a href="#5-1-2-创建用户" class="headerlink" title="5.1.2 创建用户"></a>5.1.2 创建用户</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">USER</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;主机名&#x27;</span> IDENTIFIED <span class="hljs-keyword">BY</span> <span class="hljs-string">&#x27;密码&#x27;</span>;<br></code></pre></td></tr></table></figure><h4 id="5-1-3-修改用户密码"><a href="#5-1-3-修改用户密码" class="headerlink" title="5.1.3 修改用户密码"></a>5.1.3 修改用户密码</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">USER</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;主机名&#x27;</span> IDENTIFIED <span class="hljs-keyword">WITH</span> mysql_native_password <span class="hljs-keyword">BY</span> <span class="hljs-string">&#x27;新密码&#x27;</span>;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>主机名可以使用 <code>%</code> 通配。</li><li>这类 SQL 开发人员操作得比较少，主要是 DBA（数据库管理员）使用。</li></ul><h4 id="5-1-4-删除用户"><a href="#5-1-4-删除用户" class="headerlink" title="5.1.4 删除用户"></a>5.1.4 删除用户</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">USER</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;主机名&#x27;</span>;<br></code></pre></td></tr></table></figure><h3 id="5-2-权限控制"><a href="#5-2-权限控制" class="headerlink" title="5.2 权限控制"></a>5.2 权限控制</h3><h4 id="5-2-1-常用权限"><a href="#5-2-1-常用权限" class="headerlink" title="5.2.1 常用权限"></a>5.2.1 常用权限</h4><table><thead><tr><th>权限</th><th>说明</th></tr></thead><tbody><tr><td><code>ALL</code> &#x2F; <code>ALL PRIVILEGES</code></td><td>所有权限</td></tr><tr><td><code>SELECT</code></td><td>查询数据</td></tr><tr><td><code>INSERT</code></td><td>插入数据</td></tr><tr><td><code>UPDATE</code></td><td>修改数据</td></tr><tr><td><code>DELETE</code></td><td>删除数据</td></tr><tr><td><code>ALTER</code></td><td>修改表</td></tr><tr><td><code>DROP</code></td><td>删除数据库 &#x2F; 表 &#x2F; 视图</td></tr><tr><td><code>CREATE</code></td><td>创建数据库 &#x2F; 表</td></tr></tbody></table><h4 id="5-2-2-查询权限"><a href="#5-2-2-查询权限" class="headerlink" title="5.2.2 查询权限"></a>5.2.2 查询权限</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SHOW</span> GRANTS <span class="hljs-keyword">FOR</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;主机名&#x27;</span>;<br></code></pre></td></tr></table></figure><h4 id="5-2-3-授予权限"><a href="#5-2-3-授予权限" class="headerlink" title="5.2.3 授予权限"></a>5.2.3 授予权限</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">GRANT</span> 权限列表 <span class="hljs-keyword">ON</span> 数据库名.表名 <span class="hljs-keyword">TO</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;主机名&#x27;</span>;<br></code></pre></td></tr></table></figure><h4 id="5-2-4-撤销权限"><a href="#5-2-4-撤销权限" class="headerlink" title="5.2.4 撤销权限"></a>5.2.4 撤销权限</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">REVOKE</span> 权限列表 <span class="hljs-keyword">ON</span> 数据库名.表名 <span class="hljs-keyword">FROM</span> <span class="hljs-string">&#x27;用户名&#x27;</span>@<span class="hljs-string">&#x27;主机名&#x27;</span>;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>多个权限之间，使用逗号分隔。</li><li>授权时，数据库名和表名可以使用 <code>*</code> 进行通配，代表所有。</li></ul><h2 id="6-常用函数"><a href="#6-常用函数" class="headerlink" title="6 常用函数"></a>6 常用函数</h2><h3 id="6-1-常用函数概述"><a href="#6-1-常用函数概述" class="headerlink" title="6.1 常用函数概述"></a>6.1 常用函数概述</h3><p>MySQL 中常用函数主要分为五类：</p><ul><li>字符串函数</li><li>数值函数</li><li>日期函数</li><li>流程函数</li><li>窗口函数</li></ul><h3 id="6-2-字符串函数"><a href="#6-2-字符串函数" class="headerlink" title="6.2 字符串函数"></a>6.2 字符串函数</h3><h4 id="6-2-1-函数说明"><a href="#6-2-1-函数说明" class="headerlink" title="6.2.1 函数说明"></a>6.2.1 函数说明</h4><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td><code>CONCAT(S1,S2,...Sn)</code></td><td>字符串拼接，将多个字符串拼接成一个字符串</td></tr><tr><td><code>LOWER(str)</code></td><td>将字符串全部转为小写</td></tr><tr><td><code>UPPER(str)</code></td><td>将字符串全部转为大写</td></tr><tr><td><code>LPAD(str,n,pad)</code></td><td>左填充，用 <code>pad</code> 对左边进行填充，达到长度 <code>n</code></td></tr><tr><td><code>RPAD(str,n,pad)</code></td><td>右填充，用 <code>pad</code> 对右边进行填充，达到长度 <code>n</code></td></tr><tr><td><code>TRIM(str)</code></td><td>去掉字符串头部和尾部的空格</td></tr><tr><td><code>SUBSTRING(str,start,len)</code></td><td>从指定位置截取指定长度的字符串</td></tr><tr><td><code>SUBSTRING_INDEX(str, delim, count)</code></td><td></td></tr></tbody></table><h4 id="6-2-2-示例"><a href="#6-2-2-示例" class="headerlink" title="6.2.2 示例"></a>6.2.2 示例</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> CONCAT(<span class="hljs-string">&#x27;Hello&#x27;</span>, <span class="hljs-string">&#x27; MySQL&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">LOWER</span>(<span class="hljs-string">&#x27;Hello&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">UPPER</span>(<span class="hljs-string">&#x27;Hello&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> LPAD(<span class="hljs-string">&#x27;01&#x27;</span>, <span class="hljs-number">5</span>, <span class="hljs-string">&#x27;-&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> RPAD(<span class="hljs-string">&#x27;01&#x27;</span>, <span class="hljs-number">5</span>, <span class="hljs-string">&#x27;-&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">TRIM</span>(<span class="hljs-string">&#x27; Hello MySQL &#x27;</span>);<br><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">SUBSTRING</span>(<span class="hljs-string">&#x27;Hello MySQL&#x27;</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>);<br></code></pre></td></tr></table></figure><h4 id="6-2-3-案例"><a href="#6-2-3-案例" class="headerlink" title="6.2.3 案例"></a>6.2.3 案例</h4><p>将员工工号统一补齐为 5 位，不足部分左侧补 <code>0</code>：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">UPDATE</span> emp<br><span class="hljs-keyword">SET</span> workno <span class="hljs-operator">=</span> LPAD(workno, <span class="hljs-number">5</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br></code></pre></td></tr></table></figure><h3 id="6-3-数值函数"><a href="#6-3-数值函数" class="headerlink" title="6.3 数值函数"></a>6.3 数值函数</h3><h4 id="6-3-1-函数说明"><a href="#6-3-1-函数说明" class="headerlink" title="6.3.1 函数说明"></a>6.3.1 函数说明</h4><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td><code>CEIL(x)</code></td><td>向上取整</td></tr><tr><td><code>FLOOR(x)</code></td><td>向下取整</td></tr><tr><td><code>MOD(x,y)</code></td><td>返回 <code>x / y</code> 的模</td></tr><tr><td><code>RAND()</code></td><td>返回 <code>0~1</code> 内的随机数</td></tr><tr><td><code>ROUND(x,y)</code></td><td>对 <code>x</code> 四舍五入，保留 <code>y</code> 位小数</td></tr><tr><td><code>ABS()</code></td><td>取绝对值</td></tr></tbody></table><h4 id="6-3-2-示例"><a href="#6-3-2-示例" class="headerlink" title="6.3.2 示例"></a>6.3.2 示例</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">CEIL</span>(<span class="hljs-number">1.1</span>);<br><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">FLOOR</span>(<span class="hljs-number">1.9</span>);<br><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">MOD</span>(<span class="hljs-number">7</span>, <span class="hljs-number">4</span>);<br><span class="hljs-keyword">SELECT</span> RAND();<br><span class="hljs-keyword">SELECT</span> ROUND(<span class="hljs-number">2.344</span>, <span class="hljs-number">2</span>);<br></code></pre></td></tr></table></figure><h4 id="6-3-3-案例"><a href="#6-3-3-案例" class="headerlink" title="6.3.3 案例"></a>6.3.3 案例</h4><p>生成 6 位随机验证码：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> LPAD(ROUND(RAND() <span class="hljs-operator">*</span> <span class="hljs-number">1000000</span>, <span class="hljs-number">0</span>), <span class="hljs-number">6</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br></code></pre></td></tr></table></figure><h3 id="6-4-日期函数"><a href="#6-4-日期函数" class="headerlink" title="6.4 日期函数"></a>6.4 日期函数</h3><h4 id="6-4-1-函数说明"><a href="#6-4-1-函数说明" class="headerlink" title="6.4.1 函数说明"></a>6.4.1 函数说明</h4><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td><code>CURDATE()</code></td><td>返回当前日期</td></tr><tr><td><code>CURTIME()</code></td><td>返回当前时间</td></tr><tr><td><code>NOW()</code></td><td>返回当前日期和时间</td></tr><tr><td><code>YEAR(date)</code></td><td>获取指定日期的年份</td></tr><tr><td><code>MONTH(date)</code></td><td>获取指定日期的月份</td></tr><tr><td><code>DAY(date)</code></td><td>获取指定日期的日</td></tr><tr><td><code>DATE_ADD(date, INTERVAL expr type)</code></td><td>返回加上时间间隔后的日期 &#x2F; 时间值</td></tr><tr><td><code>DATEDIFF(date1,date2)</code></td><td>返回两个日期之间相差的天数(前减后)</td></tr></tbody></table><h4 id="6-4-2-示例"><a href="#6-4-2-示例" class="headerlink" title="6.4.2 示例"></a>6.4.2 示例</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> CURDATE();<br><span class="hljs-keyword">SELECT</span> CURTIME();<br><span class="hljs-keyword">SELECT</span> NOW();<br><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">YEAR</span>(NOW());<br><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MONTH</span>(NOW());<br><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DAY</span>(NOW());<br><span class="hljs-keyword">SELECT</span> DATE_ADD(NOW(), <span class="hljs-type">INTERVAL</span> <span class="hljs-number">70</span> <span class="hljs-keyword">YEAR</span>);<br><span class="hljs-keyword">SELECT</span> DATEDIFF(<span class="hljs-string">&#x27;2021-10-01&#x27;</span>, <span class="hljs-string">&#x27;2021-12-01&#x27;</span>);<br></code></pre></td></tr></table></figure><h4 id="6-4-3-案例"><a href="#6-4-3-案例" class="headerlink" title="6.4.3 案例"></a>6.4.3 案例</h4><p>查询所有员工的入职天数，并按入职天数倒序排序：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> name, DATEDIFF(CURDATE(), entrydate) <span class="hljs-keyword">AS</span> entrydays<br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> entrydays <span class="hljs-keyword">DESC</span>;<br></code></pre></td></tr></table></figure><h3 id="6-5-流程函数"><a href="#6-5-流程函数" class="headerlink" title="6.5 流程函数"></a>6.5 流程函数</h3><h4 id="6-5-1-函数说明"><a href="#6-5-1-函数说明" class="headerlink" title="6.5.1 函数说明"></a>6.5.1 函数说明</h4><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td><code>IF(value, t, f)</code></td><td>如果 <code>value</code> 为真，返回 <code>t</code>，否则返回 <code>f</code></td></tr><tr><td><code>IFNULL(value1, value2)</code></td><td>如果 <code>value1</code> 不为空，返回 <code>value1</code>，否则返回 <code>value2</code></td></tr><tr><td><code>CASE WHEN [条件] THEN [结果] ... ELSE [默认值] END</code></td><td>类似多分支判断</td></tr><tr><td><code>CASE expr WHEN val1 THEN res1 ... ELSE default END</code></td><td>判断表达式的值并返回对应结果</td></tr></tbody></table><h4 id="6-5-2-示例"><a href="#6-5-2-示例" class="headerlink" title="6.5.2 示例"></a>6.5.2 示例</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> IF(<span class="hljs-literal">FALSE</span>, <span class="hljs-string">&#x27;Ok&#x27;</span>, <span class="hljs-string">&#x27;Error&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> IFNULL(<span class="hljs-string">&#x27;Ok&#x27;</span>, <span class="hljs-string">&#x27;Default&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> IFNULL(<span class="hljs-string">&#x27;&#x27;</span>, <span class="hljs-string">&#x27;Default&#x27;</span>);<br><span class="hljs-keyword">SELECT</span> IFNULL(<span class="hljs-keyword">NULL</span>, <span class="hljs-string">&#x27;Default&#x27;</span>);<br></code></pre></td></tr></table></figure><h4 id="6-5-3-案例1"><a href="#6-5-3-案例1" class="headerlink" title="6.5.3 案例1"></a>6.5.3 案例1</h4><p>查询员工姓名和工作地址等级：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    name,<br>    (<span class="hljs-keyword">CASE</span> workaddress<br>        <span class="hljs-keyword">WHEN</span> <span class="hljs-string">&#x27;北京&#x27;</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;一线城市&#x27;</span><br>        <span class="hljs-keyword">WHEN</span> <span class="hljs-string">&#x27;上海&#x27;</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;一线城市&#x27;</span><br>        <span class="hljs-keyword">ELSE</span> <span class="hljs-string">&#x27;二线城市&#x27;</span><br>    <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> 工作地址<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h4 id="6-5-4-案例2"><a href="#6-5-4-案例2" class="headerlink" title="6.5.4 案例2"></a>6.5.4 案例2</h4><p>按成绩判断等级：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    id,<br>    name,<br>    (<span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> math <span class="hljs-operator">&gt;=</span> <span class="hljs-number">85</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;优秀&#x27;</span> <span class="hljs-keyword">WHEN</span> math <span class="hljs-operator">&gt;=</span> <span class="hljs-number">60</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;及格&#x27;</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-string">&#x27;不及格&#x27;</span> <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> 数学,<br>    (<span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> english <span class="hljs-operator">&gt;=</span> <span class="hljs-number">85</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;优秀&#x27;</span> <span class="hljs-keyword">WHEN</span> english <span class="hljs-operator">&gt;=</span> <span class="hljs-number">60</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;及格&#x27;</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-string">&#x27;不及格&#x27;</span> <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> 英语,<br>    (<span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> chinese <span class="hljs-operator">&gt;=</span> <span class="hljs-number">85</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;优秀&#x27;</span> <span class="hljs-keyword">WHEN</span> chinese <span class="hljs-operator">&gt;=</span> <span class="hljs-number">60</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;及格&#x27;</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-string">&#x27;不及格&#x27;</span> <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> 语文<br><span class="hljs-keyword">FROM</span> score;<br></code></pre></td></tr></table></figure><h3 id="6-6-窗口函数"><a href="#6-6-窗口函数" class="headerlink" title="6.6 窗口函数"></a>6.6 窗口函数</h3><h4 id="6-6-1-概述"><a href="#6-6-1-概述" class="headerlink" title="6.6.1 概述"></a>6.6.1 概述</h4><ul><li>窗口函数用于在<strong>保留明细行</strong>的前提下，对某一组数据进行统计、排序、排名或前后行分析。</li><li>它和普通聚合函数的区别是：聚合函数通常会“多行变一行”，而窗口函数不会减少结果行数。</li><li>窗口函数通常搭配 <code>OVER()</code> 使用，可以在窗口中指定分组、排序和计算范围。</li></ul><h4 id="6-6-2-基本语法"><a href="#6-6-2-基本语法" class="headerlink" title="6.6.2 基本语法"></a>6.6.2 基本语法</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql">函数名() <span class="hljs-keyword">OVER</span>(<br>    [<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> 分组字段]<br>    [<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> 排序字段]<br>)<br></code></pre></td></tr></table></figure><p><strong>说明：</strong></p><ul><li><code>PARTITION BY</code>：把结果集划分为多个窗口，类似分组，但不会合并行。</li><li><code>ORDER BY</code>：定义窗口内部的排序规则，常用于排名、累计计算等场景。</li><li>如果只写 <code>OVER()</code>，表示在整个结果集范围内计算。</li><li><code>OVER</code>关键字的作用是：新增一列，其计算规则写在括号里</li><li>窗口函数不改变原表行数，只是在每一行旁边加一个或几个你想要的字段</li><li><code>OVER</code>前面的函数返回的是按照OVER里定义的规则，为当前行计算出的单个值，也就是一个字段的值</li></ul><h4 id="6-6-3-常见窗口函数"><a href="#6-6-3-常见窗口函数" class="headerlink" title="6.6.3 常见窗口函数"></a>6.6.3 常见窗口函数</h4><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td><code>ROW_NUMBER() OVER(...)</code></td><td>为窗口中的每一行生成唯一顺序号，不会并列</td></tr><tr><td><code>RANK() OVER(...)</code></td><td>排名，遇到并列时跳号(返回所有并列的)</td></tr><tr><td><code>DENSE_RANK() OVER(...)</code></td><td>密集排名，遇到并列时不跳号</td></tr><tr><td><code>SUM(col) OVER(...)</code></td><td>在窗口内做累计求和 &#x2F; 分组求和</td></tr><tr><td><code>AVG(col) OVER(...)</code></td><td>在窗口内做平均值统计</td></tr><tr><td><code>COUNT(col) OVER(...)</code></td><td>在窗口内统计记录数</td></tr><tr><td><code>MAX(col) OVER(...)</code></td><td>在窗口内求最大值</td></tr><tr><td><code>MIN(col) OVER(...)</code></td><td>在窗口内求最小值</td></tr><tr><td><code>LAG(col, n, default) OVER(...)</code></td><td>取当前行前第 <code>n</code> 行的值</td></tr><tr><td><code>LEAD(col, n, default) OVER(...)</code></td><td>取当前行后第 <code>n</code> 行的值</td></tr></tbody></table><h4 id="6-6-4-排名函数示例"><a href="#6-6-4-排名函数示例" class="headerlink" title="6.6.4 排名函数示例"></a>6.6.4 排名函数示例</h4><p>按部门内员工工资从高到低排名：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    name,<br>    dept_id,<br>    salary,<br>    <span class="hljs-built_in">ROW_NUMBER</span>() <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> dept_id <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> salary <span class="hljs-keyword">DESC</span>) <span class="hljs-keyword">AS</span> rn,<br>    <span class="hljs-built_in">RANK</span>() <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> dept_id <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> salary <span class="hljs-keyword">DESC</span>) <span class="hljs-keyword">AS</span> rk,<br>    <span class="hljs-built_in">DENSE_RANK</span>() <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> dept_id <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> salary <span class="hljs-keyword">DESC</span>) <span class="hljs-keyword">AS</span> drk<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><p><strong>区别：</strong></p><ul><li><code>ROW_NUMBER()</code>：即使工资相同，也会给不同序号，如 <code>1、2、3</code></li><li><code>RANK()</code>：工资相同会并列，后面的名次跳号，如 <code>1、1、3</code></li><li><code>DENSE_RANK()</code>：工资相同会并列，但后面的名次不跳号，如 <code>1、1、2</code></li></ul><h4 id="6-6-5-分析函数示例"><a href="#6-6-5-分析函数示例" class="headerlink" title="6.6.5 分析函数示例"></a>6.6.5 分析函数示例</h4><ol><li>统计每个部门的员工人数，同时保留员工明细：</li></ol><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    name,<br>    dept_id,<br>    <span class="hljs-built_in">COUNT</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> dept_id) <span class="hljs-keyword">AS</span> dept_count<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><ol start="2"><li>统计每个部门的工资累计值：</li></ol><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    name,<br>    dept_id,<br>    salary,<br>    <span class="hljs-built_in">SUM</span>(salary) <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> dept_id <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> salary <span class="hljs-keyword">DESC</span>) <span class="hljs-keyword">AS</span> dept_salary_sum<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><ol start="3"><li>查看当前员工与上一位员工的工资差异：</li></ol><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    name,<br>    salary,<br>    <span class="hljs-built_in">LAG</span>(salary, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>) <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> salary <span class="hljs-keyword">DESC</span>) <span class="hljs-keyword">AS</span> prev_salary,<br>    salary <span class="hljs-operator">-</span> <span class="hljs-built_in">LAG</span>(salary, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>) <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> salary <span class="hljs-keyword">DESC</span>) <span class="hljs-keyword">AS</span> diff_salary<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><p><strong>适用场景：</strong></p><ul><li>TopN 排名</li><li>分组内排序</li><li>累计求和 &#x2F; 累计平均</li><li>同比、环比、前后行对比</li><li>保留明细的同时做统计分析</li></ul><h2 id="7-约束"><a href="#7-约束" class="headerlink" title="7 约束"></a>7 约束</h2><h3 id="7-1-概述"><a href="#7-1-概述" class="headerlink" title="7.1 概述"></a>7.1 概述</h3><ul><li>约束是作用于表中字段上的规则，用于限制存储在表中的数据。</li><li>目的是保证数据库中数据的正确、有效性和完整性。</li></ul><h3 id="7-2-常见约束"><a href="#7-2-常见约束" class="headerlink" title="7.2 常见约束"></a>7.2 常见约束</h3><table><thead><tr><th>约束</th><th>描述</th><th>关键字</th></tr></thead><tbody><tr><td>非空约束</td><td>限制该字段不能为 <code>NULL</code></td><td><code>NOT NULL</code></td></tr><tr><td>唯一约束</td><td>保证该字段所有数据唯一、不重复</td><td><code>UNIQUE</code></td></tr><tr><td>主键约束</td><td>一行数据的唯一标识，要求非空且唯一</td><td><code>PRIMARY KEY</code></td></tr><tr><td>默认约束</td><td>未指定该字段值时采用默认值</td><td><code>DEFAULT</code></td></tr><tr><td>检查约束</td><td>保证字段值满足某个条件</td><td><code>CHECK</code></td></tr><tr><td>外键约束</td><td>让两张表建立连接，保证一致性和完整性</td><td><code>FOREIGN KEY</code></td></tr></tbody></table><p><strong>注意：</strong></p><ul><li>约束可以在创建表时添加，也可以在修改表时添加。</li></ul><h3 id="7-3-约束示例"><a href="#7-3-约束示例" class="headerlink" title="7.3 约束示例"></a>7.3 约束示例</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">CREATE TABLE</span> tb_user(<br>    id <span class="hljs-type">INT</span> AUTO_INCREMENT <span class="hljs-keyword">PRIMARY KEY</span> COMMENT <span class="hljs-string">&#x27;ID 唯一标识&#x27;</span>,<br>    name <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">NOT NULL</span> <span class="hljs-keyword">UNIQUE</span> COMMENT <span class="hljs-string">&#x27;姓名&#x27;</span>,<br>    age <span class="hljs-type">INT</span> <span class="hljs-keyword">CHECK</span> (age <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span> <span class="hljs-keyword">AND</span> age <span class="hljs-operator">&lt;=</span> <span class="hljs-number">120</span>) COMMENT <span class="hljs-string">&#x27;年龄&#x27;</span>,<br>    status <span class="hljs-type">CHAR</span>(<span class="hljs-number">1</span>) <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">&#x27;1&#x27;</span> COMMENT <span class="hljs-string">&#x27;状态&#x27;</span>,<br>    gender <span class="hljs-type">CHAR</span>(<span class="hljs-number">1</span>) COMMENT <span class="hljs-string">&#x27;性别&#x27;</span><br>);<br></code></pre></td></tr></table></figure><h3 id="7-4-外键约束"><a href="#7-4-外键约束" class="headerlink" title="7.4 外键约束"></a>7.4 外键约束</h3><h4 id="7-4-1-介绍"><a href="#7-4-1-介绍" class="headerlink" title="7.4.1 介绍"></a>7.4.1 介绍</h4><ul><li>外键用来让两张表的数据之间建立连接，从而保证数据的一致性和完整性。</li><li>一般子表中保存外键字段，关联父表主键。</li></ul><h4 id="7-4-2-添加外键"><a href="#7-4-2-添加外键" class="headerlink" title="7.4.2 添加外键"></a>7.4.2 添加外键</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs sql"><span class="hljs-keyword">CREATE TABLE</span> 表名(<br>    字段名 数据类型,<br>    ...<br>    [<span class="hljs-keyword">CONSTRAINT</span>] [外键名称] <span class="hljs-keyword">FOREIGN KEY</span> (外键字段名) <span class="hljs-keyword">REFERENCES</span> 主表(主表列名)<br>);<br></code></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 表名<br><span class="hljs-keyword">ADD CONSTRAINT</span> 外键名称 <span class="hljs-keyword">FOREIGN KEY</span> (外键字段名) <span class="hljs-keyword">REFERENCES</span> 主表(主表列名);<br></code></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> emp<br><span class="hljs-keyword">ADD CONSTRAINT</span> fk_emp_dept_id <span class="hljs-keyword">FOREIGN KEY</span> (dept_id) <span class="hljs-keyword">REFERENCES</span> dept(id);<br></code></pre></td></tr></table></figure><h4 id="7-4-3-删除外键"><a href="#7-4-3-删除外键" class="headerlink" title="7.4.3 删除外键"></a>7.4.3 删除外键</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 表名 <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">FOREIGN KEY</span> 外键名称;<br></code></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> emp <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">FOREIGN KEY</span> fk_emp_dept_id;<br></code></pre></td></tr></table></figure><h4 id="7-4-4-删除-更新行为"><a href="#7-4-4-删除-更新行为" class="headerlink" title="7.4.4 删除 &#x2F; 更新行为"></a>7.4.4 删除 &#x2F; 更新行为</h4><table><thead><tr><th>行为</th><th>说明</th></tr></thead><tbody><tr><td><code>NO ACTION</code></td><td>有关联外键时，不允许删除 &#x2F; 更新父表记录，默认行为</td></tr><tr><td><code>RESTRICT</code></td><td>与 <code>NO ACTION</code> 一致</td></tr><tr><td><code>CASCADE</code></td><td>父表删除 &#x2F; 更新时，子表对应记录也同步删除 &#x2F; 更新</td></tr><tr><td><code>SET NULL</code></td><td>父表删除 &#x2F; 更新时，子表外键字段置为 <code>NULL</code></td></tr><tr><td><code>SET DEFAULT</code></td><td>父表变更时，子表外键列设为默认值，<code>InnoDB</code> 不支持</td></tr></tbody></table><h4 id="7-4-5-指定级联行为"><a href="#7-4-5-指定级联行为" class="headerlink" title="7.4.5 指定级联行为"></a>7.4.5 指定级联行为</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">ALTER TABLE</span> 表名<br><span class="hljs-keyword">ADD CONSTRAINT</span> 外键名称 <span class="hljs-keyword">FOREIGN KEY</span> (外键字段) <span class="hljs-keyword">REFERENCES</span> 主表名(主表字段名)<br><span class="hljs-keyword">ON</span> <span class="hljs-keyword">UPDATE</span> CASCADE<br><span class="hljs-keyword">ON</span> <span class="hljs-keyword">DELETE</span> CASCADE;<br></code></pre></td></tr></table></figure><h2 id="8-多表查询"><a href="#8-多表查询" class="headerlink" title="8 多表查询"></a>8 多表查询</h2><h3 id="8-1-多表关系"><a href="#8-1-多表关系" class="headerlink" title="8.1 多表关系"></a>8.1 多表关系</h3><h4 id="8-1-1-一对多"><a href="#8-1-1-一对多" class="headerlink" title="8.1.1 一对多"></a>8.1.1 一对多</h4><ul><li>例子：部门 和 员工</li><li>关系：一个部门对应多个员工，一个员工对应一个部门</li><li>实现：在多的一方建立外键，指向一的一方主键</li></ul><h4 id="8-1-2-多对多"><a href="#8-1-2-多对多" class="headerlink" title="8.1.2 多对多"></a>8.1.2 多对多</h4><ul><li>例子：学生 和 课程</li><li>关系：一个学生可以选多门课程，一门课程也可以被多个学生选择</li><li>实现：建立第三张中间表，中间表至少包含两个外键，分别关联两边主键</li></ul><h4 id="8-1-3-一对一"><a href="#8-1-3-一对一" class="headerlink" title="8.1.3 一对一"></a>8.1.3 一对一</h4><ul><li>例子：用户 和 用户详情</li><li>关系：常用于单表拆分，把基础字段和详情字段拆到两张表</li><li>实现：在任意一方加入外键关联另一方主键，并且设置该外键唯一 <code>UNIQUE</code></li></ul><h3 id="8-2-多表查询概述"><a href="#8-2-多表查询概述" class="headerlink" title="8.2 多表查询概述"></a>8.2 多表查询概述</h3><h4 id="8-2-1-概述"><a href="#8-2-1-概述" class="headerlink" title="8.2.1 概述"></a>8.2.1 概述</h4><ul><li>多表查询就是从多张表中查询数据。</li><li>直接查询多张表会出现笛卡尔积，需要通过连接条件消除无效数据。</li></ul><h4 id="8-2-2-分类"><a href="#8-2-2-分类" class="headerlink" title="8.2.2 分类"></a>8.2.2 分类</h4><ul><li>连接查询</li><li>子查询</li></ul><h4 id="8-2-3-连接查询分类"><a href="#8-2-3-连接查询分类" class="headerlink" title="8.2.3 连接查询分类"></a>8.2.3 连接查询分类</h4><ul><li>内连接：查询两张表交集部分数据</li><li>左外连接：查询左表所有数据，以及交集部分数据</li><li>右外连接：查询右表所有数据，以及交集部分数据</li><li>自连接：当前表与自身进行连接查询</li></ul><h3 id="8-3-内连接"><a href="#8-3-内连接" class="headerlink" title="8.3 内连接"></a>8.3 内连接</h3><h4 id="8-3-1-隐式内连接"><a href="#8-3-1-隐式内连接" class="headerlink" title="8.3.1 隐式内连接"></a>8.3.1 隐式内连接</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表<span class="hljs-number">1</span>, 表<span class="hljs-number">2</span><br><span class="hljs-keyword">WHERE</span> 条件;<br></code></pre></td></tr></table></figure><h4 id="8-3-2-显式内连接"><a href="#8-3-2-显式内连接" class="headerlink" title="8.3.2 显式内连接"></a>8.3.2 显式内连接</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表<span class="hljs-number">1</span> [<span class="hljs-keyword">INNER</span>] <span class="hljs-keyword">JOIN</span> 表<span class="hljs-number">2</span> <span class="hljs-keyword">ON</span> 连接条件;<br></code></pre></td></tr></table></figure><h4 id="8-3-3-示例"><a href="#8-3-3-示例" class="headerlink" title="8.3.3 示例"></a>8.3.3 示例</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> e.name, d.name<br><span class="hljs-keyword">FROM</span> emp e, dept d<br><span class="hljs-keyword">WHERE</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> e.name, d.name<br><span class="hljs-keyword">FROM</span> emp e<br><span class="hljs-keyword">INNER</span> <span class="hljs-keyword">JOIN</span> dept d <span class="hljs-keyword">ON</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>一旦为表起了别名，就不能再使用原表名引用字段，只能使用别名。</li></ul><h3 id="8-4-外连接"><a href="#8-4-外连接" class="headerlink" title="8.4 外连接"></a>8.4 外连接</h3><h4 id="8-4-1-左外连接"><a href="#8-4-1-左外连接" class="headerlink" title="8.4.1 左外连接"></a>8.4.1 左外连接</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表<span class="hljs-number">1</span><br><span class="hljs-keyword">LEFT</span> [<span class="hljs-keyword">OUTER</span>] <span class="hljs-keyword">JOIN</span> 表<span class="hljs-number">2</span> <span class="hljs-keyword">ON</span> 条件;<br></code></pre></td></tr></table></figure><h4 id="8-4-2-右外连接"><a href="#8-4-2-右外连接" class="headerlink" title="8.4.2 右外连接"></a>8.4.2 右外连接</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表<span class="hljs-number">1</span><br><span class="hljs-keyword">RIGHT</span> [<span class="hljs-keyword">OUTER</span>] <span class="hljs-keyword">JOIN</span> 表<span class="hljs-number">2</span> <span class="hljs-keyword">ON</span> 条件;<br></code></pre></td></tr></table></figure><h4 id="8-4-3-示例"><a href="#8-4-3-示例" class="headerlink" title="8.4.3 示例"></a>8.4.3 示例</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> e.<span class="hljs-operator">*</span>, d.name<br><span class="hljs-keyword">FROM</span> emp e<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> dept d <span class="hljs-keyword">ON</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> d.<span class="hljs-operator">*</span>, e.<span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> dept d<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> emp e <span class="hljs-keyword">ON</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>左外连接和右外连接可以相互替换，开发中更常用左外连接。</li><li>判断表的相对位置取决于表名在JOIN关键字的哪一边。</li><li>JOIN 左边的就是左表，右边的就是右表，与 LEFT 或 RIGHT 关键字无关——这两个关键字只是告诉你保留哪一侧的全部数据。</li><li>我自己的理解：<br>ON：定义连接规则，根据规则把多张表横向拼接成一张包含所有列的“虚拟中间表”。<br>WHERE：对这个“虚拟中间表”进行纵向筛选（按行过滤），得到最终要用的数据。</li></ul><h3 id="8-5-自连接"><a href="#8-5-自连接" class="headerlink" title="8.5 自连接"></a>8.5 自连接</h3><h4 id="8-5-1-语法"><a href="#8-5-1-语法" class="headerlink" title="8.5.1 语法"></a>8.5.1 语法</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表<br><span class="hljs-keyword">FROM</span> 表A 别名A<br><span class="hljs-keyword">JOIN</span> 表A 别名B <span class="hljs-keyword">ON</span> 条件;<br></code></pre></td></tr></table></figure><h4 id="8-5-2-示例"><a href="#8-5-2-示例" class="headerlink" title="8.5.2 示例"></a>8.5.2 示例</h4><p>查询员工及其直属领导名字：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> a.name <span class="hljs-keyword">AS</span> 员工, b.name <span class="hljs-keyword">AS</span> 领导<br><span class="hljs-keyword">FROM</span> emp a<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> emp b <span class="hljs-keyword">ON</span> a.managerid <span class="hljs-operator">=</span> b.id;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>自连接必须起别名，否则无法区分字段来自哪一张表。</li></ul><h3 id="8-6-联合查询"><a href="#8-6-联合查询" class="headerlink" title="8.6 联合查询"></a>8.6 联合查询</h3><h4 id="8-6-1-语法"><a href="#8-6-1-语法" class="headerlink" title="8.6.1 语法"></a>8.6.1 语法</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> 字段列表 <span class="hljs-keyword">FROM</span> 表A ...<br><span class="hljs-keyword">UNION</span> [<span class="hljs-keyword">ALL</span>]<br><span class="hljs-keyword">SELECT</span> 字段列表 <span class="hljs-keyword">FROM</span> 表B ...;<br></code></pre></td></tr></table></figure><h4 id="8-6-2-说明"><a href="#8-6-2-说明" class="headerlink" title="8.6.2 说明"></a>8.6.2 说明</h4><ul><li><code>UNION ALL</code>：直接合并结果，不去重</li><li><code>UNION</code>：合并结果并去重</li></ul><h4 id="8-6-3-示例"><a href="#8-6-3-示例" class="headerlink" title="8.6.3 示例"></a>8.6.3 示例</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> emp <span class="hljs-keyword">WHERE</span> salary <span class="hljs-operator">&lt;</span> <span class="hljs-number">5000</span><br><span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span><br><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> emp <span class="hljs-keyword">WHERE</span> age <span class="hljs-operator">&gt;</span> <span class="hljs-number">50</span>;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>多个查询结果的列数必须一致，字段类型也应保持一致。</li></ul><h3 id="8-7-子查询"><a href="#8-7-子查询" class="headerlink" title="8.7 子查询"></a>8.7 子查询</h3><h4 id="8-7-1-概述"><a href="#8-7-1-概述" class="headerlink" title="8.7.1 概述"></a>8.7.1 概述</h4><ul><li>SQL 语句中嵌套 <code>SELECT</code> 语句，称为子查询。</li><li>子查询外部语句可以是 <code>INSERT</code>、<code>UPDATE</code>、<code>DELETE</code>、<code>SELECT</code>。</li></ul><h5 id="8-7-1-1-按结果分类"><a href="#8-7-1-1-按结果分类" class="headerlink" title="8.7.1.1 按结果分类"></a>8.7.1.1 按结果分类</h5><ul><li>标量子查询：结果为单个值</li><li>列子查询：结果为一列</li><li>行子查询：结果为一行</li><li>表子查询：结果为多行多列</li></ul><h5 id="8-7-1-2-按位置分类"><a href="#8-7-1-2-按位置分类" class="headerlink" title="8.7.1.2 按位置分类"></a>8.7.1.2 按位置分类</h5><ul><li><code>WHERE</code> 之后</li><li><code>FROM</code> 之后</li><li><code>SELECT</code> 之后</li></ul><h3 id="8-8-标量子查询"><a href="#8-8-标量子查询" class="headerlink" title="8.8 标量子查询"></a>8.8 标量子查询</h3><h4 id="8-8-1-常用操作符"><a href="#8-8-1-常用操作符" class="headerlink" title="8.8.1 常用操作符"></a>8.8.1 常用操作符</h4><ul><li><code>=</code></li><li><code>&lt;&gt;</code></li><li><code>&gt;</code></li><li><code>&gt;=</code></li><li><code>&lt;</code></li><li><code>&lt;=</code></li></ul><h4 id="8-8-2-示例"><a href="#8-8-2-示例" class="headerlink" title="8.8.2 示例"></a>8.8.2 示例</h4><p>查询销售部所有员工：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> dept <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;销售部&#x27;</span>);<br></code></pre></td></tr></table></figure><p>查询在“方东白”入职之后的员工信息：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> entrydate <span class="hljs-operator">&gt;</span> (<span class="hljs-keyword">SELECT</span> entrydate <span class="hljs-keyword">FROM</span> emp <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;方东白&#x27;</span>);<br></code></pre></td></tr></table></figure><h3 id="8-9-列子查询"><a href="#8-9-列子查询" class="headerlink" title="8.9 列子查询"></a>8.9 列子查询</h3><h4 id="8-9-1-常用操作符"><a href="#8-9-1-常用操作符" class="headerlink" title="8.9.1 常用操作符"></a>8.9.1 常用操作符</h4><ul><li><code>IN</code></li><li><code>NOT IN</code></li><li><code>ANY</code></li><li><code>SOME</code></li><li><code>ALL</code></li></ul><h4 id="8-9-2-示例"><a href="#8-9-2-示例" class="headerlink" title="8.9.2 示例"></a>8.9.2 示例</h4><p>查询销售部和市场部所有员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-keyword">IN</span> (<br>    <span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> dept <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;销售部&#x27;</span> <span class="hljs-keyword">OR</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;市场部&#x27;</span><br>);<br></code></pre></td></tr></table></figure><p>查询比财务部所有人工资都高的员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> salary <span class="hljs-operator">&gt;</span> <span class="hljs-keyword">ALL</span> (<br>    <span class="hljs-keyword">SELECT</span> salary<br>    <span class="hljs-keyword">FROM</span> emp<br>    <span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> dept <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;财务部&#x27;</span>)<br>);<br></code></pre></td></tr></table></figure><p>查询比研发部任意一人工资高的员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> salary <span class="hljs-operator">&gt;</span> <span class="hljs-keyword">ANY</span> (<br>    <span class="hljs-keyword">SELECT</span> salary<br>    <span class="hljs-keyword">FROM</span> emp<br>    <span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> dept <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;研发部&#x27;</span>)<br>);<br></code></pre></td></tr></table></figure><h3 id="8-10-行子查询"><a href="#8-10-行子查询" class="headerlink" title="8.10 行子查询"></a>8.10 行子查询</h3><h4 id="8-10-1-常用操作符"><a href="#8-10-1-常用操作符" class="headerlink" title="8.10.1 常用操作符"></a>8.10.1 常用操作符</h4><ul><li><code>=</code></li><li><code>&lt;&gt;</code></li><li><code>IN</code></li><li><code>NOT IN</code></li></ul><h4 id="8-10-2-示例"><a href="#8-10-2-示例" class="headerlink" title="8.10.2 示例"></a>8.10.2 示例</h4><p>查询与“张无忌”的薪资和直属领导相同的员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> (salary, managerid) <span class="hljs-operator">=</span> (<br>    <span class="hljs-keyword">SELECT</span> salary, managerid<br>    <span class="hljs-keyword">FROM</span> emp<br>    <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张无忌&#x27;</span><br>);<br></code></pre></td></tr></table></figure><h3 id="8-11-表子查询"><a href="#8-11-表子查询" class="headerlink" title="8.11 表子查询"></a>8.11 表子查询</h3><h4 id="8-11-1-常用操作符"><a href="#8-11-1-常用操作符" class="headerlink" title="8.11.1 常用操作符"></a>8.11.1 常用操作符</h4><ul><li><code>IN</code></li></ul><h4 id="8-11-2-示例"><a href="#8-11-2-示例" class="headerlink" title="8.11.2 示例"></a>8.11.2 示例</h4><p>查询与“鹿杖客”、“宋远桥”的职位和薪资相同的员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> (job, salary) <span class="hljs-keyword">IN</span> (<br>    <span class="hljs-keyword">SELECT</span> job, salary<br>    <span class="hljs-keyword">FROM</span> emp<br>    <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;鹿杖客&#x27;</span> <span class="hljs-keyword">OR</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;宋远桥&#x27;</span><br>);<br></code></pre></td></tr></table></figure><p>查询入职日期在 <code>2006-01-01</code> 之后的员工信息及其部门信息：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> e.<span class="hljs-operator">*</span>, d.<span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> (<br>    <span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br>    <span class="hljs-keyword">FROM</span> emp<br>    <span class="hljs-keyword">WHERE</span> entrydate <span class="hljs-operator">&gt;</span> <span class="hljs-string">&#x27;2006-01-01&#x27;</span><br>) e<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> dept d <span class="hljs-keyword">ON</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><h3 id="8-12-多表查询案例"><a href="#8-12-多表查询案例" class="headerlink" title="8.12 多表查询案例"></a>8.12 多表查询案例</h3><h4 id="8-12-1-查询所有部门信息，并统计部门员工人数"><a href="#8-12-1-查询所有部门信息，并统计部门员工人数" class="headerlink" title="8.12.1 查询所有部门信息，并统计部门员工人数"></a>8.12.1 查询所有部门信息，并统计部门员工人数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> d.id, d.name, (<br>    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">COUNT</span>(<span class="hljs-operator">*</span>)<br>    <span class="hljs-keyword">FROM</span> emp e<br>    <span class="hljs-keyword">WHERE</span> e.dept_id <span class="hljs-operator">=</span> d.id<br>) <span class="hljs-keyword">AS</span> 人数<br><span class="hljs-keyword">FROM</span> dept d;<br></code></pre></td></tr></table></figure><h4 id="8-12-2-查询所有学生的选课情况"><a href="#8-12-2-查询所有学生的选课情况" class="headerlink" title="8.12.2 查询所有学生的选课情况"></a>8.12.2 查询所有学生的选课情况</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> s.name, s.no, c.name<br><span class="hljs-keyword">FROM</span> student s, student_course sc, course c<br><span class="hljs-keyword">WHERE</span> s.id <span class="hljs-operator">=</span> sc.studentid<br>  <span class="hljs-keyword">AND</span> sc.courseid <span class="hljs-operator">=</span> c.id;<br></code></pre></td></tr></table></figure><h2 id="9-事务"><a href="#9-事务" class="headerlink" title="9 事务"></a>9 事务</h2><h3 id="9-1-事务简介"><a href="#9-1-事务简介" class="headerlink" title="9.1 事务简介"></a>9.1 事务简介</h3><ul><li>事务是一组操作的集合，是一个不可分割的工作单位。</li><li>事务中的操作要么同时成功，要么同时失败。</li><li>MySQL 默认自动提交事务，执行一条 <code>DML</code> 语句后会立即隐式提交。</li><li>典型场景：转账操作通常至少包含“扣款”和“加款”两个步骤，这两个步骤必须作为一个整体执行。</li><li>如果执行过程中中途报错，而前面的语句已经生效、后面的语句没有生效，就会造成数据不一致。</li><li>因此事务的核心价值就是：在业务操作成功时统一提交，在业务操作失败时统一回滚，把数据恢复到事务开始之前的状态。</li></ul><h3 id="9-2-事务操作"><a href="#9-2-事务操作" class="headerlink" title="9.2 事务操作"></a>9.2 事务操作</h3><h4 id="9-2-1-方式一：控制自动提交"><a href="#9-2-1-方式一：控制自动提交" class="headerlink" title="9.2.1 方式一：控制自动提交"></a>9.2.1 方式一：控制自动提交</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> @<span class="hljs-variable">@autocommit</span>;<br><span class="hljs-keyword">SET</span> @<span class="hljs-variable">@autocommit</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br><span class="hljs-keyword">COMMIT</span>;<br><span class="hljs-keyword">ROLLBACK</span>;<br></code></pre></td></tr></table></figure><p><strong>说明：</strong></p><ul><li>把 <code>autocommit</code> 设为 <code>0</code> 后，后续执行的 <code>DML</code> 语句不会自动提交，需要手动执行 <code>COMMIT</code>。</li><li>如果执行过程中出现异常，可以执行 <code>ROLLBACK</code> 撤销本次事务中的修改。</li></ul><h4 id="9-2-2-方式二：手动开启事务"><a href="#9-2-2-方式二：手动开启事务" class="headerlink" title="9.2.2 方式二：手动开启事务"></a>9.2.2 方式二：手动开启事务</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs sql"><span class="hljs-keyword">START</span> TRANSACTION;<br><span class="hljs-comment">-- 或 BEGIN;</span><br><br><span class="hljs-keyword">COMMIT</span>;<br><span class="hljs-keyword">ROLLBACK</span>;<br></code></pre></td></tr></table></figure><p><strong>说明：</strong></p><ul><li>这种方式更常用，也更清晰：显式开启事务，业务执行完成后再决定提交或回滚。</li><li><code>START TRANSACTION</code> 和 <code>BEGIN</code> 都可以用来开启事务。</li></ul><h4 id="9-2-3-转账案例"><a href="#9-2-3-转账案例" class="headerlink" title="9.2.3 转账案例"></a>9.2.3 转账案例</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs sql"><span class="hljs-keyword">START</span> TRANSACTION;<br><br><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> account <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张三&#x27;</span>;<br><span class="hljs-keyword">UPDATE</span> account <span class="hljs-keyword">SET</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">-</span> <span class="hljs-number">1000</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张三&#x27;</span>;<br><span class="hljs-keyword">UPDATE</span> account <span class="hljs-keyword">SET</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">+</span> <span class="hljs-number">1000</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;李四&#x27;</span>;<br><br><span class="hljs-keyword">COMMIT</span>;<br><span class="hljs-comment">-- 如果中途报错，则执行 ROLLBACK;</span><br></code></pre></td></tr></table></figure><h3 id="9-3-事务四大特性"><a href="#9-3-事务四大特性" class="headerlink" title="9.3 事务四大特性"></a>9.3 事务四大特性</h3><table><thead><tr><th>特性</th><th>说明</th></tr></thead><tbody><tr><td>原子性（Atomicity）</td><td>事务是最小操作单元，要么全部成功，要么全部失败</td></tr><tr><td>一致性（Consistency）</td><td>事务完成时，所有数据都必须保持一致状态</td></tr><tr><td>隔离性（Isolation）</td><td>多个事务之间相互隔离，互不干扰</td></tr><tr><td>持久性（Durability）</td><td>事务一旦提交或回滚，对数据的改变就是永久的</td></tr></tbody></table><h3 id="9-4-并发事务问题"><a href="#9-4-并发事务问题" class="headerlink" title="9.4 并发事务问题"></a>9.4 并发事务问题</h3><h4 id="9-4-1-脏读"><a href="#9-4-1-脏读" class="headerlink" title="9.4.1 脏读"></a>9.4.1 脏读</h4><ul><li>一个事务读到另一个事务还没有提交的数据。</li><li>比如事务 A 修改了一条记录但还没提交，事务 B 已经读取到了这条新值；如果事务 A 随后回滚，那么事务 B 读到的就是“脏数据”。</li></ul><h4 id="9-4-2-不可重复读"><a href="#9-4-2-不可重复读" class="headerlink" title="9.4.2 不可重复读"></a>9.4.2 不可重复读</h4><ul><li>一个事务先后读取同一条记录，两次读到的数据不同。</li><li>一般是因为两个读取之间，另一个事务已经提交了对该记录的更新。</li></ul><h4 id="9-4-3-幻读"><a href="#9-4-3-幻读" class="headerlink" title="9.4.3 幻读"></a>9.4.3 幻读</h4><ul><li>一个事务按条件查询时没有对应数据，但插入时却发现这行数据已存在，像出现了“幻影”。</li><li>本质上是同一事务中按照相同条件前后读取，结果集的“行数”发生了变化，通常和其他事务的插入操作有关。</li></ul><h3 id="9-5-事务隔离级别"><a href="#9-5-事务隔离级别" class="headerlink" title="9.5 事务隔离级别"></a>9.5 事务隔离级别</h3><h4 id="9-5-1-隔离级别说明"><a href="#9-5-1-隔离级别说明" class="headerlink" title="9.5.1 隔离级别说明"></a>9.5.1 隔离级别说明</h4><table><thead><tr><th>隔离级别</th><th>脏读</th><th>不可重复读</th><th>幻读</th></tr></thead><tbody><tr><td><code>READ UNCOMMITTED</code></td><td>会</td><td>会</td><td>会</td></tr><tr><td><code>READ COMMITTED</code></td><td>不会</td><td>会</td><td>会</td></tr><tr><td><code>REPEATABLE READ</code>（默认）</td><td>不会</td><td>不会</td><td>会</td></tr><tr><td><code>SERIALIZABLE</code></td><td>不会</td><td>不会</td><td>不会</td></tr></tbody></table><h4 id="9-5-2-查看隔离级别"><a href="#9-5-2-查看隔离级别" class="headerlink" title="9.5.2 查看隔离级别"></a>9.5.2 查看隔离级别</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> @<span class="hljs-variable">@TRANSACTION_ISOLATION</span>;<br></code></pre></td></tr></table></figure><h4 id="9-5-3-设置隔离级别"><a href="#9-5-3-设置隔离级别" class="headerlink" title="9.5.3 设置隔离级别"></a>9.5.3 设置隔离级别</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SET</span> [SESSION <span class="hljs-operator">|</span> <span class="hljs-keyword">GLOBAL</span>] TRANSACTION ISOLATION LEVEL<br>&#123; READ UNCOMMITTED <span class="hljs-operator">|</span> READ COMMITTED <span class="hljs-operator">|</span> REPEATABLE READ <span class="hljs-operator">|</span> SERIALIZABLE &#125;;<br></code></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>隔离级别越高，数据越安全，但性能越低。</li><li><code>REPEATABLE READ</code> 是 MySQL 中常见的默认隔离级别。</li><li>记忆规律可以简单理解为：隔离级别越往上，能解决的并发问题越多。</li></ul><h3 id="9-6-补充-事务控制过程"><a href="#9-6-补充-事务控制过程" class="headerlink" title="9.6 补充-事务控制过程"></a>9.6 补充-事务控制过程</h3><h4 id="9-6-1-未控制事务"><a href="#9-6-1-未控制事务" class="headerlink" title="9.6.1 未控制事务"></a>9.6.1 未控制事务</h4><h5 id="9-6-1-1-正常情况"><a href="#9-6-1-1-正常情况" class="headerlink" title="9.6.1.1 正常情况"></a>9.6.1.1 正常情况</h5><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> account <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张三&#x27;</span>;<br><span class="hljs-keyword">UPDATE</span> account <span class="hljs-keyword">SET</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">-</span> <span class="hljs-number">1000</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张三&#x27;</span>;<br><span class="hljs-keyword">UPDATE</span> account <span class="hljs-keyword">SET</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">+</span> <span class="hljs-number">1000</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;李四&#x27;</span>;<br></code></pre></td></tr></table></figure><h5 id="9-6-1-2-异常情况"><a href="#9-6-1-2-异常情况" class="headerlink" title="9.6.1.2 异常情况"></a>9.6.1.2 异常情况</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> account <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张三&#x27;</span>;<br><span class="hljs-keyword">UPDATE</span> account <span class="hljs-keyword">SET</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">-</span> <span class="hljs-number">1000</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张三&#x27;</span>;<br><span class="hljs-comment">-- 中途报错</span><br><span class="hljs-keyword">UPDATE</span> account <span class="hljs-keyword">SET</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">+</span> <span class="hljs-number">1000</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;李四&#x27;</span>;<br></code></pre></td></tr></table></figure><h4 id="9-6-2-控制事务一"><a href="#9-6-2-控制事务一" class="headerlink" title="9.6.2 控制事务一"></a>9.6.2 控制事务一</h4><h5 id="9-6-2-1-查看-设置事务提交方式"><a href="#9-6-2-1-查看-设置事务提交方式" class="headerlink" title="9.6.2.1 查看 &#x2F; 设置事务提交方式"></a>9.6.2.1 查看 &#x2F; 设置事务提交方式</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> @<span class="hljs-variable">@autocommit</span>;<br><span class="hljs-keyword">SET</span> @<span class="hljs-variable">@autocommit</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br></code></pre></td></tr></table></figure><h5 id="9-6-2-2-提交事务"><a href="#9-6-2-2-提交事务" class="headerlink" title="9.6.2.2 提交事务"></a>9.6.2.2 提交事务</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">COMMIT</span>;<br></code></pre></td></tr></table></figure><h5 id="9-6-2-3-回滚事务"><a href="#9-6-2-3-回滚事务" class="headerlink" title="9.6.2.3 回滚事务"></a>9.6.2.3 回滚事务</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ROLLBACK</span>;<br></code></pre></td></tr></table></figure><h4 id="9-6-3-控制事务二"><a href="#9-6-3-控制事务二" class="headerlink" title="9.6.3 控制事务二"></a>9.6.3 控制事务二</h4><h5 id="9-6-3-1-开启事务"><a href="#9-6-3-1-开启事务" class="headerlink" title="9.6.3.1 开启事务"></a>9.6.3.1 开启事务</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">START</span> TRANSACTION;<br><span class="hljs-comment">-- 或 BEGIN;</span><br></code></pre></td></tr></table></figure><h5 id="9-6-3-2-提交事务"><a href="#9-6-3-2-提交事务" class="headerlink" title="9.6.3.2 提交事务"></a>9.6.3.2 提交事务</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">COMMIT</span>;<br></code></pre></td></tr></table></figure><h5 id="9-6-3-3-回滚事务"><a href="#9-6-3-3-回滚事务" class="headerlink" title="9.6.3.3 回滚事务"></a>9.6.3.3 回滚事务</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">ROLLBACK</span>;<br></code></pre></td></tr></table></figure><h2 id="10-综合案例-员工与部门管理"><a href="#10-综合案例-员工与部门管理" class="headerlink" title="10 综合案例-员工与部门管理"></a>10 综合案例-员工与部门管理</h2><h3 id="10-1-案例说明"><a href="#10-1-案例说明" class="headerlink" title="10.1 案例说明"></a>10.1 案例说明</h3><p>下面这个案例尽量串起常用 &#x2F; 核心语法：数据库、建表、约束、外键、增删改查、函数、多表查询、子查询、事务。</p><h3 id="10-2-创建数据库并切换"><a href="#10-2-创建数据库并切换" class="headerlink" title="10.2 创建数据库并切换"></a>10.2 创建数据库并切换</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">CREATE</span> DATABASE IF <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> company_db <span class="hljs-keyword">DEFAULT</span> CHARSET utf8mb4;<br>USE company_db;<br></code></pre></td></tr></table></figure><h3 id="10-3-创建表"><a href="#10-3-创建表" class="headerlink" title="10.3 创建表"></a>10.3 创建表</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">CREATE TABLE</span> dept(<br>    id <span class="hljs-type">INT</span> <span class="hljs-keyword">PRIMARY KEY</span> AUTO_INCREMENT COMMENT <span class="hljs-string">&#x27;部门ID&#x27;</span>,<br>    name <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">20</span>) <span class="hljs-keyword">NOT NULL</span> <span class="hljs-keyword">UNIQUE</span> COMMENT <span class="hljs-string">&#x27;部门名称&#x27;</span><br>) COMMENT <span class="hljs-string">&#x27;部门表&#x27;</span>;<br></code></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs sql"><span class="hljs-keyword">CREATE TABLE</span> emp(<br>    id <span class="hljs-type">INT</span> <span class="hljs-keyword">PRIMARY KEY</span> AUTO_INCREMENT COMMENT <span class="hljs-string">&#x27;员工ID&#x27;</span>,<br>    name <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">20</span>) <span class="hljs-keyword">NOT NULL</span> COMMENT <span class="hljs-string">&#x27;姓名&#x27;</span>,<br>    gender <span class="hljs-type">CHAR</span>(<span class="hljs-number">1</span>) <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">&#x27;男&#x27;</span> COMMENT <span class="hljs-string">&#x27;性别&#x27;</span>,<br>    age <span class="hljs-type">INT</span> <span class="hljs-keyword">CHECK</span> (age <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span> <span class="hljs-keyword">AND</span> age <span class="hljs-operator">&lt;=</span> <span class="hljs-number">120</span>) COMMENT <span class="hljs-string">&#x27;年龄&#x27;</span>,<br>    job <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">20</span>) COMMENT <span class="hljs-string">&#x27;岗位&#x27;</span>,<br>    salary <span class="hljs-type">DECIMAL</span>(<span class="hljs-number">10</span>,<span class="hljs-number">2</span>) <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span> COMMENT <span class="hljs-string">&#x27;薪资&#x27;</span>,<br>    entrydate <span class="hljs-type">DATE</span> COMMENT <span class="hljs-string">&#x27;入职日期&#x27;</span>,<br>    managerid <span class="hljs-type">INT</span> COMMENT <span class="hljs-string">&#x27;直属领导ID&#x27;</span>,<br>    dept_id <span class="hljs-type">INT</span> COMMENT <span class="hljs-string">&#x27;部门ID&#x27;</span>,<br>    workno <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">UNIQUE</span> COMMENT <span class="hljs-string">&#x27;工号&#x27;</span>,<br>    <span class="hljs-keyword">CONSTRAINT</span> fk_emp_dept <span class="hljs-keyword">FOREIGN KEY</span> (dept_id) <span class="hljs-keyword">REFERENCES</span> dept(id)<br>) COMMENT <span class="hljs-string">&#x27;员工表&#x27;</span>;<br></code></pre></td></tr></table></figure><h3 id="10-4-插入数据"><a href="#10-4-插入数据" class="headerlink" title="10.4 插入数据"></a>10.4 插入数据</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT INTO</span> dept(name)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-string">&#x27;研发部&#x27;</span>), (<span class="hljs-string">&#x27;市场部&#x27;</span>), (<span class="hljs-string">&#x27;财务部&#x27;</span>), (<span class="hljs-string">&#x27;销售部&#x27;</span>);<br></code></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs sql"><span class="hljs-keyword">INSERT INTO</span> emp(name, gender, age, job, salary, entrydate, managerid, dept_id, workno)<br><span class="hljs-keyword">VALUES</span><br>(<span class="hljs-string">&#x27;张三&#x27;</span>, <span class="hljs-string">&#x27;男&#x27;</span>, <span class="hljs-number">28</span>, <span class="hljs-string">&#x27;开发&#x27;</span>, <span class="hljs-number">9000</span>, <span class="hljs-string">&#x27;2021-03-15&#x27;</span>, <span class="hljs-keyword">NULL</span>, <span class="hljs-number">1</span>, <span class="hljs-string">&#x27;1&#x27;</span>),<br>(<span class="hljs-string">&#x27;李四&#x27;</span>, <span class="hljs-string">&#x27;男&#x27;</span>, <span class="hljs-number">32</span>, <span class="hljs-string">&#x27;开发&#x27;</span>, <span class="hljs-number">12000</span>, <span class="hljs-string">&#x27;2020-07-01&#x27;</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">&#x27;2&#x27;</span>),<br>(<span class="hljs-string">&#x27;王五&#x27;</span>, <span class="hljs-string">&#x27;女&#x27;</span>, <span class="hljs-number">26</span>, <span class="hljs-string">&#x27;测试&#x27;</span>, <span class="hljs-number">8000</span>, <span class="hljs-string">&#x27;2022-01-10&#x27;</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-string">&#x27;3&#x27;</span>),<br>(<span class="hljs-string">&#x27;赵六&#x27;</span>, <span class="hljs-string">&#x27;男&#x27;</span>, <span class="hljs-number">35</span>, <span class="hljs-string">&#x27;市场专员&#x27;</span>, <span class="hljs-number">7000</span>, <span class="hljs-string">&#x27;2019-11-20&#x27;</span>, <span class="hljs-keyword">NULL</span>, <span class="hljs-number">2</span>, <span class="hljs-string">&#x27;4&#x27;</span>),<br>(<span class="hljs-string">&#x27;钱七&#x27;</span>, <span class="hljs-string">&#x27;女&#x27;</span>, <span class="hljs-number">30</span>, <span class="hljs-string">&#x27;会计&#x27;</span>, <span class="hljs-number">8500</span>, <span class="hljs-string">&#x27;2018-05-08&#x27;</span>, <span class="hljs-keyword">NULL</span>, <span class="hljs-number">3</span>, <span class="hljs-string">&#x27;5&#x27;</span>),<br>(<span class="hljs-string">&#x27;孙八&#x27;</span>, <span class="hljs-string">&#x27;男&#x27;</span>, <span class="hljs-number">40</span>, <span class="hljs-string">&#x27;销售经理&#x27;</span>, <span class="hljs-number">15000</span>, <span class="hljs-string">&#x27;2017-09-12&#x27;</span>, <span class="hljs-keyword">NULL</span>, <span class="hljs-number">4</span>, <span class="hljs-string">&#x27;6&#x27;</span>);<br></code></pre></td></tr></table></figure><h3 id="10-5-修改数据"><a href="#10-5-修改数据" class="headerlink" title="10.5 修改数据"></a>10.5 修改数据</h3><p>统一把工号补齐为 5 位：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">UPDATE</span> emp<br><span class="hljs-keyword">SET</span> workno <span class="hljs-operator">=</span> LPAD(workno, <span class="hljs-number">5</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br></code></pre></td></tr></table></figure><p>给研发部员工涨薪 1000：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">UPDATE</span> emp<br><span class="hljs-keyword">SET</span> salary <span class="hljs-operator">=</span> salary <span class="hljs-operator">+</span> <span class="hljs-number">1000</span><br><span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> dept <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;研发部&#x27;</span>);<br></code></pre></td></tr></table></figure><h3 id="10-6-删除数据"><a href="#10-6-删除数据" class="headerlink" title="10.6 删除数据"></a>10.6 删除数据</h3><p>删除年龄小于 25 的员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> age <span class="hljs-operator">&lt;</span> <span class="hljs-number">25</span>;<br></code></pre></td></tr></table></figure><h3 id="10-7-基础查询"><a href="#10-7-基础查询" class="headerlink" title="10.7 基础查询"></a>10.7 基础查询</h3><h4 id="10-7-1-查询所有员工"><a href="#10-7-1-查询所有员工" class="headerlink" title="10.7.1 查询所有员工"></a>10.7.1 查询所有员工</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h4 id="10-7-2-查询指定字段并设置别名"><a href="#10-7-2-查询指定字段并设置别名" class="headerlink" title="10.7.2 查询指定字段并设置别名"></a>10.7.2 查询指定字段并设置别名</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> name <span class="hljs-keyword">AS</span> 姓名, job <span class="hljs-keyword">AS</span> 岗位, salary <span class="hljs-keyword">AS</span> 薪资<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h4 id="10-7-3-去重查询"><a href="#10-7-3-去重查询" class="headerlink" title="10.7.3 去重查询"></a>10.7.3 去重查询</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> job<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h3 id="10-8-条件查询"><a href="#10-8-条件查询" class="headerlink" title="10.8 条件查询"></a>10.8 条件查询</h3><h4 id="10-8-1-比较-范围-模糊-空值判断"><a href="#10-8-1-比较-范围-模糊-空值判断" class="headerlink" title="10.8.1 比较 &#x2F; 范围 &#x2F; 模糊 &#x2F; 空值判断"></a>10.8.1 比较 &#x2F; 范围 &#x2F; 模糊 &#x2F; 空值判断</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> salary <span class="hljs-operator">&gt;=</span> <span class="hljs-number">9000</span><br>  <span class="hljs-keyword">AND</span> age <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">25</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">35</span><br>  <span class="hljs-keyword">AND</span> name <span class="hljs-keyword">LIKE</span> <span class="hljs-string">&#x27;张%&#x27;</span><br>  <span class="hljs-keyword">AND</span> managerid <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span>;<br></code></pre></td></tr></table></figure><h4 id="10-8-2-IN-OR-NOT"><a href="#10-8-2-IN-OR-NOT" class="headerlink" title="10.8.2 IN &#x2F; OR &#x2F; NOT"></a>10.8.2 IN &#x2F; OR &#x2F; NOT</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-keyword">IN</span> (<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>)<br>   <span class="hljs-keyword">OR</span> <span class="hljs-keyword">NOT</span> gender <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;女&#x27;</span>;<br></code></pre></td></tr></table></figure><h3 id="10-9-聚合、分组、排序、分页"><a href="#10-9-聚合、分组、排序、分页" class="headerlink" title="10.9 聚合、分组、排序、分页"></a>10.9 聚合、分组、排序、分页</h3><h4 id="10-9-1-聚合函数"><a href="#10-9-1-聚合函数" class="headerlink" title="10.9.1 聚合函数"></a>10.9.1 聚合函数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">COUNT</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> 员工数,<br>       <span class="hljs-built_in">MAX</span>(salary) <span class="hljs-keyword">AS</span> 最高薪资,<br>       <span class="hljs-built_in">MIN</span>(salary) <span class="hljs-keyword">AS</span> 最低薪资,<br>       <span class="hljs-built_in">AVG</span>(salary) <span class="hljs-keyword">AS</span> 平均薪资,<br>       <span class="hljs-built_in">SUM</span>(salary) <span class="hljs-keyword">AS</span> 薪资总和<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h4 id="10-9-2-分组查询"><a href="#10-9-2-分组查询" class="headerlink" title="10.9.2 分组查询"></a>10.9.2 分组查询</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> dept_id, <span class="hljs-built_in">COUNT</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> 人数, <span class="hljs-built_in">AVG</span>(salary) <span class="hljs-keyword">AS</span> 平均薪资<br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> dept_id<br><span class="hljs-keyword">HAVING</span> <span class="hljs-built_in">COUNT</span>(<span class="hljs-operator">*</span>) <span class="hljs-operator">&gt;=</span> <span class="hljs-number">1</span>;<br></code></pre></td></tr></table></figure><h4 id="10-9-3-排序-分页"><a href="#10-9-3-排序-分页" class="headerlink" title="10.9.3 排序 + 分页"></a>10.9.3 排序 + 分页</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> name, salary<br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> salary <span class="hljs-keyword">DESC</span>, age <span class="hljs-keyword">ASC</span><br>LIMIT <span class="hljs-number">0</span>, <span class="hljs-number">3</span>;<br></code></pre></td></tr></table></figure><h3 id="10-10-常用函数"><a href="#10-10-常用函数" class="headerlink" title="10.10 常用函数"></a>10.10 常用函数</h3><h4 id="10-10-1-字符串函数"><a href="#10-10-1-字符串函数" class="headerlink" title="10.10.1 字符串函数"></a>10.10.1 字符串函数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> CONCAT(name, <span class="hljs-string">&#x27;-&#x27;</span>, job) <span class="hljs-keyword">AS</span> 员工信息,<br>       <span class="hljs-built_in">UPPER</span>(job) <span class="hljs-keyword">AS</span> 大写岗位,<br>       <span class="hljs-built_in">SUBSTRING</span>(name, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>) <span class="hljs-keyword">AS</span> 姓<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h4 id="10-10-2-日期函数"><a href="#10-10-2-日期函数" class="headerlink" title="10.10.2 日期函数"></a>10.10.2 日期函数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> name,<br>       CURDATE() <span class="hljs-keyword">AS</span> 当前日期,<br>       DATEDIFF(CURDATE(), entrydate) <span class="hljs-keyword">AS</span> 入职天数<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h4 id="10-10-3-流程函数"><a href="#10-10-3-流程函数" class="headerlink" title="10.10.3 流程函数"></a>10.10.3 流程函数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> name,<br>       IFNULL(job, <span class="hljs-string">&#x27;未分配岗位&#x27;</span>) <span class="hljs-keyword">AS</span> 岗位,<br>       <span class="hljs-keyword">CASE</span><br>           <span class="hljs-keyword">WHEN</span> salary <span class="hljs-operator">&gt;=</span> <span class="hljs-number">12000</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;高&#x27;</span><br>           <span class="hljs-keyword">WHEN</span> salary <span class="hljs-operator">&gt;=</span> <span class="hljs-number">8000</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">&#x27;中&#x27;</span><br>           <span class="hljs-keyword">ELSE</span> <span class="hljs-string">&#x27;低&#x27;</span><br>       <span class="hljs-keyword">END</span> <span class="hljs-keyword">AS</span> 薪资等级<br><span class="hljs-keyword">FROM</span> emp;<br></code></pre></td></tr></table></figure><h3 id="10-11-多表查询"><a href="#10-11-多表查询" class="headerlink" title="10.11 多表查询"></a>10.11 多表查询</h3><h4 id="10-11-1-内连接"><a href="#10-11-1-内连接" class="headerlink" title="10.11.1 内连接"></a>10.11.1 内连接</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> e.name <span class="hljs-keyword">AS</span> 员工姓名, d.name <span class="hljs-keyword">AS</span> 部门名称<br><span class="hljs-keyword">FROM</span> emp e<br><span class="hljs-keyword">INNER</span> <span class="hljs-keyword">JOIN</span> dept d <span class="hljs-keyword">ON</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><h4 id="10-11-2-左外连接"><a href="#10-11-2-左外连接" class="headerlink" title="10.11.2 左外连接"></a>10.11.2 左外连接</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> e.name <span class="hljs-keyword">AS</span> 员工姓名, d.name <span class="hljs-keyword">AS</span> 部门名称<br><span class="hljs-keyword">FROM</span> emp e<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> dept d <span class="hljs-keyword">ON</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><h4 id="10-11-3-自连接"><a href="#10-11-3-自连接" class="headerlink" title="10.11.3 自连接"></a>10.11.3 自连接</h4><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> e.name <span class="hljs-keyword">AS</span> 员工, m.name <span class="hljs-keyword">AS</span> 领导<br><span class="hljs-keyword">FROM</span> emp e<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> emp m <span class="hljs-keyword">ON</span> e.managerid <span class="hljs-operator">=</span> m.id;<br></code></pre></td></tr></table></figure><h4 id="10-11-4-联合查询"><a href="#10-11-4-联合查询" class="headerlink" title="10.11.4 联合查询"></a>10.11.4 联合查询</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> name, job, salary<br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> salary <span class="hljs-operator">&lt;</span> <span class="hljs-number">9000</span><br><span class="hljs-keyword">UNION</span><br><span class="hljs-keyword">SELECT</span> name, job, salary<br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> age <span class="hljs-operator">&gt;</span> <span class="hljs-number">35</span>;<br></code></pre></td></tr></table></figure><h3 id="10-12-子查询"><a href="#10-12-子查询" class="headerlink" title="10.12 子查询"></a>10.12 子查询</h3><h4 id="10-12-1-标量子查询"><a href="#10-12-1-标量子查询" class="headerlink" title="10.12.1 标量子查询"></a>10.12.1 标量子查询</h4><p>查询研发部员工：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> dept <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;研发部&#x27;</span>);<br></code></pre></td></tr></table></figure><h4 id="10-12-2-列子查询"><a href="#10-12-2-列子查询" class="headerlink" title="10.12.2 列子查询"></a>10.12.2 列子查询</h4><p>查询市场部和销售部员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> dept_id <span class="hljs-keyword">IN</span> (<br>    <span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> dept <span class="hljs-keyword">WHERE</span> name <span class="hljs-keyword">IN</span> (<span class="hljs-string">&#x27;市场部&#x27;</span>, <span class="hljs-string">&#x27;销售部&#x27;</span>)<br>);<br></code></pre></td></tr></table></figure><h4 id="10-12-3-行子查询"><a href="#10-12-3-行子查询" class="headerlink" title="10.12.3 行子查询"></a>10.12.3 行子查询</h4><p>查询与“李四”薪资和直属领导都相同的员工：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> (salary, managerid) <span class="hljs-operator">=</span> (<br>    <span class="hljs-keyword">SELECT</span> salary, managerid<br>    <span class="hljs-keyword">FROM</span> emp<br>    <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;李四&#x27;</span><br>);<br></code></pre></td></tr></table></figure><h4 id="10-12-4-表子查询"><a href="#10-12-4-表子查询" class="headerlink" title="10.12.4 表子查询"></a>10.12.4 表子查询</h4><p>查询 2020 年以后入职员工及其部门信息：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> t.<span class="hljs-operator">*</span>, d.name <span class="hljs-keyword">AS</span> 部门名称<br><span class="hljs-keyword">FROM</span> (<br>    <span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br>    <span class="hljs-keyword">FROM</span> emp<br>    <span class="hljs-keyword">WHERE</span> entrydate <span class="hljs-operator">&gt;=</span> <span class="hljs-string">&#x27;2020-01-01&#x27;</span><br>) t<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> dept d <span class="hljs-keyword">ON</span> t.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><h3 id="10-13-事务"><a href="#10-13-事务" class="headerlink" title="10.13 事务"></a>10.13 事务</h3><p>模拟转账：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs sql"><span class="hljs-keyword">START</span> TRANSACTION;<br><br><span class="hljs-keyword">UPDATE</span> emp <span class="hljs-keyword">SET</span> salary <span class="hljs-operator">=</span> salary <span class="hljs-operator">-</span> <span class="hljs-number">500</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;李四&#x27;</span>;<br><span class="hljs-keyword">UPDATE</span> emp <span class="hljs-keyword">SET</span> salary <span class="hljs-operator">=</span> salary <span class="hljs-operator">+</span> <span class="hljs-number">500</span> <span class="hljs-keyword">WHERE</span> name <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;张三&#x27;</span>;<br><br><span class="hljs-keyword">COMMIT</span>;<br><span class="hljs-comment">-- 如果中途报错，则执行 ROLLBACK;</span><br></code></pre></td></tr></table></figure><h3 id="10-14-执行顺序理解"><a href="#10-14-执行顺序理解" class="headerlink" title="10.14 执行顺序理解"></a>10.14 执行顺序理解</h3><p>上面这类复杂查询的执行顺序通常是：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">FROM</span> <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">WHERE</span> <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">HAVING</span> <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">SELECT</span> <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> LIMIT<br></code></pre></td></tr></table></figure><h2 id="11-正则表达式"><a href="#11-正则表达式" class="headerlink" title="11 正则表达式"></a>11 正则表达式</h2><h3 id="11-1-概述"><a href="#11-1-概述" class="headerlink" title="11.1 概述"></a>11.1 概述</h3><p>正则表达式（Regular Expression）是一种强大的文本模式匹配工具，在 SQL 中常用于复杂字符串的查找、替换和提取操作。MySQL 提供了对正则表达式的内置支持。</p><h3 id="11-2-常用元字符"><a href="#11-2-常用元字符" class="headerlink" title="11.2 常用元字符"></a>11.2 常用元字符</h3><table><thead><tr><th>元字符</th><th>描述</th><th>示例</th></tr></thead><tbody><tr><td><code>^</code></td><td>匹配字符串的开始</td><td><code>^a</code> 匹配以 a 开头的字符串</td></tr><tr><td><code>$</code></td><td>匹配字符串的结束</td><td><code>a$</code> 匹配以 a 结尾的字符串</td></tr><tr><td><code>.</code></td><td>匹配任意单个字符（除换行符外）</td><td><code>a.c</code> 匹配 abc、a1c 等</td></tr><tr><td><code>*</code></td><td>匹配前一个字符零次或多次</td><td><code>a*b</code> 匹配 b、ab、aab 等</td></tr><tr><td><code>+</code></td><td>匹配前一个字符一次或多次</td><td><code>a+b</code> 匹配 ab、aab 等，不匹配 b</td></tr><tr><td><code>?</code></td><td>匹配前一个字符零次或一次</td><td><code>a?b</code> 匹配 b、ab</td></tr><tr><td><code>[]</code></td><td>匹配括号内的任意一个字符</td><td><code>[abc]</code> 匹配 a、b 或 c</td></tr><tr><td><code>[^]</code></td><td>匹配不在括号内的任意字符</td><td><code>[^abc]</code> 匹配除 a、b、c 外的任意字符</td></tr><tr><td><code>|</code></td><td>匹配两个或多个分支选择（或）</td><td><code>a|b</code> 匹配 a 或 b</td></tr><tr><td><code>{n}</code></td><td>匹配前一个字符恰好 n 次</td><td><code>a{3}</code> 匹配 aaa</td></tr><tr><td><code>{n,}</code></td><td>匹配前一个字符至少 n 次</td><td><code>a{2,}</code> 匹配 aa、aaa 等</td></tr><tr><td><code>{n,m}</code></td><td>匹配前一个字符至少 n 次，最多 m 次</td><td><code>a{1,3}</code> 匹配 a、aa、aaa</td></tr></tbody></table><h3 id="11-3-常用正则函数"><a href="#11-3-常用正则函数" class="headerlink" title="11.3 常用正则函数"></a>11.3 常用正则函数</h3><p>MySQL 8.0 引入了多个强大的正则表达式函数（旧版本主要使用 <code>REGEXP</code> &#x2F; <code>RLIKE</code> 运算符）。</p><table><thead><tr><th>函数 &#x2F; 运算符</th><th>功能说明</th></tr></thead><tbody><tr><td><code>expr REGEXP pat</code> &#x2F; <code>expr RLIKE pat</code></td><td>模式匹配，如果 <code>expr</code> 匹配 <code>pat</code> 则返回 1，否则返回 0</td></tr><tr><td><code>REGEXP_LIKE(expr, pat[, match_type])</code></td><td>同 <code>REGEXP</code>，但可以指定匹配参数（如区分大小写）</td></tr><tr><td><code>REGEXP_INSTR(expr, pat[, pos[, occurrence[, return_option[, match_type]]]])</code></td><td>返回匹配模式的子串的起始索引（找不到返回 0）</td></tr><tr><td><code>REGEXP_REPLACE(expr, pat, repl[, pos[, occurrence[, match_type]]])</code></td><td>替换匹配模式的子串</td></tr><tr><td><code>REGEXP_SUBSTR(expr, pat[, pos[, occurrence[, match_type]]])</code></td><td>返回匹配模式的子串</td></tr></tbody></table><h3 id="11-4-示例操作"><a href="#11-4-示例操作" class="headerlink" title="11.4 示例操作"></a>11.4 示例操作</h3><h4 id="11-4-1-基础匹配-REGEXP-RLIKE"><a href="#11-4-1-基础匹配-REGEXP-RLIKE" class="headerlink" title="11.4.1 基础匹配 (REGEXP &#x2F; RLIKE)"></a>11.4.1 基础匹配 (<code>REGEXP</code> &#x2F; <code>RLIKE</code>)</h4><p>查询姓名以“张”开头或以“三”结尾的员工：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> name REGEXP <span class="hljs-string">&#x27;^张|三$&#x27;</span>;<br></code></pre></td></tr></table></figure><p>查询工号包含连续两个数字的员工：</p><figure class="highlight sql"><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><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> emp<br><span class="hljs-keyword">WHERE</span> workno REGEXP <span class="hljs-string">&#x27;[0-9]&#123;2&#125;&#x27;</span>;<br></code></pre></td></tr></table></figure><h4 id="11-4-2-提取子串-REGEXP-SUBSTR"><a href="#11-4-2-提取子串-REGEXP-SUBSTR" class="headerlink" title="11.4.2 提取子串 (REGEXP_SUBSTR)"></a>11.4.2 提取子串 (<code>REGEXP_SUBSTR</code>)</h4><p>从邮箱中提取域名部分：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> email, REGEXP_SUBSTR(email, <span class="hljs-string">&#x27;@.*$&#x27;</span>) <span class="hljs-keyword">AS</span> domain<br><span class="hljs-keyword">FROM</span> users;<br></code></pre></td></tr></table></figure><h4 id="11-4-3-替换子串-REGEXP-REPLACE"><a href="#11-4-3-替换子串-REGEXP-REPLACE" class="headerlink" title="11.4.3 替换子串 (REGEXP_REPLACE)"></a>11.4.3 替换子串 (<code>REGEXP_REPLACE</code>)</h4><p>将手机号中间四位替换为星号：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> phone, REGEXP_REPLACE(phone, <span class="hljs-string">&#x27;([0-9]&#123;3&#125;)[0-9]&#123;4&#125;([0-9]&#123;4&#125;)&#x27;</span>, <span class="hljs-string">&#x27;$1****$2&#x27;</span>) <span class="hljs-keyword">AS</span> masked_phone<br><span class="hljs-keyword">FROM</span> users;<br></code></pre></td></tr></table></figure><h4 id="11-4-4-查找位置-REGEXP-INSTR"><a href="#11-4-4-查找位置-REGEXP-INSTR" class="headerlink" title="11.4.4 查找位置 (REGEXP_INSTR)"></a>11.4.4 查找位置 (<code>REGEXP_INSTR</code>)</h4><p>查找第一个非数字字符出现的位置：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> text_col, REGEXP_INSTR(text_col, <span class="hljs-string">&#x27;[^0-9]&#x27;</span>) <span class="hljs-keyword">AS</span> first_non_digit_pos<br><span class="hljs-keyword">FROM</span> table_name;<br></code></pre></td></tr></table></figure><h3 id="11-5-注意事项"><a href="#11-5-注意事项" class="headerlink" title="11.5 注意事项"></a>11.5 注意事项</h3><ul><li>MySQL 正则表达式默认不区分大小写，若要区分大小写，可以使用 <code>BINARY</code> 关键字（如 <code>REGEXP BINARY &#39;a&#39;</code>）或在 <code>match_type</code> 参数中指定 <code>&#39;c&#39;</code>。</li><li>正则表达式查询通常会导致全表扫描，无法使用索引，在数据量大的表上使用需谨慎。</li><li>在匹配具有特殊含义的元字符（如 <code>.</code>、<code>*</code>、<code>[</code> 等）本身时，需要使用转义符（MySQL 中通常使用 <code>\\</code>，如 <code>\\.</code>）。</li></ul><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1775055741133" data-twikoo-path="article_1775055741133"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/31/sql/</id>
    <link href="https://aoiblog.top/2026/03/31/sql/"/>
    <published>2026-03-31T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>SQL</h2>
# SQL学习笔记

<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li>1 SQL基础<ul>
<li>1.1 SQL通用语法</li>
<li>1.2]]>
    </summary>
    <title>SQL</title>
    <updated>2026-04-08T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="随笔" scheme="https://aoiblog.top/categories/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<h2>博客更新声明</h2>2026年3月27日12:31:50<p>接下来的两周可能会有点忙，做不到日更博客，只能尽力抽时间来更新了</p><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1774585936462" data-twikoo-path="article_1774585936462"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/26/bo-ke-geng-xin-sheng-ming/</id>
    <link href="https://aoiblog.top/2026/03/26/bo-ke-geng-xin-sheng-ming/"/>
    <published>2026-03-26T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>博客更新声明</h2>
2026年3月27日12:31:50

<p>接下来的两周可能会有点忙，做不到日更博客，只能尽力抽时间来更新了</p>
<section class="legacy-comments">
  <h2>评论区</h2>
  <div id="twik]]>
    </summary>
    <title>博客更新声明</title>
    <updated>2026-03-26T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="随笔" scheme="https://aoiblog.top/categories/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<h2>b站视频收集录</h2><h3>1. PPT正在被HTML淘汰 | 我开始用html做视频素材</h3><p>视频链接：<a href="https://www.bilibili.com/video/BV1jCAGzpE7i" target="_blank" style="color: #0366d6; text-decoration: underline;">https://www.bilibili.com/video/BV1jCAGzpE7i</a></p><p><img src="http://i0.hdslb.com/bfs/archive/0596aa39da63197af2910d399369d92b5b3962c3.jpg" alt="视频封面" style="max-width: 100%; border-radius: 8px; margin: 10px 0; box-shadow: 0 4px 6px rgba(0,0,0,0.1);"></p><p>视频内容：本视频探讨了在制作视频素材和演示文稿时，使用 HTML 替代传统 PPT 的新趋势。UP主（卷卷姐juan）分享了将网页前端技术应用于视频制作的实际案例，对比了 HTML 与 PPT 的表现力差异。视频还展示了如何结合 AI 工具（如小米的 Mimo 平台）及自动化技能（如前端设计、PPT代码转换等），大幅提升演示内容的交互性、动画效果和生成效率。这为现代内容创作者提供了一条从传统静态幻灯片向网页交互式素材转型的新思路与工作流。</p><br><hr><br><h3>2. 和硅谷码农聊养龙虾：当生产力不再是问题，人该怎么活？｜《此刻有声》视频播客003对话鸿泽</h3><p>视频链接：<a href="https://www.bilibili.com/video/BV1CdA7zUEBy" target="_blank" style="color: #0366d6; text-decoration: underline;">https://www.bilibili.com/video/BV1CdA7zUEBy</a></p><p><img src="http://i2.hdslb.com/bfs/archive/5c6b3a612f8460b02a528183f02b3d70db5c3ea6.jpg" alt="视频封面" style="max-width: 100%; border-radius: 8px; margin: 10px 0; box-shadow: 0 4px 6px rgba(0,0,0,0.1);"></p><p>视频内容：这是一期关于在AI时代如何反焦虑的深度访谈播客。UP主C.T.邀请了在硅谷扎根9年、35岁主动裁掉大厂光环的硬核码农柏杨。节目探讨了当AI将生产力推向极致时，普通人应如何自处：认清时间才是唯一真实的资源，通过动手创造来对抗算法带来的被动焦虑，将AI视为员工而非竞争对手，并最终回归到探寻自我本质的问题上当生产力不再是束缚，你到底是谁？。</p><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1774409965259" data-twikoo-path="article_1774409965259"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/24/b-zhan-shi-pin-shou-ji-lu/</id>
    <link href="https://aoiblog.top/2026/03/24/b-zhan-shi-pin-shou-ji-lu/"/>
    <published>2026-03-24T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>b站视频收集录</h2>
<h3>1. PPT正在被HTML淘汰 | 我开始用html做视频素材</h3>
<p>视频链接：<a href="https://www.bilibili.com/video/BV1jCAGzpE7i" target="_blank" styl]]>
    </summary>
    <title>b站视频收集录</title>
    <updated>2026-03-24T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="随笔" scheme="https://aoiblog.top/categories/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<h2>和硅谷码农聊养龙虾：当生产力不再是问题，人该怎么活？</h2><p>视频链接：<a href="https://www.bilibili.com/video/BV1CdA7zUEBy" target="_blank" style="color: #0366d6; text-decoration: underline;">https://www.bilibili.com/video/BV1CdA7zUEBy</a></p><p><img src="http://i2.hdslb.com/bfs/archive/5c6b3a612f8460b02a528183f02b3d70db5c3ea6.jpg" alt="视频封面" style="max-width: 100%; border-radius: 8px; margin: 10px 0; box-shadow: 0 4px 6px rgba(0,0,0,0.1);"></p><p>视频内容：这是一期关于在AI时代如何反焦虑的深度访谈播客。UP主C.T.邀请了在硅谷扎根9年、35岁主动裁掉大厂光环的硬核码农柏杨。节目探讨了当AI将生产力推向极致时，普通人应如何自处：认清时间才是唯一真实的资源，通过动手创造来对抗算法带来的被动焦虑，将AI视为员工而非竞争对手，并最终回归到探寻自我本质的问题上当生产力不再是束缚，你到底是谁？。</p><p>2026年3月26日00:08:55<br>昨天看了这条视频，十分有感触，故把这篇文章从收集录中单独拎出来，之后会在这下面单独聊聊我对这个视频里一些观点的看法</p><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1774454571938" data-twikoo-path="article_1774454571938"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/24/he-gui-gu-ma-nong-liao-yang-long-xia-dang-sheng-chan-li-bu-zai-shi-wen-ti-ren-gai-zen-me-huo/</id>
    <link href="https://aoiblog.top/2026/03/24/he-gui-gu-ma-nong-liao-yang-long-xia-dang-sheng-chan-li-bu-zai-shi-wen-ti-ren-gai-zen-me-huo/"/>
    <published>2026-03-24T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>和硅谷码农聊养龙虾：当生产力不再是问题，人该怎么活？</h2>
<p>视频链接：<a href="https://www.bilibili.com/video/BV1CdA7zUEBy" target="_blank" style="color: #0366d6; tex]]>
    </summary>
    <title>和硅谷码农聊养龙虾：当生产力不再是问题，人该怎么活？</title>
    <updated>2026-03-24T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="技术学习" scheme="https://aoiblog.top/categories/%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2>Javascript</h2># Javascript<p>2026年3月25日23:55:50<br>发这篇文章的时候发现让哈基米修博客的时候越修越错，前前后后在MongoDB和vercel里掰扯了半个小时，又跟哈基米掰扯了半个多小时，真的心累……<br>不知道是不是哈基米能力太差了，但我现在也没有很多米，也不想订claude和gpt来用他们的api<br>将就着用吧……<br>事实证明用ai的人还是一定要懂技术和会写代码，不然就是瞎用</p><p><strong>1.字符串</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">//一、字符串的创建方式</span><br><span class="hljs-comment">// 字面量方式（推荐）</span><br><span class="hljs-keyword">let</span> str1 = <span class="hljs-string">&quot;Hello&quot;</span>;<br><span class="hljs-keyword">let</span> str2 = <span class="hljs-string">&#x27;World&#x27;</span>;<br><span class="hljs-keyword">let</span> str3 = <span class="hljs-string">`Template`</span>; <span class="hljs-comment">// 模板字符串（ES6）</span><br><br><span class="hljs-comment">// 二、字符串的基本属性与访问</span><br><span class="hljs-comment">// 1. .length：获取字符串长度</span><br><span class="hljs-keyword">let</span> text = <span class="hljs-string">&quot;JavaScript&quot;</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(text.<span class="hljs-property">length</span>); <span class="hljs-comment">// 10</span><br><br><span class="hljs-comment">// 2. 通过索引访问字符（只读）</span><br><span class="hljs-keyword">let</span> s = <span class="hljs-string">&quot;ABC&quot;</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(s[<span class="hljs-number">0</span>]);      <span class="hljs-comment">// &quot;A&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(s.<span class="hljs-title function_">charAt</span>(<span class="hljs-number">1</span>)); <span class="hljs-comment">// &quot;B&quot;（等价于 s[1]）</span><br><br><span class="hljs-comment">// 注意：字符串是不可变的！</span><br>s[<span class="hljs-number">0</span>] = <span class="hljs-string">&quot;X&quot;</span>; <span class="hljs-comment">// ❌ 无效，不会报错但也不会改变原字符串</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(s); <span class="hljs-comment">// 仍是 &quot;ABC&quot;</span><br><br><span class="hljs-comment">// 三、常用字符串操作方法（重点！）</span><br><span class="hljs-comment">// ✅ 1. 查找类方法</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// |------|------|</span><br><span class="hljs-comment">// | `.indexOf(search, fromIndex)` | 返回首次出现位置，未找到返回 `-1` |</span><br><span class="hljs-comment">// | `.lastIndexOf(search)` | 从后往前查找 |</span><br><span class="hljs-comment">// | `.includes(search)` | 是否包含（返回布尔值，ES6） |</span><br><span class="hljs-comment">// | `.startsWith(prefix)` | 是否以某字符串开头（ES6） |</span><br><span class="hljs-comment">// | `.endsWith(suffix)` | 是否以某字符串结尾（ES6） |</span><br><br><span class="hljs-keyword">let</span> sentence = <span class="hljs-string">&quot;The quick brown fox jumps over the lazy dog&quot;</span>;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(sentence.<span class="hljs-title function_">indexOf</span>(<span class="hljs-string">&quot;fox&quot;</span>));        <span class="hljs-comment">// 16</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(sentence.<span class="hljs-title function_">indexOf</span>(<span class="hljs-string">&quot;cat&quot;</span>));        <span class="hljs-comment">// -1</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(sentence.<span class="hljs-title function_">includes</span>(<span class="hljs-string">&quot;brown&quot;</span>));     <span class="hljs-comment">// true</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(sentence.<span class="hljs-title function_">startsWith</span>(<span class="hljs-string">&quot;The&quot;</span>));     <span class="hljs-comment">// true</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(sentence.<span class="hljs-title function_">endsWith</span>(<span class="hljs-string">&quot;dog&quot;</span>));       <span class="hljs-comment">// true</span><br><br><span class="hljs-comment">// ✅ 2. 截取/提取类方法</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// |------|------|</span><br><span class="hljs-comment">// | `.slice(start, end)` | 提取子串（不包含 end），支持负数 |</span><br><span class="hljs-comment">// | `.substring(start, end)` | 类似 slice，但不支持负数，且自动交换参数大小 |</span><br><span class="hljs-comment">// | `.substr(start, length)` | 已废弃！用 `slice` 替代 |</span><br><br><span class="hljs-keyword">let</span> str = <span class="hljs-string">&quot;Hello World&quot;</span>;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(str.<span class="hljs-title function_">slice</span>(<span class="hljs-number">0</span>, <span class="hljs-number">5</span>));     <span class="hljs-comment">// &quot;Hello&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(str.<span class="hljs-title function_">slice</span>(-<span class="hljs-number">5</span>));       <span class="hljs-comment">// &quot;World&quot;（从倒数第5位到末尾）</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(str.<span class="hljs-title function_">substring</span>(<span class="hljs-number">6</span>, <span class="hljs-number">11</span>)); <span class="hljs-comment">// &quot;World&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(str.<span class="hljs-title function_">substring</span>(<span class="hljs-number">11</span>, <span class="hljs-number">6</span>)); <span class="hljs-comment">// &quot;World&quot;（自动调换顺序）</span><br><br><span class="hljs-comment">// 推荐始终使用 slice！</span><br><br><span class="hljs-comment">// ✅ 3. 替换类方法</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// |------|------|</span><br><span class="hljs-comment">// | `.replace(search, replaceValue)` | 只替换第一个匹配项 |</span><br><span class="hljs-comment">// | `.replaceAll(search, replaceValue)` | 替换所有匹配项（ES2021） |</span><br><span class="hljs-comment">// | 支持正则表达式（强大！） |</span><br><br><span class="hljs-keyword">let</span> text = <span class="hljs-string">&quot;apple, banana, apple&quot;</span>;<br><br><span class="hljs-comment">// 替换第一个</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(text.<span class="hljs-title function_">replace</span>(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-string">&quot;orange&quot;</span>));<br><span class="hljs-comment">// → &quot;orange, banana, apple&quot;</span><br><br><span class="hljs-comment">// 替换全部（方法1：用 replaceAll）</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(text.<span class="hljs-title function_">replaceAll</span>(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-string">&quot;orange&quot;</span>));<br><span class="hljs-comment">// → &quot;orange, banana, orange&quot;</span><br><br><span class="hljs-comment">// 替换全部（方法2：用正则 + g 标志）</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(text.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/apple/g</span>, <span class="hljs-string">&quot;orange&quot;</span>));<br><span class="hljs-comment">// → &quot;orange, banana, orange&quot;</span><br><br><span class="hljs-comment">// 忽略大小写替换</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;Hello HELLO&quot;</span>.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/hello/gi</span>, <span class="hljs-string">&quot;Hi&quot;</span>));<br><span class="hljs-comment">// → &quot;Hi Hi&quot;</span><br><br><span class="hljs-comment">// ✅ 4. 大小写转换</span><br><span class="hljs-keyword">let</span> str = <span class="hljs-string">&quot;JavaScript&quot;</span>;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(str.<span class="hljs-title function_">toUpperCase</span>()); <span class="hljs-comment">// &quot;JAVASCRIPT&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(str.<span class="hljs-title function_">toLowerCase</span>()); <span class="hljs-comment">// &quot;javascript&quot;</span><br><br><span class="hljs-comment">// ✅ 5. 去除空白字符</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// |------|------|</span><br><span class="hljs-comment">// | `.trim()` | 去除首尾空白 |</span><br><span class="hljs-comment">// | `.trimStart()` / `.trimLeft()` | 去除开头空白（ES2019） |</span><br><span class="hljs-comment">// | `.trimEnd()` / `.trimRight()` | 去除结尾空白（ES2019） |</span><br><span class="hljs-keyword">let</span> dirty = <span class="hljs-string">&quot;   \t  Hello World!  \n  &quot;</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;[&quot;</span> + dirty.<span class="hljs-title function_">trim</span>() + <span class="hljs-string">&quot;]&quot;</span>);      <span class="hljs-comment">// &quot;[Hello World!]&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;[&quot;</span> + dirty.<span class="hljs-title function_">trimStart</span>() + <span class="hljs-string">&quot;]&quot;</span>); <span class="hljs-comment">// &quot;[Hello World!  \n  ]&quot;</span><br><br><span class="hljs-comment">// ✅ 6. 分割与连接</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// |------|------|</span><br><span class="hljs-comment">// | `.split(separator)` | 按分隔符拆分为数组 |</span><br><span class="hljs-comment">// | `.join(separator)` | 数组 → 字符串（Array 的方法） |</span><br><br><span class="hljs-keyword">let</span> csv = <span class="hljs-string">&quot;apple,banana,orange&quot;</span>;<br><span class="hljs-keyword">let</span> fruits = csv.<span class="hljs-title function_">split</span>(<span class="hljs-string">&quot;,&quot;</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(fruits); <span class="hljs-comment">// [&quot;apple&quot;, &quot;banana&quot;, &quot;orange&quot;]</span><br><br><span class="hljs-keyword">let</span> backToString = fruits.<span class="hljs-title function_">join</span>(<span class="hljs-string">&quot; | &quot;</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(backToString); <span class="hljs-comment">// &quot;apple | banana | orange&quot;</span><br><br><span class="hljs-comment">// 拆成单个字符</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;ABC&quot;</span>.<span class="hljs-title function_">split</span>(<span class="hljs-string">&quot;&quot;</span>)); <span class="hljs-comment">// [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;]</span><br><br><span class="hljs-comment">// ✅ 7. 填充与重复（ES2015+）</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// |------|------|</span><br><span class="hljs-comment">// | `.padStart(targetLength, padString)` | 从开头填充 |</span><br><span class="hljs-comment">// | `.padEnd(targetLength, padString)` | 从末尾填充 |</span><br><span class="hljs-comment">// | `.repeat(count)` | 重复字符串 |</span><br><br><span class="hljs-comment">// 数字补零（常用于时间、序号格式化）</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;5&quot;</span>.<span class="hljs-title function_">padStart</span>(<span class="hljs-number">3</span>, <span class="hljs-string">&quot;0&quot;</span>));   <span class="hljs-comment">// &quot;005&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;12&quot;</span>.<span class="hljs-title function_">padStart</span>(<span class="hljs-number">5</span>, <span class="hljs-string">&quot;*&quot;</span>));  <span class="hljs-comment">// &quot;***12&quot;</span><br><br><span class="hljs-comment">// 重复</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;Ha&quot;</span>.<span class="hljs-title function_">repeat</span>(<span class="hljs-number">3</span>)); <span class="hljs-comment">// &quot;HaHaHa&quot;</span><br><br><span class="hljs-comment">// 四、模板字面量（Template Literals）— ES6 重磅特性！</span><br><span class="hljs-comment">// 使用反引号  ... ，支持：</span><br><span class="hljs-comment">// 多行字符串</span><br><span class="hljs-comment">// 嵌入变量/表达式  $ &#123;&#125;</span><br><br><span class="hljs-keyword">let</span> name = <span class="hljs-string">&quot;小明&quot;</span>;<br><span class="hljs-keyword">let</span> age = <span class="hljs-number">20</span>;<br><br><span class="hljs-comment">// 传统拼接（繁琐易错）</span><br><span class="hljs-keyword">let</span> oldWay = <span class="hljs-string">&quot;我叫&quot;</span> + name + <span class="hljs-string">&quot;，今年&quot;</span> + age + <span class="hljs-string">&quot;岁。&quot;</span>;<br><br><span class="hljs-comment">// 模板字面量（简洁清晰）</span><br><span class="hljs-keyword">let</span> newWay = <span class="hljs-string">`我叫<span class="hljs-subst">$&#123;name&#125;</span>，今年<span class="hljs-subst">$&#123;age&#125;</span>岁。`</span>;<br><br><span class="hljs-comment">// 多行</span><br><span class="hljs-keyword">let</span> html = <span class="hljs-string">`</span><br><span class="hljs-string">  &lt;div&gt;</span><br><span class="hljs-string">    &lt;h1&gt;欢迎，<span class="hljs-subst">$&#123;name&#125;</span>！&lt;/h1&gt;</span><br><span class="hljs-string">    &lt;p&gt;年龄：<span class="hljs-subst">$&#123;age&#125;</span>&lt;/p&gt;</span><br><span class="hljs-string">  &lt;/div&gt;</span><br><span class="hljs-string">`</span>;<br><br><span class="hljs-comment">// 嵌入表达式</span><br><span class="hljs-keyword">let</span> price = <span class="hljs-number">100</span>;<br><span class="hljs-keyword">let</span> tax = <span class="hljs-number">0.1</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`总价：<span class="hljs-subst">$&#123;(price * (<span class="hljs-number">1</span> + tax)).toFixed(<span class="hljs-number">2</span>)&#125;</span> 元`</span>);<br><span class="hljs-comment">// → &quot;总价：110.00 元&quot;</span><br><br><span class="hljs-comment">// 五、字符串编码与转义</span><br><span class="hljs-comment">// 常见转义字符：</span><br><span class="hljs-comment">// \n：换行</span><br><span class="hljs-comment">// \t：制表符</span><br><span class="hljs-comment">// \&quot; 或 \&#x27;：引号</span><br><span class="hljs-comment">// \\：反斜杠</span><br></code></pre></td></tr></table></figure><p>2026年3月26日10:18:51<br><strong>2.数组</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 二、如何创建数组？</span><br><span class="hljs-comment">// ✅ 1. 数组字面量（推荐）</span><br><span class="hljs-keyword">let</span> fruits = [<span class="hljs-string">&quot;苹果&quot;</span>, <span class="hljs-string">&quot;香蕉&quot;</span>, <span class="hljs-string">&quot;橙子&quot;</span>];<br><span class="hljs-keyword">let</span> mixed = [<span class="hljs-number">1</span>, <span class="hljs-string">&quot;hello&quot;</span>, <span class="hljs-literal">true</span>, <span class="hljs-literal">null</span>, &#123; <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;JS&quot;</span> &#125;];<br><br><span class="hljs-comment">// 三、访问与修改数组元素</span><br><span class="hljs-keyword">let</span> colors = [<span class="hljs-string">&quot;红&quot;</span>, <span class="hljs-string">&quot;绿&quot;</span>, <span class="hljs-string">&quot;蓝&quot;</span>];<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(colors[<span class="hljs-number">0</span>]);     <span class="hljs-comment">// &quot;红&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(colors.<span class="hljs-property">length</span>); <span class="hljs-comment">// 3</span><br><br><span class="hljs-comment">// 修改元素</span><br>colors[<span class="hljs-number">1</span>] = <span class="hljs-string">&quot;黄&quot;</span>;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(colors); <span class="hljs-comment">// [&quot;红&quot;, &quot;黄&quot;, &quot;蓝&quot;]</span><br><br><span class="hljs-comment">// 添加新元素（不推荐直接用索引）</span><br>colors[<span class="hljs-number">3</span>] = <span class="hljs-string">&quot;紫&quot;</span>; <span class="hljs-comment">// 可以，但容易出错</span><br><br><span class="hljs-comment">//数组是可变的</span><br><br><span class="hljs-comment">// 四、判断是否为数组</span><br><span class="hljs-comment">// 不要用 typeof（它返回 &quot;object&quot;）！</span><br><span class="hljs-comment">// ✅ 正确方式：</span><br><span class="hljs-keyword">let</span> arr = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title class_">Array</span>.<span class="hljs-title function_">isArray</span>(arr)); <span class="hljs-comment">// true ✅</span><br><br><span class="hljs-comment">// 五、数组的常用方法（重点！）</span><br><span class="hljs-comment">// 📌 1. 栈方法（LIFO：后进先出）</span><br><span class="hljs-comment">// | 方法 | 作用 | 返回值 |</span><br><span class="hljs-comment">// | `.push(item)` | 末尾添加一个/多个元素 | 新长度 |</span><br><span class="hljs-comment">// | `.pop()` | 删除并返回最后一个元素 | 被删元素 |</span><br><span class="hljs-keyword">let</span> stack = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>];<br>stack.<span class="hljs-title function_">push</span>(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>);      <span class="hljs-comment">// [1, 2, 3, 4]</span><br><span class="hljs-keyword">let</span> last = stack.<span class="hljs-title function_">pop</span>(); <span class="hljs-comment">// last = 4, stack = [1, 2, 3]</span><br><br><span class="hljs-comment">// 📌 2. 队列方法（FIFO：先进先出）</span><br><span class="hljs-comment">// | 方法 | 作用 | 返回值 |</span><br><span class="hljs-comment">// | `.shift()` | 删除并返回第一个元素 | 被删元素 |</span><br><span class="hljs-comment">// | `.unshift(item)` | 开头添加一个/多个元素 | 新长度 |</span><br><span class="hljs-keyword">let</span> queue = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br>queue.<span class="hljs-title function_">unshift</span>(<span class="hljs-number">0</span>);       <span class="hljs-comment">// [0, 1, 2, 3]</span><br><span class="hljs-keyword">let</span> first = queue.<span class="hljs-title function_">shift</span>(); <span class="hljs-comment">// first = 0, queue = [1, 2, 3]</span><br><br><span class="hljs-comment">// 📌 3. 连接与转换</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// | `.join(separator)` | 将数组转为字符串，默认用 `,` 连接 |</span><br><span class="hljs-comment">// | `.toString()` | 等价于 `.join(&quot;,&quot;)` |</span><br><span class="hljs-comment">// | `.concat(arr2)` | 合并数组，不修改原数组，返回新数组 |</span><br><span class="hljs-keyword">let</span> letters = [<span class="hljs-string">&quot;a&quot;</span>, <span class="hljs-string">&quot;b&quot;</span>, <span class="hljs-string">&quot;c&quot;</span>];<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(letters.<span class="hljs-title function_">join</span>(<span class="hljs-string">&quot;-&quot;</span>)); <span class="hljs-comment">// &quot;a-b-c&quot;</span><br><br><span class="hljs-keyword">let</span> nums1 = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>];<br><span class="hljs-keyword">let</span> nums2 = [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>];<br><span class="hljs-keyword">let</span> combined = nums1.<span class="hljs-title function_">concat</span>(nums2); <span class="hljs-comment">// [1, 2, 3, 4]</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(nums1); <span class="hljs-comment">// [1, 2]（原数组不变）</span><br><br><span class="hljs-comment">// 📌 4. 截取与拼接</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// | `.slice(start, end)` | 提取子数组（不包含 end），不修改原数组 |</span><br><span class="hljs-comment">// | `.splice(start, deleteCount, ...items)` | 从 start 开始删除 deleteCount 个，并插入 items，会修改原数组 |</span><br><span class="hljs-keyword">let</span> arr = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];<br><br><span class="hljs-comment">// slice：安全提取</span><br><span class="hljs-keyword">let</span> part = arr.<span class="hljs-title function_">slice</span>(<span class="hljs-number">1</span>, <span class="hljs-number">3</span>); <span class="hljs-comment">// [2, 3]</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(arr); <span class="hljs-comment">// [1, 2, 3, 4, 5]（不变）</span><br><br><span class="hljs-comment">// splice：强力编辑</span><br>arr.<span class="hljs-title function_">splice</span>(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-string">&quot;x&quot;</span>, <span class="hljs-string">&quot;y&quot;</span>); <span class="hljs-comment">// 从索引2开始删2个，插入&quot;x&quot;,&quot;y&quot;</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(arr); <span class="hljs-comment">// [1, 2, &quot;x&quot;, &quot;y&quot;, 5]</span><br><br><span class="hljs-comment">// 📌 5. 查找与判断</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// | `.indexOf(item)` | 返回首次出现的索引，未找到返回 `-1` |</span><br><span class="hljs-comment">// | `.lastIndexOf(item)` | 从后往前找 |</span><br><span class="hljs-comment">// | `.includes(item)` | 是否包含（返回布尔值，ES2016） |</span><br><span class="hljs-comment">// | `.find(fn)` | 返回第一个满足条件的元素 |</span><br><span class="hljs-comment">// | `.findIndex(fn)` | 返回第一个满足条件的元素的索引 |</span><br><span class="hljs-keyword">let</span> scores = [<span class="hljs-number">85</span>, <span class="hljs-number">92</span>, <span class="hljs-number">78</span>, <span class="hljs-number">96</span>];<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(scores.<span class="hljs-title function_">includes</span>(<span class="hljs-number">92</span>)); <span class="hljs-comment">// true</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(scores.<span class="hljs-title function_">indexOf</span>(<span class="hljs-number">78</span>));  <span class="hljs-comment">// 2</span><br><br><span class="hljs-comment">// 找出第一个大于90的分数</span><br><span class="hljs-keyword">let</span> highScore = scores.<span class="hljs-title function_">find</span>(<span class="hljs-function"><span class="hljs-params">score</span> =&gt;</span> score &gt; <span class="hljs-number">90</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(highScore); <span class="hljs-comment">// 92</span><br><br><span class="hljs-comment">// 找出它的位置</span><br><span class="hljs-keyword">let</span> index = scores.<span class="hljs-title function_">findIndex</span>(<span class="hljs-function"><span class="hljs-params">score</span> =&gt;</span> score &gt; <span class="hljs-number">90</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(index); <span class="hljs-comment">// 1</span><br><br><span class="hljs-comment">// 📌 6. 遍历数组（不修改原数组）</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// | `.forEach(fn)` | 遍历每个元素（无返回值） |</span><br><span class="hljs-comment">// | `.map(fn)` | 对每个元素调用函数，返回新数组 |</span><br><span class="hljs-comment">// | `.filter(fn)` | 筛选出满足条件的元素，返回新数组 |</span><br><span class="hljs-comment">// | `.reduce(fn, init)` | 累积计算，常用于求和、扁平化等 |</span><br><span class="hljs-keyword">let</span> numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];<br><br><span class="hljs-comment">// 每个数乘2</span><br><span class="hljs-keyword">let</span> doubled = numbers.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">n</span> =&gt;</span> n * <span class="hljs-number">2</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(doubled); <span class="hljs-comment">// [2, 4, 6, 8, 10]</span><br><br><span class="hljs-comment">// 只保留偶数</span><br><span class="hljs-keyword">let</span> evens = numbers.<span class="hljs-title function_">filter</span>(<span class="hljs-function"><span class="hljs-params">n</span> =&gt;</span> n % <span class="hljs-number">2</span> === <span class="hljs-number">0</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(evens); <span class="hljs-comment">// [2, 4]</span><br><br><span class="hljs-keyword">let</span> sum = numbers.<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">total, current</span>) =&gt;</span> total + current, <span class="hljs-number">0</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(sum); <span class="hljs-comment">// 15</span><br><br><span class="hljs-comment">// 📌 7. 排序与反转</span><br><span class="hljs-comment">// | 方法 | 说明 |</span><br><span class="hljs-comment">// | `.reverse()` | 颠倒数组顺序（修改原数组） |</span><br><span class="hljs-comment">// | `.sort([compareFn])` | 排序（修改原数组） |</span><br><span class="hljs-comment">// ⚠️ 注意：.sort() 默认按字符串 Unicode 编码排序！</span><br><br><span class="hljs-keyword">let</span> nums = [<span class="hljs-number">10</span>, <span class="hljs-number">5</span>, <span class="hljs-number">40</span>, <span class="hljs-number">25</span>];<br><br><span class="hljs-comment">// 错误排序（按字符串排）</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(nums.<span class="hljs-title function_">sort</span>()); <span class="hljs-comment">// [10, 25, 40, 5] ❌</span><br><br><span class="hljs-comment">// 正确排序（传入比较函数）</span><br>nums.<span class="hljs-title function_">sort</span>(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a - b); <span class="hljs-comment">// 升序</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(nums); <span class="hljs-comment">// [5, 10, 25, 40] ✅</span><br><br><span class="hljs-comment">// 降序</span><br>nums.<span class="hljs-title function_">sort</span>(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> b - a);<br><br><span class="hljs-comment">// 七、实战小例子</span><br><span class="hljs-comment">// 1. 去重（使用 Set）</span><br><br><span class="hljs-keyword">let</span> dup = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>];<br><span class="hljs-keyword">let</span> unique = [...<span class="hljs-keyword">new</span> <span class="hljs-title class_">Set</span>(dup)];<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(unique); <span class="hljs-comment">// [1, 2, 3]</span><br></code></pre></td></tr></table></figure><p><strong>3.函数</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br></pre></td><td class="code"><pre><code class="hljs javascript">二、函数的定义方式<br>✅ <span class="hljs-number">1.</span> 函数声明（<span class="hljs-title class_">Function</span> <span class="hljs-title class_">Declaration</span>）<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">greet</span>(<span class="hljs-params">name</span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;你好，&quot;</span> + name + <span class="hljs-string">&quot;！&quot;</span>;<br>&#125;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">greet</span>(<span class="hljs-string">&quot;小明&quot;</span>)); <span class="hljs-comment">// &quot;你好，小明！&quot;</span><br><br>✅ 特点：函数提升（<span class="hljs-title class_">Hoisting</span>） —— 可以在声明前调用。<br><br><span class="hljs-title function_">sayHello</span>(); <span class="hljs-comment">// ✅ 正常执行（因为函数声明被提升了）</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">sayHello</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;Hello!&quot;</span>);<br>&#125;<br><br>✅ <span class="hljs-number">2.</span> 函数表达式（<span class="hljs-title class_">Function</span> <span class="hljs-title class_">Expression</span>）<br><br><span class="hljs-keyword">const</span> add = <span class="hljs-keyword">function</span>(<span class="hljs-params">a, b</span>) &#123;<br>  <span class="hljs-keyword">return</span> a + b;<br>&#125;;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">add</span>(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>)); <span class="hljs-comment">// 8</span><br><br>⚠️ 注意：不会被提升！不能在声明前调用<br><br><span class="hljs-comment">// subtract(); // ❌ ReferenceError</span><br><span class="hljs-keyword">const</span> subtract = <span class="hljs-keyword">function</span>(<span class="hljs-params">x, y</span>) &#123;<br>  <span class="hljs-keyword">return</span> x - y;<br>&#125;;<br><br>✅ <span class="hljs-number">3.</span> 箭头函数（<span class="hljs-title class_">Arrow</span> <span class="hljs-title class_">Function</span>，<span class="hljs-title class_">ES6</span>）<br>语法更简洁，没有自己的 <span class="hljs-variable language_">this</span>、<span class="hljs-variable language_">arguments</span>、<span class="hljs-variable language_">super</span> 或 <span class="hljs-keyword">new</span>.<span class="hljs-property">target</span><br><br><span class="hljs-number">1.</span> 基本形式<br>(参数<span class="hljs-number">1</span>, 参数<span class="hljs-number">2</span>, ...) =&gt; &#123; 函数体 &#125;<br>箭头表示返回，若函数体为表达式，则自动作为返回值<br><br><span class="hljs-number">2.</span> 简化规则<br>| 情况 | 写法 | 示例 |<br>| 单个参数 | 可省略括号 | <span class="hljs-string">`x =&gt; x * 2`</span> |<br>| 多个参数 | 必须加括号 | <span class="hljs-string">`(a, b) =&gt; a + b`</span> |<br>| 函数体只有一行 <span class="hljs-keyword">return</span> | 可省略 <span class="hljs-string">`&#123;&#125;`</span> 和 <span class="hljs-string">`return`</span> | <span class="hljs-string">`(a, b) =&gt; a + b`</span> |<br>| 函数体有多行 | 必须用 <span class="hljs-string">`&#123;&#125;`</span>，显式写 <span class="hljs-string">`return`</span> | <span class="hljs-string">`(a, b) =&gt; &#123; console.log(a); return a + b; &#125;`</span> |<br><br><span class="hljs-function">(<span class="hljs-params">total, current</span>) =&gt;</span> total + current 等价于：<br><span class="hljs-keyword">function</span>(<span class="hljs-params">total, current</span>) &#123;<br>  <span class="hljs-keyword">return</span> total + current;<br>&#125;<br><br><span class="hljs-comment">// 基本形式</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">multiply</span> = (<span class="hljs-params">a, b</span>) =&gt; a * b;<br><br><span class="hljs-comment">// 单参数可省略括号</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">square</span> = x =&gt; x * x;<br><br><span class="hljs-comment">// 多行语句需用 &#123;&#125;</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">greet</span> = name =&gt; &#123;<br>  <span class="hljs-keyword">const</span> msg = <span class="hljs-string">&quot;欢迎，&quot;</span> + name;<br>  <span class="hljs-keyword">return</span> msg;<br>&#125;;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">multiply</span>(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>)); <span class="hljs-comment">// 20</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">square</span>(<span class="hljs-number">3</span>));      <span class="hljs-comment">// 9</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">greet</span>(<span class="hljs-string">&quot;小红&quot;</span>));   <span class="hljs-comment">// &quot;欢迎，小红&quot;</span><br><br>📌 适用场景：简短的回调函数（如 map, filter）。<br><br>三、函数的参数（<span class="hljs-title class_">Parameters</span> &amp; <span class="hljs-title class_">Arguments</span>）<br><span class="hljs-number">1.</span> 默认参数（<span class="hljs-title class_">ES6</span>）<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">power</span>(<span class="hljs-params">base, exponent = <span class="hljs-number">2</span></span>) &#123;<br>  <span class="hljs-keyword">return</span> base ** exponent;<br>&#125;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">power</span>(<span class="hljs-number">3</span>));    <span class="hljs-comment">// 9（exponent 默认为 2）</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">power</span>(<span class="hljs-number">3</span>, <span class="hljs-number">3</span>)); <span class="hljs-comment">// 27</span><br><br><span class="hljs-number">2.</span> 剩余参数（<span class="hljs-title class_">Rest</span> <span class="hljs-title class_">Parameters</span>，<span class="hljs-title class_">ES6</span>）<br>用 ... 收集多余参数为数组。<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">sum</span>(<span class="hljs-params">...numbers</span>) &#123;<br>  <span class="hljs-keyword">return</span> numbers.<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">total, n</span>) =&gt;</span> total + n, <span class="hljs-number">0</span>);<br>&#125;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">sum</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>)); <span class="hljs-comment">// 10</span><br><br>四、返回值（<span class="hljs-title class_">Return</span>）<br>使用 <span class="hljs-keyword">return</span> 语句返回结果。<br>如果没有 <span class="hljs-keyword">return</span>，函数默认返回 <span class="hljs-literal">undefined</span>。<br><br>五、作用域（<span class="hljs-title class_">Scope</span>）与闭包（<span class="hljs-title class_">Closure</span>）<br><span class="hljs-number">1.</span> 作用域<br>全局作用域：在函数外定义的变量。<br>函数作用域：在函数内定义的变量（<span class="hljs-keyword">var</span>）。<br>块级作用域：在 &#123;&#125; 内用 <span class="hljs-keyword">let</span>/<span class="hljs-keyword">const</span> 定义的变量。<br><br><span class="hljs-keyword">let</span> globalVar = <span class="hljs-string">&quot;全局&quot;</span>;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">outer</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">let</span> localVar = <span class="hljs-string">&quot;局部&quot;</span>;<br>  <span class="hljs-keyword">if</span> (<span class="hljs-literal">true</span>) &#123;<br>    <span class="hljs-keyword">let</span> blockVar = <span class="hljs-string">&quot;块级&quot;</span>;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(blockVar); <span class="hljs-comment">// ✅</span><br>  &#125;<br>  <span class="hljs-comment">// console.log(blockVar); // ❌ 无法访问</span><br>&#125;<br><br><span class="hljs-number">2.</span> 闭包（<span class="hljs-title class_">Closure</span>）<br>内部函数可以访问外部函数的变量，即使外部函数已执行完毕。<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">createCounter</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;<br>    count++;<br>    <span class="hljs-keyword">return</span> count;<br>  &#125;;<br>&#125;<br><br><span class="hljs-keyword">const</span> counter = <span class="hljs-title function_">createCounter</span>();<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">counter</span>()); <span class="hljs-comment">// 1</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">counter</span>()); <span class="hljs-comment">// 2</span><br><span class="hljs-comment">// count 变量被“封闭”在返回的函数中！</span><br><br>✅ 应用：模块模式、私有变量、防抖节流等。<br><br>六、高阶函数（<span class="hljs-title class_">Higher</span>-<span class="hljs-title class_">Order</span> <span class="hljs-title class_">Functions</span>）<br>函数可以：<br>接收函数作为参数<br>返回一个函数<br>这是函数式编程的基础！<br>示例 <span class="hljs-number">1</span>：接收函数作为参数<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">operate</span>(<span class="hljs-params">a, b, fn</span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-title function_">fn</span>(a, b);<br>&#125;<br><br><span class="hljs-keyword">const</span> result = <span class="hljs-title function_">operate</span>(<span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-function">(<span class="hljs-params">x, y</span>) =&gt;</span> x / y);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(result); <span class="hljs-comment">// 2</span><br><br>示例 <span class="hljs-number">2</span>：返回函数（柯里化）<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">multiply</span>(<span class="hljs-params">a</span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params">b</span>) &#123;<br>    <span class="hljs-keyword">return</span> a * b;<br>  &#125;;<br>&#125;<br><br><span class="hljs-keyword">const</span> double = <span class="hljs-title function_">multiply</span>(<span class="hljs-number">2</span>);<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">double</span>(<span class="hljs-number">5</span>)); <span class="hljs-comment">// 10</span><br><br>🔥 数组方法如 .<span class="hljs-title function_">map</span>(), .<span class="hljs-title function_">filter</span>(), .<span class="hljs-title function_">reduce</span>() 都是高阶函数！<br><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x * <span class="hljs-number">2</span>); <span class="hljs-comment">// [2, 4, 6]</span><br><br>七、<span class="hljs-variable language_">this</span> 关键字（重要！）<br>普通函数中的 <span class="hljs-variable language_">this</span> 取决于调用方式。<br>箭头函数中的 <span class="hljs-variable language_">this</span> 继承自外层作用域。<br><br><span class="hljs-keyword">const</span> obj = &#123;<br>  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;小明&quot;</span>,<br>  <span class="hljs-attr">greetNormal</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;普通函数:&quot;</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span>); <span class="hljs-comment">// &quot;小明&quot;</span><br>  &#125;,<br>  <span class="hljs-attr">greetArrow</span>: <span class="hljs-function">() =&gt;</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;箭头函数:&quot;</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span>); <span class="hljs-comment">// undefined（this 指向全局）</span><br>  &#125;<br>&#125;;<br><br>obj.<span class="hljs-title function_">greetNormal</span>(); <span class="hljs-comment">// ✅</span><br>obj.<span class="hljs-title function_">greetArrow</span>();  <span class="hljs-comment">// ❌</span><br><br>✅ 规则：<br>方法用普通函数<br>回调用箭头函数（避免 <span class="hljs-variable language_">this</span> 丢失）<br><br>八、立即执行函数表达式（<span class="hljs-variable constant_">IIFE</span>）<br>定义后立即调用，常用于创建私有作用域。<br><br>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">var</span> privateVar = <span class="hljs-string">&quot;我是私有的&quot;</span>;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(privateVar);<br>&#125;)(); <span class="hljs-comment">// &quot;我是私有的&quot;</span><br><br><span class="hljs-comment">// 外部无法访问 privateVar</span><br><br><span class="hljs-title class_">ES6</span> 后多用块级作用域替代，但 <span class="hljs-variable constant_">IIFE</span> 在模块化中仍有价值。<br><br>九、实战小例子<br><span class="hljs-number">1.</span> 防抖函数（<span class="hljs-title class_">Debounce</span>）<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">debounce</span>(<span class="hljs-params">fn, delay</span>) &#123;<br>  <span class="hljs-keyword">let</span> timer;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>) &#123;<br>    <span class="hljs-built_in">clearTimeout</span>(timer);<br>    timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> fn.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>, args), delay);<br>  &#125;;<br>&#125;<br><br><span class="hljs-keyword">const</span> handleInput = <span class="hljs-title function_">debounce</span>(<span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;搜索:&quot;</span>, value);<br>&#125;, <span class="hljs-number">300</span>);<br><br><span class="hljs-number">2.</span> 记忆化函数（缓存计算结果）<br><span class="hljs-keyword">function</span> <span class="hljs-title function_">memoize</span>(<span class="hljs-params">fn</span>) &#123;<br>  <span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Map</span>();<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>) &#123;<br>    <span class="hljs-keyword">const</span> key = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(args);<br>    <span class="hljs-keyword">if</span> (cache.<span class="hljs-title function_">has</span>(key)) &#123;<br>      <span class="hljs-keyword">return</span> cache.<span class="hljs-title function_">get</span>(key);<br>    &#125;<br>    <span class="hljs-keyword">const</span> result = <span class="hljs-title function_">fn</span>(...args);<br>    cache.<span class="hljs-title function_">set</span>(key, result);<br>    <span class="hljs-keyword">return</span> result;<br>  &#125;;<br>&#125;<br><br><span class="hljs-keyword">const</span> fib = <span class="hljs-title function_">memoize</span>(<span class="hljs-function"><span class="hljs-params">n</span> =&gt;</span> n &lt;= <span class="hljs-number">1</span> ? n : <span class="hljs-title function_">fib</span>(n - <span class="hljs-number">1</span>) + <span class="hljs-title function_">fib</span>(n - <span class="hljs-number">2</span>));<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">fib</span>(<span class="hljs-number">10</span>)); <span class="hljs-comment">// 快速计算</span><br><br>✅ 推荐实践<br>在现代 <span class="hljs-title class_">JavaScript</span>（<span class="hljs-title class_">ES6</span>+）中，优先使用以下方式定义函数：<br><br><span class="hljs-comment">// 纯计算、工具函数 → 用箭头函数 + const</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">add</span> = (<span class="hljs-params">a, b</span>) =&gt; a + b;<br><span class="hljs-keyword">const</span> <span class="hljs-title function_">isEven</span> = n =&gt; n % <span class="hljs-number">2</span> === <span class="hljs-number">0</span>;<br><br><span class="hljs-comment">// 需要函数名用于调试或递归 → 用命名函数表达式</span><br><span class="hljs-keyword">const</span> factorial = <span class="hljs-keyword">function</span> <span class="hljs-title function_">calc</span>(<span class="hljs-params">n</span>) &#123;<br>  <span class="hljs-keyword">return</span> n &lt;= <span class="hljs-number">1</span> ? <span class="hljs-number">1</span> : n * <span class="hljs-title function_">calc</span>(n - <span class="hljs-number">1</span>);<br>&#125;;<br><br><span class="hljs-comment">// 对象方法、需要 this 的场景 → 用普通函数（或方法简写）</span><br><span class="hljs-keyword">const</span> obj = &#123;<br>  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;JS&quot;</span>,<br>  <span class="hljs-title function_">greet</span>(<span class="hljs-params"></span>) &#123; <span class="hljs-comment">// 等价于 greet: function() &#123; ... &#125;</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello, <span class="hljs-subst">$&#123;<span class="hljs-variable language_">this</span>.name&#125;</span>!`</span>;<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p><strong>4.Date对象</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br></pre></td><td class="code"><pre><code class="hljs javascript">一、创建 <span class="hljs-title class_">Date</span> 对象<br>✅ <span class="hljs-number">1.</span> 当前时间（最常用）<br><span class="hljs-keyword">const</span> now = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(now); <span class="hljs-comment">// 如：2026-03-27T10:30:45.123Z（本地时区显示）</span><br><br>✅ <span class="hljs-number">2.</span> 指定日期时间（多种方式）<br>方式 B：传入数字参数（推荐！明确无歧义）<br><br><span class="hljs-comment">// new Date(year, monthIndex, day, hours, minutes, seconds, milliseconds)</span><br><span class="hljs-comment">// 注意：monthIndex 从 0 开始（0=January, 11=December）</span><br><br><span class="hljs-keyword">const</span> date1 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-number">2026</span>, <span class="hljs-number">2</span>, <span class="hljs-number">27</span>);        <span class="hljs-comment">// 2026年3月27日（month=2 → 3月）</span><br><span class="hljs-keyword">const</span> date2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-number">2026</span>, <span class="hljs-number">2</span>, <span class="hljs-number">27</span>, <span class="hljs-number">14</span>, <span class="hljs-number">30</span>); <span class="hljs-comment">// 2026-03-27 14:30:00</span><br><br>方式 C：传入时间戳（毫秒）<br><span class="hljs-keyword">const</span> timestamp = <span class="hljs-number">1711536645123</span>;<br><span class="hljs-keyword">const</span> date3 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(timestamp);<br><br>💡 获取当前时间戳：<br><span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>(); <span class="hljs-comment">// 返回毫秒数（推荐）</span><br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">getTime</span>(); <span class="hljs-comment">// 等价</span><br><br>二、获取日期/时间信息（<span class="hljs-title class_">Getter</span> 方法）<br>所有 getter 方法都基于本地时区（除非用 <span class="hljs-variable constant_">UTC</span> 方法）。<br><br>.<span class="hljs-title function_">getFullYear</span>()年份（<span class="hljs-number">4</span>位）<span class="hljs-number">2026</span><br>.<span class="hljs-title function_">getMonth</span>()月份（<span class="hljs-number">0</span>~<span class="hljs-number">11</span>）<span class="hljs-number">2</span>（表示<span class="hljs-number">3</span>月）⚠️<br>.<span class="hljs-title function_">getDate</span>()日期（<span class="hljs-number">1</span>~<span class="hljs-number">31</span>）<span class="hljs-number">27</span><br>.<span class="hljs-title function_">getDay</span>()星期（<span class="hljs-number">0</span>~<span class="hljs-number">6</span>，<span class="hljs-number">0</span>=周日）<span class="hljs-number">4</span>（周四）<br>.<span class="hljs-title function_">getHours</span>()小时（<span class="hljs-number">0</span>~<span class="hljs-number">23</span>）<span class="hljs-number">14</span><br>.<span class="hljs-title function_">getMinutes</span>()分钟（<span class="hljs-number">0</span>~<span class="hljs-number">59</span>）<span class="hljs-number">30</span><br>.<span class="hljs-title function_">getSeconds</span>()秒（<span class="hljs-number">0</span>~<span class="hljs-number">59</span>）<span class="hljs-number">45</span><br>.<span class="hljs-title function_">getMilliseconds</span>()毫秒（<span class="hljs-number">0</span>~<span class="hljs-number">999</span>）<span class="hljs-number">123</span><br>.<span class="hljs-title function_">getTime</span>()时间戳（毫秒）<span class="hljs-number">1711536645123</span><br><br>🔁 <span class="hljs-variable constant_">UTC</span> 版本（加 <span class="hljs-variable constant_">UTC</span> 前缀）<br>date.<span class="hljs-title function_">getUTCFullYear</span>(); <span class="hljs-comment">// 获取 UTC 年份</span><br>date.<span class="hljs-title function_">getUTCHours</span>();    <span class="hljs-comment">// 获取 UTC 小时</span><br><br>三、设置日期/时间（<span class="hljs-title class_">Setter</span> 方法）<br>.<span class="hljs-title function_">setFullYear</span>(year, month?, day?)设置年、月、日<br>.<span class="hljs-title function_">setMonth</span>(month, day?)设置月（<span class="hljs-number">0</span>~<span class="hljs-number">11</span>）、日<br>.<span class="hljs-title function_">setDate</span>(day)设置日期<br>.<span class="hljs-title function_">setHours</span>(hours, min?, sec?, ms?)设置时、分、秒、毫秒<br>.<span class="hljs-title function_">setTime</span>(timestamp)通过时间戳设置<br><br><span class="hljs-keyword">const</span> d = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-number">2026</span>, <span class="hljs-number">2</span>, <span class="hljs-number">27</span>);<br>d.<span class="hljs-title function_">setFullYear</span>(<span class="hljs-number">2027</span>);        <span class="hljs-comment">// 改为 2027-03-27</span><br>d.<span class="hljs-title function_">setMonth</span>(<span class="hljs-number">3</span>);              <span class="hljs-comment">// 改为 2027-04-27（month=3 → 4月）</span><br>d.<span class="hljs-title function_">setDate</span>(<span class="hljs-number">1</span>);               <span class="hljs-comment">// 改为 2027-04-01</span><br><br>⚠️ 注意：如果设置的值超出范围，会自动进位！<br><br>d.<span class="hljs-title function_">setDate</span>(<span class="hljs-number">32</span>); <span class="hljs-comment">// 如果当前月只有31天，会变成下个月1号</span><br><br>四、日期计算与比较<br>✅ <span class="hljs-number">1.</span> 时间差（毫秒）<br><span class="hljs-keyword">const</span> start = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;2026-01-01&quot;</span>);<br><span class="hljs-keyword">const</span> end = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;2026-03-27&quot;</span>);<br><span class="hljs-keyword">const</span> diffMs = end - start; <span class="hljs-comment">// 直接相减得到毫秒差</span><br><br><span class="hljs-comment">// 转换为天数</span><br><span class="hljs-keyword">const</span> diffDays = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(diffMs / (<span class="hljs-number">1000</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">24</span>));<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(diffDays); <span class="hljs-comment">// 86 天</span><br><br>✅ <span class="hljs-number">2.</span> 比较日期<br><br><span class="hljs-keyword">const</span> d1 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;2026-03-27&quot;</span>);<br><span class="hljs-keyword">const</span> d2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;2026-04-01&quot;</span>);<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(d1 &lt; d2);  <span class="hljs-comment">// true</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(d1 &gt; d2);  <span class="hljs-comment">// false</span><br><br>💡 日期对象可以直接用 &lt;, &gt;, == 比较（内部转为时间戳）。<br><br>五、日期格式化（重点！）<br><br>✅ 推荐方法 <span class="hljs-number">1</span>：手动拼接（简单场景）<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">formatDate</span>(<span class="hljs-params">date</span>) &#123;<br>  <span class="hljs-keyword">const</span> y = date.<span class="hljs-title function_">getFullYear</span>();<br>  <span class="hljs-keyword">const</span> m = <span class="hljs-title class_">String</span>(date.<span class="hljs-title function_">getMonth</span>() + <span class="hljs-number">1</span>).<span class="hljs-title function_">padStart</span>(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>); <span class="hljs-comment">// 月份+1并补零</span><br>  <span class="hljs-keyword">const</span> d = <span class="hljs-title class_">String</span>(date.<span class="hljs-title function_">getDate</span>()).<span class="hljs-title function_">padStart</span>(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br>  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;y&#125;</span>-<span class="hljs-subst">$&#123;m&#125;</span>-<span class="hljs-subst">$&#123;d&#125;</span>`</span>;<br>&#125;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">formatDate</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>())); <span class="hljs-comment">// &quot;2026-03-27&quot;</span><br><br>✅ 推荐方法 <span class="hljs-number">2</span>：<span class="hljs-title function_">toLocaleDateString</span>()（本地化）<br><br><span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;2026-03-27&quot;</span>);<br><br><span class="hljs-comment">// 中文格式</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(date.<span class="hljs-title function_">toLocaleDateString</span>(<span class="hljs-string">&#x27;zh-CN&#x27;</span>));<br><span class="hljs-comment">// &quot;2026/3/27&quot;</span><br><br><span class="hljs-comment">// 自定义选项</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(date.<span class="hljs-title function_">toLocaleDateString</span>(<span class="hljs-string">&#x27;zh-CN&#x27;</span>, &#123;<br>  <span class="hljs-attr">year</span>: <span class="hljs-string">&#x27;numeric&#x27;</span>,<br>  <span class="hljs-attr">month</span>: <span class="hljs-string">&#x27;2-digit&#x27;</span>,<br>  <span class="hljs-attr">day</span>: <span class="hljs-string">&#x27;2-digit&#x27;</span><br>&#125;)); <span class="hljs-comment">// &quot;2026/03/27&quot;</span><br><br>✅ 推荐方法 <span class="hljs-number">3</span>：<span class="hljs-title function_">toISOString</span>()（<span class="hljs-variable constant_">ISO</span> <span class="hljs-number">8601</span> 标准，常用于 <span class="hljs-variable constant_">API</span>）<br><br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>();<br><span class="hljs-comment">// &quot;2026-03-27T02:30:45.123Z&quot;（UTC 时间）</span><br><br>✅ 推荐方法 <span class="hljs-number">4</span>：第三方库（复杂需求）<br>date-fns（轻量、函数式）<br><span class="hljs-title class_">Day</span>.<span class="hljs-property">js</span>（轻量、<span class="hljs-title class_">Moment</span>.<span class="hljs-property">js</span> 替代）<br><span class="hljs-title class_">Luxon</span>（强大、时区支持好）<br>🌟 现代项目建议：不要自己造轮子，用 date-fns 或 <span class="hljs-title class_">Day</span>.<span class="hljs-property">js</span>！<br><br>六、常见陷阱与注意事项<br><br>⚠️ <span class="hljs-number">1.</span> 月份从 <span class="hljs-number">0</span> 开始<br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-number">2026</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>); <span class="hljs-comment">// 是 2026年**4月1日**，不是3月！</span><br><br>⚠️ <span class="hljs-number">2.</span> 日期字符串解析不一致<br><span class="hljs-comment">// 安全写法（ISO 8601）</span><br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;2026-03-27&quot;</span>);      <span class="hljs-comment">// ✅ 一致</span><br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;2026/03/27&quot;</span>);      <span class="hljs-comment">// ❌ Safari 可能报错</span><br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">&quot;03/27/2026&quot;</span>);      <span class="hljs-comment">// ❌ 格式混乱</span><br><br>⚠️ <span class="hljs-number">3.</span> 时区问题<br><span class="hljs-title class_">Date</span> 对象内部存储的是 <span class="hljs-variable constant_">UTC</span> 时间戳。<br>所有 getter/setter 默认使用本地时区。<br>跨时区应用务必明确使用 <span class="hljs-variable constant_">UTC</span> 方法或第三方库。<br><br>⚠️ <span class="hljs-number">4.</span> 夏令时（<span class="hljs-variable constant_">DST</span>）影响<br>在夏令时切换日，某些小时可能不存在或重复，导致计算偏差。<br><br>七、实战小例子<br><span class="hljs-number">1.</span> 判断是否是闰年<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">isLeapYear</span>(<span class="hljs-params">year</span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(year, <span class="hljs-number">1</span>, <span class="hljs-number">29</span>).<span class="hljs-title function_">getDate</span>() === <span class="hljs-number">29</span>;<br>&#125;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">isLeapYear</span>(<span class="hljs-number">2024</span>)); <span class="hljs-comment">// true</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">isLeapYear</span>(<span class="hljs-number">2025</span>)); <span class="hljs-comment">// false</span><br><br><span class="hljs-number">2.</span> 获取本月最后一天<br><span class="hljs-keyword">function</span> <span class="hljs-title function_">getLastDayOfMonth</span>(<span class="hljs-params">year, month</span>) &#123;<br>  <span class="hljs-comment">// 下个月的第0天就是本月最后一天</span><br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(year, month, <span class="hljs-number">0</span>).<span class="hljs-title function_">getDate</span>();<br>&#125;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">getLastDayOfMonth</span>(<span class="hljs-number">2026</span>, <span class="hljs-number">2</span>)); <span class="hljs-comment">// 28（2026年2月）</span><br><br><span class="hljs-number">3.</span> 计算年龄<br><span class="hljs-keyword">function</span> <span class="hljs-title function_">getAge</span>(<span class="hljs-params">birthDate</span>) &#123;<br>  <span class="hljs-keyword">const</span> today = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();<br>  <span class="hljs-keyword">const</span> birth = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(birthDate);<br>  <span class="hljs-keyword">let</span> age = today.<span class="hljs-title function_">getFullYear</span>() - birth.<span class="hljs-title function_">getFullYear</span>();<br>  <span class="hljs-keyword">const</span> monthDiff = today.<span class="hljs-title function_">getMonth</span>() - birth.<span class="hljs-title function_">getMonth</span>();<br>  <span class="hljs-keyword">if</span> (monthDiff &lt; <span class="hljs-number">0</span> || (monthDiff === <span class="hljs-number">0</span> &amp;&amp; today.<span class="hljs-title function_">getDate</span>() &lt; birth.<span class="hljs-title function_">getDate</span>())) &#123;<br>    age--;<br>  &#125;<br>  <span class="hljs-keyword">return</span> age;<br>&#125;<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">getAge</span>(<span class="hljs-string">&quot;1990-05-15&quot;</span>)); <span class="hljs-comment">// 如：35</span><br></code></pre></td></tr></table></figure><p><strong>5.this</strong><br>（1）this到底是什么？</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 定义一个“修车”函数</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">changeTire</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;正在给 &quot;</span> + <span class="hljs-variable language_">this</span>.<span class="hljs-property">brand</span> + <span class="hljs-string">&quot; 换轮胎&quot;</span>);<br>&#125;<br><br><span class="hljs-comment">// 张三的车</span><br><span class="hljs-keyword">const</span> zhangsanCar = &#123; <span class="hljs-attr">brand</span>: <span class="hljs-string">&quot;BMW&quot;</span>, <span class="hljs-attr">fix</span>: changeTire &#125;;<br><br><span class="hljs-comment">// 李四的车</span><br><span class="hljs-keyword">const</span> lisiCar = &#123; <span class="hljs-attr">brand</span>: <span class="hljs-string">&quot;Toyota&quot;</span>, <span class="hljs-attr">fix</span>: changeTire &#125;;<br><br><span class="hljs-comment">// 谁调用，this 就是谁！</span><br>zhangsanCar.<span class="hljs-title function_">fix</span>(); <span class="hljs-comment">// 输出：正在给 BMW 换轮胎 → this = zhangsanCar</span><br>lisiCar.<span class="hljs-title function_">fix</span>();     <span class="hljs-comment">// 输出：正在给 Toyota 换轮胎 → this = lisiCar</span><br></code></pre></td></tr></table></figure><p>✅ 看懂了吗？this 的值不是写函数时定的，而是调用函数那一刻才确定的！</p><p>❓ 那如果没人“拥有”这个函数呢？<br>changeTire(); &#x2F;&#x2F; 没有通过对象调用<br>这时候：<br>非严格模式：this 会变成 全局对象（浏览器里就是 window）。<br>相当于“没人派活，你自己瞎修，修的是整个地球（window）”。<br>严格模式 (‘use strict’)：this 是 undefined。<br>相当于“没人派活，你不能乱修，报错！”<br>✅ 现代开发建议：永远不要依赖这种“裸调用”的 this，很容易出错！</p><p>🔧 this 到底有什么用？<br>它的核心作用就一个：让同一个函数能为不同的对象服务！<br>如果没有 this，你得为每辆车写一个专属修车函数：<br>function fixBMW() { … }<br>function fixToyota() { … }<br>function fixBenz() { … }<br>&#x2F;&#x2F; 太蠢了！<br>有了 this，一个函数搞定所有车！这就是代码复用，是面向对象编程的精髓。</p><p>⚠️ 常见坑：箭头函数没有自己的 this</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> car = &#123;<br>  <span class="hljs-attr">brand</span>: <span class="hljs-string">&quot;Tesla&quot;</span>,<br>  <span class="hljs-attr">report</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-comment">// 普通函数：this = car</span><br>    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;<br>      <span class="hljs-comment">// 普通回调函数：this = window (或 undefined)</span><br>      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">brand</span>); <span class="hljs-comment">// undefined!</span><br>    &#125;, <span class="hljs-number">1000</span>);<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>解决方案：用箭头函数（它会“继承”外层的 this）</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> car = &#123;<br>  <span class="hljs-attr">brand</span>: <span class="hljs-string">&quot;Tesla&quot;</span>,<br>  <span class="hljs-attr">report</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>      <span class="hljs-comment">// 箭头函数：this 继承自 report 函数 → this = car</span><br>      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">brand</span>); <span class="hljs-comment">// &quot;Tesla&quot; ✓</span><br>    &#125;, <span class="hljs-number">1000</span>);<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>✅ 记住：对象方法用普通函数，回调&#x2F;工具函数用箭头函数。</p><p>📌 终极总结</p><p><img src="https://www.aoiblog.top/images/img_1774665975804.png" alt="图片"></p><p><strong>6.事件</strong></p><p><img src="https://www.aoiblog.top/images/img_1774684139264.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684152843.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684164022.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684171207.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684178801.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684185041.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684191234.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684197384.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684205144.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684211778.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684216949.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684225265.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684231865.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684236842.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684242181.png" alt="图片"></p><p><img src="https://www.aoiblog.top/images/img_1774684246762.png" alt="图片"></p><p><strong>6.定时器</strong><br>🎯 一、定时器是什么？<br>简单说，定时器就是让 JavaScript “等一会儿再做某事” 或 “每隔一段时间就做某事” 的机制。<br>因为 JavaScript 是单线程的，它不能真正“暂停”，所以定时器是一种异步操作：你告诉浏览器“X 毫秒后帮我做 Y”，然后 JS 继续执行后面的代码，等时间到了，浏览器再把 Y 任务塞回执行队列。</p><p>⏱️ 二、两种核心定时器<br>JavaScript 提供了两个全局函数（其实是 window 对象的方法）：</p><ol><li>setTimeout：延迟一次执行<br>作用：在指定的毫秒数后，执行一次某个函数。<br>语法：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> timerId = <span class="hljs-built_in">setTimeout</span>(callback, delay, arg1, arg2, ...);<br></code></pre></td></tr></table></figure><p>callback：要执行的函数。<br>delay：延迟时间（毫秒）。注意：这是最小延迟，不是精确时间！<br>arg1, arg2…：可选，传递给 callback 的参数。<br>返回值：一个定时器 ID（数字），用于后续取消。<br>✅ 基础示例</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;开始&#x27;</span>);<br><br><span class="hljs-comment">// 2秒后执行</span><br><span class="hljs-keyword">const</span> id = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;2秒过去了！&#x27;</span>);<br>&#125;, <span class="hljs-number">2000</span>);<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;结束&#x27;</span>); <span class="hljs-comment">// 这行会立刻打印</span><br></code></pre></td></tr></table></figure><p>🛑 取消定时器：clearTimeout<br>如果你在定时器触发前改变了主意，可以用 clearTimeout 取消它。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> id = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;这条消息不会出现&#x27;</span>);<br>&#125;, <span class="hljs-number">5000</span>);<br><br><span class="hljs-comment">// 在5秒内取消</span><br><span class="hljs-built_in">clearTimeout</span>(id);<br></code></pre></td></tr></table></figure><ol start="2"><li>setInterval：周期性重复执行<br>作用：每隔指定的毫秒数，重复执行某个函数。<br>语法：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> timerId = <span class="hljs-built_in">setInterval</span>(callback, interval, arg1, arg2, ...);<br></code></pre></td></tr></table></figure><p>参数含义同 setTimeout。<br>返回值：定时器 ID，用于取消。</p><p>✅ 基础示例：每秒打印时间</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> id = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toLocaleTimeString</span>());<br>&#125;, <span class="hljs-number">1000</span>);<br><br><span class="hljs-comment">// 5秒后停止</span><br><span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-built_in">clearInterval</span>(id);<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;定时器已停止&#x27;</span>);<br>&#125;, <span class="hljs-number">5000</span>);<br></code></pre></td></tr></table></figure><p>🛑 取消定时器：clearInterval<br>必须手动取消，否则它会一直执行下去（可能导致内存泄漏或性能问题）！</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> id = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> &#123; <span class="hljs-comment">/* ... */</span> &#125;, <span class="hljs-number">1000</span>);<br><span class="hljs-built_in">clearInterval</span>(id); <span class="hljs-comment">// 停止</span><br></code></pre></td></tr></table></figure><p>⚠️ 三、关键注意事项（非常重要！）</p><ol><li>定时器不是精确的！<br>浏览器的定时器受系统负载、标签页是否激活、浏览器节流策略影响。<br>最小延迟：<br>HTML5 规范规定，嵌套定时器（如 setTimeout 内部再调用 setTimeout）的最小延迟为 4ms。<br>未激活的标签页中，setTimeout&#x2F;setInterval 的最小间隔可能被限制为 1000ms。<br>不要用定时器做高精度计时器（比如游戏帧同步），应该用 requestAnimationFrame。</li><li>this 的指向问题<br>在定时器的回调函数中，this 默认指向 全局对象（浏览器中是 window），而不是你期望的对象。</li></ol><p>✅ 解决方案<br>使用箭头函数（推荐）：箭头函数没有自己的 this，会继承外层作用域。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title function_">greet</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span>); <span class="hljs-comment">// &quot;MyObject&quot; ✓</span><br>  &#125;, <span class="hljs-number">1000</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>缓存 this：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title function_">greet</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">const</span> self = <span class="hljs-variable language_">this</span>;<br>  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(self.<span class="hljs-property">name</span>); <span class="hljs-comment">// &quot;MyObject&quot;</span><br>  &#125;, <span class="hljs-number">1000</span>);<br>&#125;<br></code></pre></td></tr></table></figure><ol start="3"><li>不要忘记清理！<br>页面卸载（如跳转、刷新）时，未清理的定时器可能会导致内存泄漏或错误。<br>最佳实践：在组件销毁、页面离开时，主动调用 clearTimeout &#x2F; clearInterval。</li></ol><p>🛠️ 四、高级技巧与应用场景</p><ol><li>防抖（Debounce）<br>场景：用户在搜索框输入，你不想每按一个键就发一次请求，而是等他“停手”后再查。<br>原理：每次触发事件时，都重置定时器。只有在指定时间内不再触发，才执行函数。</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">debounce</span>(<span class="hljs-params">func, delay</span>) &#123;<br>  <span class="hljs-keyword">let</span> timerId;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>) &#123;<br>    <span class="hljs-comment">// 清除上一个定时器</span><br>    <span class="hljs-built_in">clearTimeout</span>(timerId);<br>    <span class="hljs-comment">// 设置新的定时器</span><br>    timerId = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> func.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>, args), delay);<br>  &#125;;<br>&#125;<br><br><span class="hljs-comment">// 使用</span><br><span class="hljs-keyword">const</span> searchInput = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;search&#x27;</span>);<br>searchInput.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;input&#x27;</span>, <span class="hljs-title function_">debounce</span>(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;搜索:&#x27;</span>, e.<span class="hljs-property">target</span>.<span class="hljs-property">value</span>);<br>&#125;, <span class="hljs-number">300</span>)); <span class="hljs-comment">// 用户停止输入300ms后才触发</span><br></code></pre></td></tr></table></figure><ol start="2"><li>节流（Throttle）<br>场景：监听页面滚动 (scroll) 或窗口缩放 (resize)，这些事件触发频率极高，需要限制执行频率。<br>原理：保证函数在 delay 时间内最多只执行一次。</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">throttle</span>(<span class="hljs-params">func, delay</span>) &#123;<br>  <span class="hljs-keyword">let</span> lastExecTime = <span class="hljs-number">0</span>;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>) &#123;<br>    <span class="hljs-keyword">const</span> now = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>();<br>    <span class="hljs-keyword">if</span> (now - lastExecTime &gt;= delay) &#123;<br>      func.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>, args);<br>      lastExecTime = now;<br>    &#125;<br>  &#125;;<br>&#125;<br><br><span class="hljs-comment">// 使用</span><br><span class="hljs-variable language_">window</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;scroll&#x27;</span>, <span class="hljs-title function_">throttle</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;页面滚动了&#x27;</span>);<br>&#125;, <span class="hljs-number">100</span>)); <span class="hljs-comment">// 每100ms最多执行一次</span><br></code></pre></td></tr></table></figure><ol start="3"><li>倒计时器</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">countdown</span>(<span class="hljs-params">seconds, onTick, onComplete</span>) &#123;<br>  <span class="hljs-keyword">let</span> remaining = seconds;<br><br>  <span class="hljs-keyword">const</span> timerId = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>    <span class="hljs-title function_">onTick</span>(remaining); <span class="hljs-comment">// 每秒调用</span><br>    remaining--;<br><br>    <span class="hljs-keyword">if</span> (remaining &lt; <span class="hljs-number">0</span>) &#123;<br>      <span class="hljs-built_in">clearInterval</span>(timerId);<br>      <span class="hljs-title function_">onComplete</span>(); <span class="hljs-comment">// 倒计时结束</span><br>    &#125;<br>  &#125;, <span class="hljs-number">1000</span>);<br>&#125;<br><br><span class="hljs-comment">// 使用</span><br><span class="hljs-title function_">countdown</span>(<span class="hljs-number">5</span>,<br>  <span class="hljs-function">(<span class="hljs-params">sec</span>) =&gt;</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`剩余: <span class="hljs-subst">$&#123;sec&#125;</span> 秒`</span>),<br>  <span class="hljs-function">() =&gt;</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;时间到！&#x27;</span>)<br>);<br></code></pre></td></tr></table></figure><p>记住：<br>定时器是异步的，不会阻塞代码执行。<br>永远考虑清理，避免内存泄漏。<br>复杂场景优先用 防抖&#x2F;节流 优化性能。<br>高精度动画请用 requestAnimationFrame。</p><p><strong>7.onload与onerror</strong></p><p>一句话总结，onload就是等资源加载成功之后，执行这个函数，onerror同理</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">loadImage</span>(<span class="hljs-params">src, successCallback, errorCallback</span>) &#123;<br>  <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>();<br><br>  img.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> <span class="hljs-title function_">successCallback</span>(img);<br>  img.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> <span class="hljs-title function_">errorCallback</span>(src); <span class="hljs-comment">// 把失败的地址传出去</span><br><br>  img.<span class="hljs-property">src</span> = src; <span class="hljs-comment">// 最后赋值！</span><br>&#125;<br><br><span class="hljs-comment">// 使用</span><br><span class="hljs-title function_">loadImage</span>(<br>  <span class="hljs-string">&#x27;user-avatar.jpg&#x27;</span>,<br>  <span class="hljs-function">(<span class="hljs-params">img</span>) =&gt;</span> <span class="hljs-variable language_">document</span>.<span class="hljs-property">body</span>.<span class="hljs-title function_">appendChild</span>(img),<br>  <span class="hljs-function">(<span class="hljs-params">failedUrl</span>) =&gt;</span> &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">warn</span>(<span class="hljs-string">`图片加载失败: <span class="hljs-subst">$&#123;failedUrl&#125;</span>`</span>);<br>    <span class="hljs-comment">// 显示默认头像</span><br>    <span class="hljs-keyword">const</span> defaultImg = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>();<br>    defaultImg.<span class="hljs-property">src</span> = <span class="hljs-string">&#x27;default-avatar.jpg&#x27;</span>;<br>    <span class="hljs-variable language_">document</span>.<span class="hljs-property">body</span>.<span class="hljs-title function_">appendChild</span>(defaultImg);<br>  &#125;<br>);<br></code></pre></td></tr></table></figure><p>应该使用onload和onerror的典型场景：<br>场景 1：动态加载图片（尤其是用户头像、商品图等）<br>为什么需要：<br>用户上传的图片可能被删除；<br>CDN 可能暂时不可用；<br>网络不稳定导致加载失败。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>();<br>img.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// ✅ 成功：显示图片</span><br>  container.<span class="hljs-title function_">appendChild</span>(img);<br>&#125;;<br>img.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// ❌ 失败：显示默认占位图</span><br>  img.<span class="hljs-property">src</span> = <span class="hljs-string">&#x27;/assets/default-avatar.png&#x27;</span>;<br>&#125;;<br>img.<span class="hljs-property">src</span> = user.<span class="hljs-property">avatarUrl</span>; <span class="hljs-comment">// 最后赋值！</span><br></code></pre></td></tr></table></figure><p>场景 2：动态加载外部脚本（如第三方 SDK、A&#x2F;B 测试、广告）<br>为什么需要：<br>第三方服务可能宕机；<br>你需要确保脚本加载成功后再调用其 API；<br>避免因脚本未加载导致功能报错。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> script = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">&#x27;script&#x27;</span>);<br>script.<span class="hljs-property">src</span> = <span class="hljs-string">&#x27;https://cdn.example.com/analytics.js&#x27;</span>;<br><br>script.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// ✅ 脚本加载成功，初始化</span><br>  <span class="hljs-title function_">initAnalytics</span>();<br>&#125;;<br><br>script.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-comment">// ❌ 加载失败，记录日志或降级</span><br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">&#x27;Analytics SDK failed to load&#x27;</span>);<br>  <span class="hljs-comment">// 可选：尝试备用 CDN</span><br>  <span class="hljs-comment">// loadBackupScript();</span><br>&#125;;<br><br><span class="hljs-variable language_">document</span>.<span class="hljs-property">head</span>.<span class="hljs-title function_">appendChild</span>(script);<br></code></pre></td></tr></table></figure><p>场景 3：懒加载（Lazy Loading）或预加载（Preloading）资源<br>为什么需要：<br>图片进入视口才加载，需在加载完成后替换占位符；<br>预加载关键资源（如游戏素材），需知道何时可用。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 懒加载示例</span><br><span class="hljs-keyword">const</span> placeholder = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;.lazy-img&#x27;</span>);<br><span class="hljs-keyword">const</span> realImg = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>();<br><br>realImg.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  placeholder.<span class="hljs-property">src</span> = realImg.<span class="hljs-property">src</span>; <span class="hljs-comment">// 替换为真实图</span><br>  placeholder.<span class="hljs-property">classList</span>.<span class="hljs-title function_">remove</span>(<span class="hljs-string">&#x27;loading&#x27;</span>);<br>&#125;;<br><br>realImg.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  placeholder.<span class="hljs-property">classList</span>.<span class="hljs-title function_">add</span>(<span class="hljs-string">&#x27;load-failed&#x27;</span>);<br>&#125;;<br><br>realImg.<span class="hljs-property">src</span> = placeholder.<span class="hljs-property">dataset</span>.<span class="hljs-property">src</span>; <span class="hljs-comment">// 从 data-src 读取真实 URL</span><br></code></pre></td></tr></table></figure><p>场景 4：处理 iframe 内容加载（较少见但有用）<br>为什么需要：<br>嵌入第三方页面（如支付页、地图）；<br>需要知道 iframe 是否加载完成以进行交互。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> iframe = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">&#x27;iframe&#x27;</span>);<br>iframe.<span class="hljs-property">src</span> = <span class="hljs-string">&#x27;https://payment.example.com&#x27;</span>;<br><br>iframe.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;支付页面已加载&#x27;</span>);<br>  <span class="hljs-comment">// 注意：跨域 iframe 无法访问其内容</span><br>&#125;;<br><br><span class="hljs-comment">// iframe 通常没有 onerror（规范不统一），慎用</span><br></code></pre></td></tr></table></figure><p><img src="https://www.aoiblog.top/images/img_1774838308678.png" alt="图片"></p><p><strong>8.try…catch</strong></p><p>一、基本语法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">try</span> &#123;<br>  <span class="hljs-comment">// 可能会出错的代码（“危险区”）</span><br>&#125; <span class="hljs-keyword">catch</span> (error) &#123;<br>  <span class="hljs-comment">// 如果 try 块中抛出错误，这里会执行</span><br>  <span class="hljs-comment">// error 是错误对象（通常包含 message、stack 等信息）</span><br>&#125; <span class="hljs-keyword">finally</span> &#123;<br>  <span class="hljs-comment">// （可选）无论是否出错，都会执行</span><br>&#125;<br></code></pre></td></tr></table></figure><p>二、核心作用：捕获“抛出”的错误<br>JavaScript 中的错误分为两类：<br>语法错误（SyntaxError） → 无法用 try…catch 捕获（因为代码根本无法运行）<br>运行时错误（Runtime Error） → 可以被 try…catch 捕获<br>✅ 能捕获的情况：<br>手动 throw 错误<br>调用函数时发生异常（如访问 null 属性、JSON 解析失败等）<br>❌ 不能捕获的情况：<br>语法错误（如 console.log( 缺少括号）<br>异步回调中的错误（除非在回调内部用 try…catch）</p><p>三、基本示例<br>示例 1：手动抛出并捕获错误</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">try</span> &#123;<br>  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&quot;这是一个自定义错误！&quot;</span>);<br>&#125; <span class="hljs-keyword">catch</span> (err) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;捕获到错误:&quot;</span>, err.<span class="hljs-property">message</span>); <span class="hljs-comment">// &quot;捕获到错误: 这是一个自定义错误！&quot;</span><br>&#125;<br></code></pre></td></tr></table></figure><p>示例 2：处理可能失败的操作</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">divide</span>(<span class="hljs-params">a, b</span>) &#123;<br>  <span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-keyword">if</span> (b === <span class="hljs-number">0</span>) &#123;<br>      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&quot;除数不能为零&quot;</span>);<br>    &#125;<br>    <span class="hljs-keyword">return</span> a / b;<br>  &#125; <span class="hljs-keyword">catch</span> (err) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">&quot;计算失败:&quot;</span>, err.<span class="hljs-property">message</span>);<br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>  &#125;<br>&#125;<br><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">divide</span>(<span class="hljs-number">10</span>, <span class="hljs-number">2</span>)); <span class="hljs-comment">// 5</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title function_">divide</span>(<span class="hljs-number">10</span>, <span class="hljs-number">0</span>)); <span class="hljs-comment">// 计算失败: 除数不能为零 → 返回 null</span><br></code></pre></td></tr></table></figure><p>四、与异步代码结合（重点！）<br>❌ 错误示范：try…catch 无法捕获异步回调中的错误<br>✅ 正确做法 1：在异步函数内部使用 try…catch</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&quot;异步错误&quot;</span>);<br>  &#125; <span class="hljs-keyword">catch</span> (err) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;在回调内部捕获:&quot;</span>, err.<span class="hljs-property">message</span>);<br>  &#125;<br>&#125;, <span class="hljs-number">100</span>);<br></code></pre></td></tr></table></figure><p>✅ 正确做法 2：使用 async&#x2F;await（推荐！）</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">fetchData</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&#x27;/api/data&#x27;</span>);<br>    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>(); <span class="hljs-comment">// 如果 JSON 无效，会抛出错误</span><br>    <span class="hljs-keyword">return</span> data;<br>  &#125; <span class="hljs-keyword">catch</span> (err) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">&quot;请求或解析失败:&quot;</span>, err.<span class="hljs-property">message</span>);<br>    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>  &#125;<br>&#125;<br><br><span class="hljs-comment">// 调用</span><br><span class="hljs-title function_">fetchData</span>();<br></code></pre></td></tr></table></figure><p>五、finally 块：无论成功失败都执行<br>常用于清理资源，比如关闭连接、隐藏加载动画等。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">let</span> loading = <span class="hljs-literal">true</span>;<br><br><span class="hljs-keyword">try</span> &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;开始加载...&quot;</span>);<br>  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-title function_">apiCall</span>();<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;数据:&quot;</span>, data);<br>&#125; <span class="hljs-keyword">catch</span> (err) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">&quot;失败:&quot;</span>, err.<span class="hljs-property">message</span>);<br>&#125; <span class="hljs-keyword">finally</span> &#123;<br>  loading = <span class="hljs-literal">false</span>; <span class="hljs-comment">// 无论成功失败，都结束 loading</span><br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&quot;加载状态已重置&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>六、错误对象（error）详解<br>catch 中的参数（通常叫 error 或 err）是一个 Error 对象，包含：<br>属性说明<br>error.message错误描述（字符串）<br>error.name错误类型（如 “Error”, “TypeError”, “SyntaxError”）<br>error.stack错误堆栈（调试用，显示错误发生的位置）</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">try</span> &#123;<br>  <span class="hljs-literal">null</span>.<span class="hljs-title function_">toString</span>();<br>&#125; <span class="hljs-keyword">catch</span> (err) &#123;<br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(err.<span class="hljs-property">name</span>);    <span class="hljs-comment">// &quot;TypeError&quot;</span><br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(err.<span class="hljs-property">message</span>); <span class="hljs-comment">// &quot;Cannot read properties of null (reading &#x27;toString&#x27;)&quot;</span><br>  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(err.<span class="hljs-property">stack</span>);   <span class="hljs-comment">// 包含文件名、行号等</span><br>&#125;<br></code></pre></td></tr></table></figure><p>八、常见应用场景<br>场景用法<br>API 请求捕获网络错误、JSON 解析失败<br>用户输入验证捕获格式错误（如日期解析）<br>文件操作捕获读取失败<br>第三方库调用防止库抛出未处理异常导致页面崩溃<br>降级处理出错时返回默认值或备用方案<br>🎯 记住一句话：<br>“把可能出错的代码放进 try，把容错逻辑放进 catch。”</p><p><strong>9.promise</strong><br>✅ 1. 什么是 Promise？<br>Promise 是一个对象，代表一个尚未完成但未来会完成的操作。<br>它有三种状态：<br>pending（进行中）<br>fulfilled（成功，也叫 resolved）<br>rejected（失败）<br>⚠️ 状态一旦改变（pending → fulfilled&#x2F;rejected），就不可逆。<br>✅ 2. 如何创建 Promise？<br>使用构造函数 new Promise(executor)：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> myPromise = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> &#123;<br>  <span class="hljs-comment">// 异步操作（比如 setTimeout、fetch、fs.readFile）</span><br>  <span class="hljs-keyword">if</span> (<span class="hljs-comment">/* 成功 */</span>) &#123;<br>    <span class="hljs-title function_">resolve</span>(value);     <span class="hljs-comment">// 改变状态为 fulfilled，传递 value</span><br>  &#125; <span class="hljs-keyword">else</span> &#123;<br>    <span class="hljs-title function_">reject</span>(error);      <span class="hljs-comment">// 改变状态为 rejected，传递 error</span><br>  &#125;<br>&#125;);<br></code></pre></td></tr></table></figure><p>resolve 和 reject 是 JS 引擎自动传入的函数。<br>你决定何时调用它们。<br>注：resolve(value) 和 reject(reason) 的作用是：<br>将 Promise 的状态从 pending（等待中）变为 fulfilled（成功）或 rejected（失败）；<br>将传入的 value 或 reason（通常是一个 Error 对象）作为“结果数据”保存在 Promise 内部；<br>当状态确定后，JavaScript 引擎会自动调用 .then(onFulfilled, onRejected) 中对应的回调函数，并将 value 或 reason 作为参数传递给它们。</p><p>✅ 3. 如何使用 Promise？—— .then() 和 .catch()</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript">myPromise<br>  .<span class="hljs-title function_">then</span>(<br>    <span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> &#123; <span class="hljs-comment">/* 处理成功 */</span> &#125;,<br>    <span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> &#123; <span class="hljs-comment">/* 处理失败（不常用）*/</span> &#125;<br>  )<br>  .<span class="hljs-title function_">catch</span>(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> &#123; <span class="hljs-comment">/* 统一处理链中任意错误 */</span> &#125;);<br></code></pre></td></tr></table></figure><p>关键特性：<br>.then() 返回新 Promise → 支持链式调用<br>值穿透：如果 .then() 不返回值，默认返回 undefined<br>异常穿透：链中任意 .then() 抛出错误，会跳到最近的 .catch()</p><p>✅ 4. 静态方法（非常有用！）<br>方法作用<br>Promise.resolve(value)创建一个已 fulfilled 的 Promise<br>Promise.reject(reason)创建一个已 rejected 的 Promise<br>Promise.all([p1, p2, …])并行执行，全部成功才成功；任一失败则失败<br>Promise.race([p1, p2, …])谁先完成（无论成功失败），就用谁的结果<br>Promise.allSettled([…])等所有 Promise 结束（不管成功失败），返回结果数组</p><p>示例：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 你自己创建 Promise</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">delay</span>(<span class="hljs-params">ms</span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> &#123;<br>    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>      <span class="hljs-keyword">if</span> (ms &gt; <span class="hljs-number">0</span>) &#123;<br>        <span class="hljs-title function_">resolve</span>(<span class="hljs-string">`等待了 <span class="hljs-subst">$&#123;ms&#125;</span> 毫秒`</span>); <span class="hljs-comment">// ← 你决定成功</span><br>      &#125; <span class="hljs-keyword">else</span> &#123;<br>        <span class="hljs-title function_">reject</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&#x27;时间不能为负数&#x27;</span>)); <span class="hljs-comment">// ← 你决定失败</span><br>      &#125;<br>    &#125;, <span class="hljs-number">100</span>);<br>  &#125;);<br>&#125;<br><br><span class="hljs-comment">// 你自己写 onFulfilled 和 onRejected</span><br><span class="hljs-title function_">delay</span>(<span class="hljs-number">500</span>).<span class="hljs-title function_">then</span>(<br>  <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> &#123;<br>    <span class="hljs-comment">// 👈 这个函数是你写的！处理成功</span><br>    <span class="hljs-title function_">alert</span>(message);<br>  &#125;,<br>  <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> &#123;<br>    <span class="hljs-comment">// 👈 这个函数也是你写的！处理失败</span><br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(err.<span class="hljs-property">message</span>);<br>  &#125;<br>);<br></code></pre></td></tr></table></figure><p>三、async &#x2F; await：Promise 的语法糖<br>✅ 1. async 函数是什么？<br>在函数前加 async，该函数自动返回一个 Promise<br>允许在函数内部使用 await</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">foo</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>;<br>&#125;<br><span class="hljs-comment">// 等价于：</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">foo</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">resolve</span>(<span class="hljs-number">42</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>✅ 2. await 是什么？<br>只能在 async 函数内部使用<br>等待一个 Promise 完成，并自动“解包”其结果</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getUser</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&#x27;/api/user&#x27;</span>); <span class="hljs-comment">// 暂停，等 fetch 完成</span><br>  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>();        <span class="hljs-comment">// 暂停，等 json() 完成</span><br>  <span class="hljs-keyword">return</span> user; <span class="hljs-comment">// 自动包装为 Promise</span><br>&#125;<br></code></pre></td></tr></table></figure><p>✅ await 后面可以是：<br>Promise（最常见）<br>普通值（如 await 42 → 直接返回 42）<br>表达式</p><p>✅ 3. 错误处理：用 try…catch<br>这是 async&#x2F;await 最大的优势之一：用同步风格处理异步错误！</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">safeFetch</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&#x27;/api/data&#x27;</span>);<br>    <span class="hljs-keyword">if</span> (!res.<span class="hljs-property">ok</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`HTTP <span class="hljs-subst">$&#123;res.status&#125;</span>`</span>);<br>    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>();<br>    <span class="hljs-keyword">return</span> data;<br>  &#125; <span class="hljs-keyword">catch</span> (error) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">&#x27;请求失败:&#x27;</span>, error.<span class="hljs-property">message</span>);<br>    <span class="hljs-comment">// 可以返回默认值、重试、或重新抛出</span><br>    <span class="hljs-keyword">throw</span> error;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>✅ 4. 并行 vs 串行<br>❌ 串行（低效）：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">loadSlowly</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetchUser</span>();     <span class="hljs-comment">// 等 1 秒</span><br>  <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetchPosts</span>();   <span class="hljs-comment">// 再等 1 秒 → 总共 2 秒</span><br>  <span class="hljs-keyword">return</span> &#123; user, posts &#125;;<br>&#125;<br></code></pre></td></tr></table></figure><p>✅ 并行（高效）：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">loadQuickly</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-keyword">const</span> [user, posts] = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([<br>    <span class="hljs-title function_">fetchUser</span>(),<br>    <span class="hljs-title function_">fetchPosts</span>()<br>  ]); <span class="hljs-comment">// 同时发起，总耗时 ≈ 1 秒</span><br>  <span class="hljs-keyword">return</span> &#123; user, posts &#125;;<br>&#125;<br></code></pre></td></tr></table></figure><p>五、常见误区与注意事项<br>❌ 误区 1：await 会阻塞整个程序<br>不会！<br>它只暂停当前 async 函数的执行，不影响其他代码。<br>❌ 误区 2：async 函数能直接返回值<br>不能！ 它总是返回 Promise：<br>❌ 误区 3：忘记处理错误</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 危险！未捕获的 reject 会导致 unhandledrejection</span><br><span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-title function_">badApi</span>();<br><br><span class="hljs-comment">// 安全做法：</span><br><span class="hljs-keyword">try</span> &#123;<br>  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-title function_">badApi</span>();<br>&#125; <span class="hljs-keyword">catch</span> (err) &#123;<br>  <span class="hljs-comment">// 处理错误</span><br>&#125;<br></code></pre></td></tr></table></figure><p>❌ 误区 4：在循环中滥用 await</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 串行（慢）</span><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> url <span class="hljs-keyword">of</span> urls) &#123;<br>  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url);<br>  results.<span class="hljs-title function_">push</span>(data);<br>&#125;<br><br><span class="hljs-comment">// 并行（快）</span><br><span class="hljs-keyword">const</span> promises = urls.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">url</span> =&gt;</span> <span class="hljs-title function_">fetch</span>(url));<br><span class="hljs-keyword">const</span> results = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>(promises);<br></code></pre></td></tr></table></figure><p>六、最佳实践<br>优先使用 async&#x2F;await 而不是 .then() 链<br>总是用 try…catch 包裹 await<br>并行任务用 Promise.all<br>避免在顶层代码直接用 await（除非你的环境支持 top-level await，如现代浏览器模块或 Node.js ES 模块）<br>不要忽略 Promise 的 reject（会导致警告或崩溃）</p><p>七、完整示例：用户登录流程</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 模拟 API</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">login</span>(<span class="hljs-params">credentials</span>) &#123;<br>  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> &#123;<br>    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> &#123;<br>      <span class="hljs-keyword">if</span> (credentials.<span class="hljs-property">password</span> === <span class="hljs-string">&#x27;secret&#x27;</span>) &#123;<br>        <span class="hljs-title function_">resolve</span>(&#123; <span class="hljs-attr">token</span>: <span class="hljs-string">&#x27;abc123&#x27;</span>, <span class="hljs-attr">user</span>: &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Alice&#x27;</span> &#125; &#125;);<br>      &#125; <span class="hljs-keyword">else</span> &#123;<br>        <span class="hljs-title function_">reject</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&#x27;Invalid password&#x27;</span>));<br>      &#125;<br>    &#125;, <span class="hljs-number">500</span>);<br>  &#125;);<br>&#125;<br><br><span class="hljs-comment">// 使用 async/await</span><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handleLogin</span>(<span class="hljs-params">username, password</span>) &#123;<br>  <span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-keyword">const</span> &#123; token, user &#125; = <span class="hljs-keyword">await</span> <span class="hljs-title function_">login</span>(&#123; username, password &#125;);<br>    <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">setItem</span>(<span class="hljs-string">&#x27;token&#x27;</span>, token);<br>    <span class="hljs-title function_">showWelcome</span>(user.<span class="hljs-property">name</span>);<br>  &#125; <span class="hljs-keyword">catch</span> (error) &#123;<br>    <span class="hljs-title function_">showError</span>(error.<span class="hljs-property">message</span>);<br>  &#125;<br>&#125;<br><br><span class="hljs-comment">// 调用</span><br><span class="hljs-title function_">handleLogin</span>(<span class="hljs-string">&#x27;alice&#x27;</span>, <span class="hljs-string">&#x27;wrong&#x27;</span>); <span class="hljs-comment">// 显示 &quot;Invalid password&quot;</span><br></code></pre></td></tr></table></figure><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1774454087284" data-twikoo-path="article_1774454087284"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/24/javascript/</id>
    <link href="https://aoiblog.top/2026/03/24/javascript/"/>
    <published>2026-03-24T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>Javascript</h2>
# Javascript

<p>2026年3月25日23:55:50<br>发这篇文章的时候发现让哈基米修博客的时候越修越错，前前后后在MongoDB和vercel里掰扯了半个小时，又跟哈基米掰扯了半个多小时，真的心累……<br>不知道是]]>
    </summary>
    <title>Javascript</title>
    <updated>2026-05-07T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="数据结构与算法" scheme="https://aoiblog.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<h2>数据结构与算法学习日记（理论部分）</h2><strong>1.优先级队列及二叉堆</strong>参考文章：<a href="https://labuladong.online/zh/algo/data-structure-basic/binary-heap-implement/#%E7%AE%80%E5%8C%96%E7%89%88%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97" target="_blank" style="color: #0366d6; text-decoration: underline;">点击这里跳转</a><p>我们可以使用数组来模拟二叉堆（本质上还是一棵二叉树）<br>此处我们只讨论索引从0开始的情况</p><img src="/images/img_1774365422873.png" alt="图片" style="max-width: 100%;">因此我们可以得到父节点，左右子节点的索引公式<pre><code class="language-javascript">// 父节点的索引int parent(int node) {    return (node - 1) / 2;}// 左子节点的索引int left(int node) {    return node * 2 + 1;}// 右子节点的索引int right(int node) {    return node * 2 + 2;}</code></pre><p><strong>核心操作：增</strong><br>以小顶堆为例，向小顶堆中插入新元素遵循两个步骤：<br>1、先把新元素追加到二叉树底层的最右侧，保持完全二叉树的结构。此时该元素的父节点可能比它大，不满足小顶堆的性质。<br>2、为了恢复小顶堆的性质，需要将这个新元素不断上浮（swim），直到它的父节点比它小为止，或者到达根节点。此时整个二叉树就满足小顶堆的性质了。</p><p><strong>核心操作：删</strong><br>以小顶堆为例，删除小顶堆的堆顶元素遵循两个步骤：<br>1、先把堆顶元素删除，把二叉树底层的最右侧元素摘除并移动到堆顶，保持完全二叉树的结构。此时堆顶元素可能比它的子节点大，不满足小顶堆的性质。<br>2、为了恢复小顶堆的性质，需要将这个新的堆顶元素不断下沉（sink），直到它比它的子节点小为止，或者到达叶子节点。此时整个二叉树就满足小顶堆的性质了。</p><p><span style="color: #0366d6;">用数组模拟二叉堆的原因</span><br>1.链表节点需要一个额外的指针存储相邻节点的地址，所以相对数组，链表的内存消耗会大一些。<br>2.假如我们使用二叉树来构造二叉堆，那么就需要层序遍历或递归遍历二叉树，时间复杂度是 O（N），导致push和pop方法的时间复杂度也为O（N），假如我们使用数组，那么时间复杂度就为O（1）</p><p><span style="color: #0366d6;">优先级队列完整代码实现</span><br>此处以二叉堆实现为例</p><pre><code class="language-cpp">#include &lt;iostream&gt;#include &lt;vector&gt;#include &lt;functional&gt;#include &lt;stdexcept&gt;#include &lt;algorithm&gt;  // 添加这个头文件用于 std::swaptemplate&lt;typename T&gt;class MyPriorityQueue{private:    //堆数组    std::vector&lt;T&gt; heap;    //堆中元素的数量    int size;    //元素比较器    std::function&lt;bool(const T&, const T&)&gt; comparator;    //父节点的索引    int parent(int node){        return (node - 1) / 2;    }    //左子节点的索引    int left(int node){        return node * 2 + 1;    }    //右子节点的索引    int right(int node){        return node * 2 + 2;    }    //交换数组的两个元素    void swap(int i, int j){        std::swap(heap[i], heap[j]);  // 使用 std::swap    }    //调整堆的大小    void resize(int capacity){        heap.resize(capacity);    }    //上浮操作，时间复杂度是树高O(logN)    void swim(int node){        while(node &gt; 0 && comparator(heap[parent(node)], heap[node])){            swap(parent(node), node);            node = parent(node);        }    }    //下沉操作    void sink(int node){        while(left(node) &lt; size){            int min = node;            int l = left(node);            int r = right(node);                        // 使用 comparator 进行比较            if(l &lt; size && comparator(heap[min], heap[l])){                min = l;            }            if(r &lt; size && comparator(heap[min], heap[r])){                min = r;            }                        if(min == node){                break;            }                        swap(node, min);            node = min;        }    }public:    //构造函数 - 参数名改为 comp 避免与成员变量混淆    MyPriorityQueue(int capacity, std::function&lt;bool(const T&, const T&)&gt; comp)        : heap(capacity), size(0), comparator(std::move(comp)){}    //返回堆的大小    int getSize() const{        return size;    }    //判断堆是否为空    bool isEmpty() const{        return size == 0;    }    //查，返回堆顶元素,时间复杂度O(1)    const T& peek() const{        if(isEmpty()){            throw std::underflow_error("Priority queue underflow");         }        return heap[0];    }    //增，向堆中插入一个元素，时间复杂度O(logN)    void push(const T& x){        //扩容        if(size == heap.size()){            if(heap.size() == 0){                resize(1);            } else {                resize(2 * heap.size());            }        }        //把新元素追加到最后        heap[size] = x;        //然后上浮到正确位置        swim(size);        size++;    }    //删，删除堆顶元素，时间复杂度O(logN)    T pop(){        if(isEmpty()){            throw std::underflow_error("Priority queue underflow");        }        T res = heap[0];        //把堆底元素放到堆顶        swap(0, size - 1);        size--;        //然后下沉到正确位置        sink(0);        //缩容        if(size &gt; 0 && size &lt;= heap.size() / 4 && heap.size() &gt; 1){            resize(heap.size() / 2);        }        return res;    }};// 测试代码int main() {    // 使用lambda表达式来传递比较器    // 小顶堆    MyPriorityQueue&lt;int&gt; pq(3, [](const int& a, const int& b) { return a &gt; b; });    pq.push(3);    pq.push(1);    pq.push(4);    pq.push(1);    pq.push(5);    pq.push(9);    // 1 1 3 4 5 9    while (!pq.isEmpty()) {        std::cout &lt;&lt; pq.pop() &lt;&lt; " ";    }    std::cout &lt;&lt; std::endl;    // 测试大顶堆    MyPriorityQueue&lt;int&gt; pq2(3, [](const int& a, const int& b) { return a &lt; b; });    pq2.push(3);    pq2.push(1);    pq2.push(4);    pq2.push(1);    pq2.push(5);    pq2.push(9);    // 9 5 4 3 1 1    while (!pq2.isEmpty()) {        std::cout &lt;&lt; pq2.pop() &lt;&lt; " ";    }    std::cout &lt;&lt; std::endl;    return 0;}</code></pre><p>语法部分：<br>1.什么时候应该用move？</p><pre><code class="language-none">// 1. 构造函数参数（避免拷贝）MyClass(std::vector&lt;int&gt; v) : data(std::move(v)) {}// 2. 函数返回值（自动移动，不需要显式move）vector&lt;int&gt; createVector() {    vector&lt;int&gt; v = {1, 2, 3};    return v;  // 自动移动，不需要 std::move(v)}// 3. 转移资源所有权unique_ptr&lt;Widget&gt; ptr1 = make_unique&lt;Widget&gt;();unique_ptr&lt;Widget&gt; ptr2 = std::move(ptr1);  // ptr1 失去所有权</code></pre><p>2.std::function&lt;bool(const T&amp;, const T&amp;)&gt; comp<br>等价写法：</p><pre><code class="language-none">// std::function 写法std::function&lt;bool(const T&, const T&)&gt; comp;// 等价的函数指针写法bool (*comp)(const T&, const T&);// 等价的函数引用写法bool (&comp)(const T&, const T&);</code></pre><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1774365835226" data-twikoo-path="article_1774365835226"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/23/shu-ju-jie-gou-yu-suan-fa-xue-xi-ri-ji-li-lun-bu-fen/</id>
    <link href="https://aoiblog.top/2026/03/23/shu-ju-jie-gou-yu-suan-fa-xue-xi-ri-ji-li-lun-bu-fen/"/>
    <published>2026-03-23T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>数据结构与算法学习日记（理论部分）</h2>
<strong>1.优先级队列及二叉堆</strong>
参考文章：<a href="https://labuladong.online/zh/algo/data-structure-basic/binary-heap-imp]]>
    </summary>
    <title>数据结构与算法学习日记（理论部分）</title>
    <updated>2026-03-24T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="数据结构与算法" scheme="https://aoiblog.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<h2>数据结构与算法学习日记</h2>2026.3.23也许很快就会给博客更新代码高亮显示功能…？<p>2026年3月23日19:38:08<br>想到自己以前总是眼高手低，想得太多而做得太少，故创建这篇博客来记录自己学习数据结构与算法的过程<br>顺便更新了博客显示代码块的功能</p><p><strong>1.合并两个有序链表</strong><br><a href="https://leetcode.cn/problems/merge-two-sorted-lists/" target="_blank" style="color: #0366d6; text-decoration: underline;">点击这里跳转</a></p><pre><code class="language-c++">class Solution {public:    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {        //创建虚拟头结点        ListNode dummy(-1);        ListNode *p = &dummy;        ListNode *p1 = list1;        ListNode *p2 = list2;        while(p1 != nullptr && p2 != nullptr){            if(p1->val > p2->val){                p->next = p2;                p2 = p2->next;            }else{                p->next = p1;                p1 = p1->next;            }            p = p->next;        }        if(p1 != nullptr){            p->next = p1;        }        if(p2 != nullptr){            p->next = p2;        }        return dummy.next;    }};</code></pre><p><span style="color: red; font-weight: bold;">什么时候使用虚拟头结点：当你需要创造一条新链表的时候</span><br>头结点的作用：<br>（1）：统一处理空链表和头结点。如果没有虚拟头节点，当需要操作头节点时（比如删除、插入、或像本题这样需要将头节点移动到另一个链表），代码会变得很复杂，需要额外判断 head 是否为空或是否要修改 head<br>（2）：避免复杂的空指针判断。使用虚拟头节点后，所有节点操作都可以用相同的逻辑处理，无需区分第一个节点和后续节点。<br>（3）：简化边界条件处理</p><p><strong>2.分隔链表</strong><br><a href="https://leetcode.cn/problems/partition-list/description/" target="_blank" style="color: #0366d6; text-decoration: underline;">点击这里跳转</a></p><pre><code class="language-javascript">class Solution {public:    ListNode* partition(ListNode* head, int x) {        // 存放小于 x 的链表的虚拟头结点        ListNode* dummy1 = new ListNode(-1);        // 存放大于等于 x 的链表的虚拟头结点        ListNode* dummy2 = new ListNode(-1);        // p1, p2 指针负责生成结果链表        ListNode* p1 = dummy1, *p2 = dummy2;        // p 负责遍历原链表，类似合并两个有序链表的逻辑        // 这里是将一个链表分解成两个链表        ListNode* p = head;        while (p != nullptr) {            if (p->val >= x) {                p2->next = p;                p2 = p2->next;            } else {                p1->next = p;                p1 = p1->next;            }            // 不能直接让 p 指针前进，            // p = p->next            // 断开原链表中的每个节点的 next 指针            ListNode* temp = p->next;            p->next = nullptr;            p = temp;        }        // 连接两个链表        p1->next = dummy2->next;        return dummy1->next;    }};</code></pre><p><strong>为什么在创建虚拟头结点的时候需要使用new？</strong><br>使用虚拟头节点（无论 new 还是栈上）是链表题目的最佳实践，它能：<br>1.消除对头节点的特殊处理<br>2.让代码逻辑更清晰<br>3.减少边界条件 bug</p><p>至于用 new 还是栈上分配，两种都可以：<br>用 new：习惯使然，和原链表节点创建方式一致<br>用栈上：更简洁，无需 delete，但要注意虚拟节点的 next 被修改后，不能通过虚拟节点本身访问链表（这本来就不需要）</p><p>对比总结<br>写法                                        优点                                                         缺点<br>new 创建虚拟头节点统一处理逻辑，代码简洁需要手动 delete（但力扣通常不检查）<br>栈上创建虚拟头节点统一处理逻辑，无需 delete需要小心作用域，返回后栈对象被销毁但它的 next 还在用（这没问题）<br>不用虚拟头节点                  无额外内存                                      代码复杂，边界条件多</p><p><span style="color: red; font-weight: bold;">注：本题在while循环最后不能直接让p &#x3D; p-&gt;next</span><br>否则会让p2链表指向p1链表的最后一个节点，使得两个链表形成一个环</p><p><span style="color: #0366d6; font-weight: bold;">总的来说，如果我们需要把原链表的节点接到新链表上，而不是 new 新节点来组成新链表的话，那么断开节点和原链表之间的链接可能是必要的。那其实我们可以养成一个好习惯，但凡遇到这种情况，就把原链表的节点断开，这样就不会出错了。</span></p><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1774253776836" data-twikoo-path="article_1774253776836"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/22/shu-ju-jie-gou-yu-suan-fa-xue-xi-ri-ji/</id>
    <link href="https://aoiblog.top/2026/03/22/shu-ju-jie-gou-yu-suan-fa-xue-xi-ri-ji/"/>
    <published>2026-03-22T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>数据结构与算法学习日记</h2>
2026.3.23
也许很快就会给博客更新代码高亮显示功能…？

<p>2026年3月23日19:38:08<br>想到自己以前总是眼高手低，想得太多而做得太少，故创建这篇博客来记录自己学习数据结构与算法的过程<br>顺便更新了博客显示代]]>
    </summary>
    <title>数据结构与算法学习日记</title>
    <updated>2026-03-22T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="随笔" scheme="https://aoiblog.top/categories/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<h2>Vibe Coding 时代：文科生、工科生与 AI 洪流中的个体尊严</h2><p style="text-indent: 2em; line-height: 1.8;">在这个 vibe coding 盛行的时代，各路媒体大肆吹捧文科生相比工科生具有更加明显的优势，即核心能力从“代码能力”转变到了“表达能力”。不得不承认，文科生相比理工科学生通常是具备更强的逻辑表达、叙事能力、与用户共情的能力与需求抽象能力，能更准确地将模糊的想法转化为结构化的提示词，从而引导 AI 生成高质量的代码或原型。</p><p style="text-indent: 2em; line-height: 1.8;">但我想说的是，在理工科学生里，像我这样富有人文情怀的人肯定也不在少数。</p><p style="text-indent: 2em; line-height: 1.8;">看了开头之后，也许有的人就会有疑问：如果是一个零计算机基础的文科生，那么他该如何将自己的想法转化为结构化的提示词，指示 AI 使用什么架构，什么工具，什么语言，什么方式来精准地写出高质量的代码呢？</p><p style="text-indent: 2em; line-height: 1.8;">在回答这个问题之前，我们首先明确一下 vibe coding 的范围。很多人没有注意到，vibe coding 只适用于个人项目、MVP 验证、内部工具和小微 SaaS，而完全不适用于像微信、QQ、淘宝、拼多多这类拥有亿级用户、高并发、强一致性、多端协同和要求金融级安全的平台级产品。也就是说，假如你作为一个零计算机基础的文科生，你想用 vibe coding 做一个基于现代云服务（比如说 Vercel + Supabase + Stripe）的工具，并且用户量不多，只解决几个具体问题的、聚焦真实小需求的产品，是可以落地和上线的。</p><p style="text-indent: 2em; line-height: 1.8;">现在我们开始回答之前的那个问题。如今要想让 AI 写出可用的代码，提示词不必包含“你要使用 Next.js + TypeScript + Supabase……”等详细技术栈，而只用说清楚功能需求、约束条件、部署目标和风险规避即可。AI 可能会自动选择更加安全和免运维的方案，高质量提示词的核心是“表达意图 + 设定边界”，给 AI 的任务执行添加必要的约束条件，而非指定技术实现。</p><h2>真实落地中的隐患与工具属性</h2><p style="text-indent: 2em; line-height: 1.8;">而不可否认的是，假如让一个零基础的文科生来 vibe coding 一个真正可落地、能上线的实际项目的话，会含有很多安全隐患。AI 在生成代码的时候，为了快速跑通，常常会把敏感信息直接写在前端 JS 中。就像我直接 vibe 这个博客的时候，Gemini 3.1 Pro 试图直接把 GitHub Token 硬编码在我的博客首页，触发了 GitHub 的 Secret Scanning 机制，导致 <code>git push</code> 被强行拦截。假如第三方服务不会自动检测操作的安全性的话，那么极有可能就会造成密钥泄露的情况。vibe coding 产出的只是能跑的 demo，而非真正能上线的产品。</p><p style="text-indent: 2em; line-height: 1.8;">同时，当 AI 生成的代码出错、页面白屏时，零基础的人会看不懂控制台和终端报错，不会检查网络请求，不知道整个流程中是哪个环节出了问题，只能反复地对 AI 说“我的项目中出现了错误，请检查错误并修复”，而 AI 可能并不能一次就改对地方，导致多次无效迭代，浪费 token 和时间。</p><p style="text-indent: 2em; line-height: 1.8; font-weight: bold;">一句话总结：vibe coding 只是加速器，而不是自动驾驶系统。</p><h2>大模型时代的创造与意义</h2><p style="text-indent: 2em; line-height: 1.8;">有的人可能又会说，本质上文科生还是要依赖能力强大的大模型，而训练这些模型的人只能是工科生。事实上的确如此，大模型的底层构建高度依赖工科能力，文科生无法直接参与模型训练或架构设计。然而文科生在将 AI 的价值观对齐、规范数据伦理、审查训练数据中的偏见、歧视、虚假信息，优化人机交互设计，使技术真正可用、可亲，定义 AI 在教育、法律、心理、文化等领域上的应用问题，以及批判性监督技术等方面确实有着重要的作用。工科生造出“能思考的机器”，文科生指引“它该思考什么、为谁服务”。</p><p style="text-indent: 2em; line-height: 1.8;">在产品上，也许文科生更擅长从 0 到 1，定义问题，验证价值，快速确定原型；计算机学生更擅长从 1 到 100，将产品工程化，规模化，稳定化。但值得注意的是，有些时候从 0 到 1 也需要技术直觉，从 1 到 100 也需要人文判断。在 AI 降低编码门槛的今天，0-1 的价值被空前放大，而 1-100 的门槛依然高耸，重要的不再是能力高低之分，而是创新链条上的自然分工。在如今的时代，更吃香的，显然是同时具有人文素养和技术理解力的人。</p><h2>时代洪流中的个体焦虑与解药</h2><p style="text-indent: 2em; line-height: 1.8;">那么，我们自然又会思考下一个问题：以前只要人才能具备一项能力就能找到工作，现在不仅要求人才在专业领域拥有丰富的技术栈，还得懂其他学科和方面的知识。这究竟是社会进步推动人才标准自然升级，还是资本逻辑驱动企业降本增效、成本转嫁呢？</p><p style="text-indent: 2em; line-height: 1.8;">也许两者兼有，但后者也许正在日益主导前者。</p><p style="text-indent: 2em; line-height: 1.8;">知识融合是创新的必然趋势，复杂问题本身不分文理，解决它们自然要求跨界能力，如今高校的教育体系也正在响应这种需求。而现实中企业也确实在借复合型人才之名行降本之实。最近网上也是在疯传各大企业大量裁员的消息，有的已经辟谣，有的还不知真假，这些消息确实也弄得包括我在内的一部分人十分焦虑。有的人说，自己辛苦学习了十六年，到头来却通知自己要被 AI 取代了。</p><p style="text-indent: 2em; line-height: 1.8;">也许个人的努力，在时代的洪流面前真的不值一提。</p><p style="text-indent: 2em; line-height: 1.8;">实际上，AI 替代的不是人，而是某些可自动化的 dirty work，替代的是“标准化、重复性、信息处理型”的任务环节，而非“人”的整体价值。传统教育体系需要的是“确定性问题解决者”，而 AI 时代需要的是“不确定性问题定义者”，要求人能够提出好问题，整合跨领域信息，在模糊或迷雾中创造新路径。</p><p style="text-indent: 2em; line-height: 1.8;">也许 AI 的到来并不是要夺走我们的饭碗，而是要逼着我们去寻找那个只有我们自己才能端起来的饭碗。它可能更重，但装的是我们真正的尊严。</p><p style="text-indent: 2em; line-height: 1.8;">企业会调整，行业会变迁，但我们的思考能力、解决问题的能力、与人协作的能力——这些永远不会被 AI 或裁员夺走。</p><p style="text-indent: 2em; line-height: 1.8;">忽然想起《激荡三十年》中的一段话：一个人要让自己快乐其实是一件不难的事，你只要给自己一个较长时间的目标，然后按部就班地去接近它，实现它。结果如何，在某种意义上可能是不重要的，重要的是，在这个过程中，你会非常的单纯和满足。</p><p style="text-indent: 2em; line-height: 1.8;">我们正处在一个“结果极度不确定，过程极易被干扰”的时代。在这种环境下，执着于“结果”（比如“必须年薪百万”“35岁前买房”）只会带来持续的挫败感。而回归“过程”，专注于一个你认同的长期目标，并享受一步步靠近它的踏实感，反而成了最稳固的心理锚点。</p><p style="text-indent: 2em; line-height: 1.8;">最后向各位展示 Gemini 3.1 Pro 给我总结的 vibe 这个博客的时候我遇到的问题，希望这篇文章能让各位在今后的每一天，都在深夜回顾自己这一天都干了些什么时，都觉得自己比昨天更加从容。</p><img src="/images/img_1774112271507.png" alt="图片" style="max-width: 100%;"><img src="/images/img_1774112279132.png" alt="图片" style="max-width: 100%;"><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_vibe_coding_reflection" data-twikoo-path="article_vibe_coding_reflection"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/21/vibe-coding-shi-dai-wen-ke-sheng-gong-ke-sheng-yu-ai-hong-liu-zhong-de-ge-ti-zun-yan/</id>
    <link href="https://aoiblog.top/2026/03/21/vibe-coding-shi-dai-wen-ke-sheng-gong-ke-sheng-yu-ai-hong-liu-zhong-de-ge-ti-zun-yan/"/>
    <published>2026-03-21T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>Vibe Coding 时代：文科生、工科生与 AI 洪流中的个体尊严</h2>
<p style="text-indent: 2em; line-height: 1.8;">在这个 vibe coding 盛行的时代，各路媒体大肆吹捧文科生相比工科生具有更加明显的优势，]]>
    </summary>
    <title>Vibe Coding 时代：文科生、工科生与 AI 洪流中的个体尊严</title>
    <updated>2026-03-20T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Aoitsuki</name>
    </author>
    <category term="测试" scheme="https://aoiblog.top/categories/%E6%B5%8B%E8%AF%95/"/>
    <content>
      <![CDATA[<h2>测试</h2>666<img src="/images/img_1774022028502.png" alt="图片" style="max-width: 100%;"><section class="legacy-comments">  <h2>评论区</h2>  <div id="twikoo-article_1774022033700" data-twikoo-path="article_1774022033700"></div></section>]]>
    </content>
    <id>https://aoiblog.top/2026/03/19/ce-shi/</id>
    <link href="https://aoiblog.top/2026/03/19/ce-shi/"/>
    <published>2026-03-19T16:00:00.000Z</published>
    <summary>
      <![CDATA[<h2>测试</h2>
666
<img src="/images/img_1774022028502.png" alt="图片" style="max-width: 100%;">


<section class="legacy-comments">
  <h2>评论区</h]]>
    </summary>
    <title>测试</title>
    <updated>2026-03-19T16:00:00.000Z</updated>
  </entry>
</feed>
